Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Alpha2Code.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ public function getName(): string
return $this->name;
}

public function toString(): string
{
return $this->value;
}

/**
* Converts this Alpha-2 code to its corresponding Alpha-3 code.
*
Expand Down
5 changes: 5 additions & 0 deletions src/Alpha3Code.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ public function getName(): string
return $this->name;
}

public function toString(): string
{
return $this->value;
}

/**
* Converts this Alpha-3 code to its corresponding Alpha-2 code.
*
Expand Down
6 changes: 6 additions & 0 deletions src/AlphaCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ interface AlphaCode
* @return string The name of the enum case representing the alpha code.
*/
public function getName(): string;

/**
* Gets the alpha code value (e.g. 'BR' for Alpha-2, 'BRA' for Alpha-3).
* @return string The alpha code value.
*/
public function toString(): string;
}
6 changes: 3 additions & 3 deletions src/Timezones.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ public function contains(string $iana): bool
* Finds a Timezone by its IANA identifier.
*
* @param string $iana The IANA timezone identifier to search for (e.g. America/Sao_Paulo).
* @return Timezone|null The matching Timezone, or null if not found in this country.
* @return Timezone The matching Timezone, or UTC if not found in this country.
*/
public function findByIdentifier(string $iana): ?Timezone
public function findByIdentifier(string $iana): Timezone
{
return array_find(
$this->items,
static fn(Timezone $timezone): bool => $timezone->value === $iana
);
) ?? Timezone::utc();
Comment thread
gustavofreze marked this conversation as resolved.
}
Comment on lines +95 to 101
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signature of findByIdentifier has been changed from returning Timezone|null to returning Timezone (with UTC fallback). However, the README documentation still shows the old behavior where null is returned when a timezone is not found (line 192: $country->timezones->findByIdentifier(iana: 'Asia/Tokyo'); # null). The documentation should be updated to reflect that UTC is now returned instead of null in this case.

Copilot uses AI. Check for mistakes.
Comment on lines +95 to 101
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking API change. Code that previously relied on checking for null when a timezone is not found will no longer work as expected. Consider whether this breaking change is intentional and properly communicated. If the behavior is changed, consumers who were checking for null (if ($timezone === null)) will now always receive a Timezone object (UTC), which could lead to silent bugs in existing code that expects to handle the null case differently.

Copilot uses AI. Check for mistakes.
Comment on lines +95 to 101
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title only mentions adding toString methods, but this PR also includes a significant breaking change to the Timezones.findByIdentifier method (changing from returning null to returning UTC when a timezone is not found). This breaking change should be prominently mentioned in the PR description to ensure reviewers and users are aware of the behavioral change and its implications.

Copilot uses AI. Check for mistakes.

/**
Expand Down
118 changes: 83 additions & 35 deletions tests/CountryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,26 @@ public function testCountryAlpha3ConvertsToAlpha2Correctly(): void
self::assertSame(Alpha3Code::SWITZERLAND, $country->alpha3);
}

#[DataProvider('alphaCodeToStringDataProvider')]
public function testCountryAlphaCodeToStringReturnsValue(
Alpha2Code $alpha2,
string $expectedAlpha2String,
string $expectedAlpha3String
): void {
/** @Given a Country created from an Alpha-2 code */
$country = Country::from(alphaCode: $alpha2);

/** @When calling toString on each alpha code */
$alpha2String = $country->alpha2->toString();
$alpha3String = $country->alpha3->toString();

/** @Then the Alpha-2 toString should return the two-letter code */
self::assertSame($expectedAlpha2String, $alpha2String);

/** @And the Alpha-3 toString should return the three-letter code */
self::assertSame($expectedAlpha3String, $alpha3String);
}

public function testCountryFromStringWithAlpha2Code(): void
{
/** @Given a valid two-letter alpha code string */
Expand Down Expand Up @@ -219,23 +239,20 @@ public function testCountryTimezonesFindByIdentifierReturnsTimezone(): void
/** @When searching for a known timezone identifier */
$timezone = $country->timezones->findByIdentifier(iana: 'America/New_York');

/** @Then a Timezone object should be returned */
self::assertInstanceOf(Timezone::class, $timezone);

/** @And its value should match the searched identifier */
/** @Then the returned Timezone value should match the searched identifier */
self::assertSame('America/New_York', $timezone->value);
}

public function testCountryTimezonesFindByIdentifierReturnsNullWhenNotFound(): void
public function testCountryTimezonesFindByIdentifierReturnsUtcWhenNotFound(): void
{
/** @Given a Country created from Alpha-2 code DE (Germany) */
$country = Country::from(alphaCode: Alpha2Code::GERMANY);

/** @When searching for a timezone that does not belong to Germany */
$timezone = $country->timezones->findByIdentifier(iana: 'Asia/Tokyo');

/** @Then null should be returned */
self::assertNull($timezone);
/** @Then the fallback UTC timezone should be returned */
self::assertSame('UTC', $timezone->value);
}

public function testCountryTimezonesCountMatchesAllSize(): void
Expand Down Expand Up @@ -299,7 +316,7 @@ public function testCountryWithMultipleTimezonesPreservesAll(): void

/** @And each timezone should be findable by its identifier */
foreach ($country->timezones->all() as $timezone) {
self::assertNotNull($country->timezones->findByIdentifier(iana: $timezone->value));
self::assertSame($timezone->value, $country->timezones->findByIdentifier(iana: $timezone->value)->value);
}
}

Expand All @@ -316,7 +333,7 @@ public function testCountryTimezonesCreatedFromSameCodeAreConsistent(): void
self::assertSame($first->timezones->count(), $second->timezones->count());
}

public function testCountryWhenInvalidTimezoneIdentifier(): void
public function testCountryWhenInvalidTimezone(): void
{
/** @Given a non-empty string that is not a valid IANA timezone */
$invalidIdentifier = 'Invalid/Timezone';
Expand Down Expand Up @@ -370,48 +387,48 @@ public function testCountryWhenInvalidAlphaCodeImplementation(): void
public static function alphaCodeObjectsDataProvider(): array
{
return [
'Alpha2 with custom name' => [
'alphaCode' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'name' => 'United States',
'expectedName' => 'United States',
'expectedAlpha2' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha3' => Alpha3Code::UNITED_STATES_OF_AMERICA,
],
'Alpha3 with custom name' => [
'alphaCode' => Alpha3Code::UNITED_STATES_OF_AMERICA,
'name' => 'United States',
'expectedName' => 'United States',
'expectedAlpha2' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha3' => Alpha3Code::UNITED_STATES_OF_AMERICA,
],
'Alpha2 with null name' => [
'alphaCode' => Alpha2Code::BRAZIL,
'name' => null,
'expectedName' => 'Brazil',
'expectedAlpha2' => Alpha2Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL
],
'Alpha3 with null name' => [
'alphaCode' => Alpha3Code::BRAZIL,
'name' => null,
'expectedName' => 'Brazil',
'expectedAlpha2' => Alpha2Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL
],
'Alpha2 with custom name' => [
'alphaCode' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'name' => 'United States',
'expectedName' => 'United States',
'expectedAlpha2' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha3' => Alpha3Code::UNITED_STATES_OF_AMERICA
],
'Alpha3 with custom name' => [
'alphaCode' => Alpha3Code::UNITED_STATES_OF_AMERICA,
'name' => 'United States',
'expectedName' => 'United States',
'expectedAlpha2' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha3' => Alpha3Code::UNITED_STATES_OF_AMERICA
],
'Alpha2 GB with full name' => [
'alphaCode' => Alpha2Code::UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND,
'name' => 'United Kingdom of Great Britain and Northern Ireland',
'expectedName' => 'United Kingdom of Great Britain and Northern Ireland',
'expectedAlpha2' => Alpha2Code::UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND,
'expectedAlpha3' => Alpha3Code::UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND,
'expectedAlpha3' => Alpha3Code::UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND
],
'Alpha3 GBR with null name' => [
'alphaCode' => Alpha3Code::UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND,
'name' => null,
'expectedName' => 'United Kingdom of Great Britain and Northern Ireland',
'expectedAlpha2' => Alpha2Code::UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND,
'expectedAlpha3' => Alpha3Code::UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND,
],
'expectedAlpha3' => Alpha3Code::UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND
]
];
}

Expand All @@ -423,43 +440,74 @@ public static function alphaCodeStringsDataProvider(): array
'name' => 'United States',
'expectedName' => 'United States',
'expectedAlpha2' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha3' => Alpha3Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha3' => Alpha3Code::UNITED_STATES_OF_AMERICA
],
'Alpha3 string USA' => [
'alphaCode' => 'USA',
'name' => 'United States',
'expectedName' => 'United States',
'expectedAlpha2' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha3' => Alpha3Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha3' => Alpha3Code::UNITED_STATES_OF_AMERICA
],
'Alpha2 string with null name' => [
'alphaCode' => 'BR',
'name' => null,
'expectedName' => 'Brazil',
'expectedAlpha2' => Alpha2Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL
],
'Alpha3 string with null name' => [
'alphaCode' => 'BRA',
'name' => null,
'expectedName' => 'Brazil',
'expectedAlpha2' => Alpha2Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL
],
'Alpha2 string with custom name' => [
'alphaCode' => 'BR',
'name' => 'Brasil',
'expectedName' => 'Brasil',
'expectedAlpha2' => Alpha2Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL
],
'Alpha3 string with custom name' => [
'alphaCode' => 'BRA',
'name' => 'Brasil',
'expectedName' => 'Brasil',
'expectedAlpha2' => Alpha2Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL,
'expectedAlpha3' => Alpha3Code::BRAZIL
]
];
}

public static function alphaCodeToStringDataProvider(): array
{
return [
'Japan' => [
'alpha2' => Alpha2Code::JAPAN,
'expectedAlpha2String' => 'JP',
'expectedAlpha3String' => 'JPN'
],
'Brazil' => [
'alpha2' => Alpha2Code::BRAZIL,
'expectedAlpha2String' => 'BR',
'expectedAlpha3String' => 'BRA'
],
'Germany' => [
'alpha2' => Alpha2Code::GERMANY,
'expectedAlpha2String' => 'DE',
'expectedAlpha3String' => 'DEU'
],
'Switzerland' => [
'alpha2' => Alpha2Code::SWITZERLAND,
'expectedAlpha2String' => 'CH',
'expectedAlpha3String' => 'CHE'
],
'United States' => [
'alpha2' => Alpha2Code::UNITED_STATES_OF_AMERICA,
'expectedAlpha2String' => 'US',
'expectedAlpha3String' => 'USA'
]
];
}

Expand All @@ -469,7 +517,7 @@ public static function invalidAlphaCodeStringsDataProvider(): array
'Single character' => ['alphaCode' => 'X'],
'Two characters' => ['alphaCode' => 'XY'],
'Three characters' => ['alphaCode' => 'XYZ'],
'Four characters' => ['alphaCode' => 'XYZ1'],
'Four characters' => ['alphaCode' => 'XYZ1']
];
}
}
5 changes: 5 additions & 0 deletions tests/Models/AlphaCodeXpto.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ public function getName(): string
{
return $this->value;
}

public function toString(): string
{
return $this->value;
}
}