When building export tools or data filters in PHP, setting up dropdown options usually feels routine: a list of standard codes for specific selections, and a wildcard string like 'all' to signal “export everything.”
However, during a recent code review, a subtle architectural flaw stood out in a currency filtering trait – one that worked perfectly in manual testing, but hid a bug for specific real-world data.
The Code That Looked Good
The initial implementation was straightforward. The exporter accepted the incoming request parameter, sanitized and normalized it, and determined whether to filter by a single currency or export every transaction in the database:
namespace App\Exporter;
class TransactionExporter
{
public const ALL_CURRENCIES = 'ALL';
protected string $currency = '';
public function setCurrencyFilter(string $rawInput): void
{
$requested = $this->normalizeCurrencyCode($rawInput);
if (strtoupper(self::ALL_CURRENCIES) === $requested) {
$this->currency = self::ALL_CURRENCIES;
return;
}
$this->currency = $requested;
}
private function normalizeCurrencyCode(string $code): string
{
return strtoupper(trim(sanitize_text_field($code)));
}
}
With ALL_CURRENCIES defined as 'ALL', passing 'all' from the form dropdown gets normalized to ‘ALL’, matching the wildcard check. The exporter sets $this->currency to export everything.
It worked exactly as intended, until you attempt to export transactions paid in Albanian Lek– edge case I know but presumably for someone selling there in domestic currency…
The Sentinel Value Collision
In standard ISO 4217 currency formatting, the official three-letter alpha code for the Albanian Lek is ALL.
Because the normalization logic uppercased every input before checking it against the control constant 'ALL', the code lost the ability to distinguish between:
- A user selecting the wildcard “All Currencies” option (
'all' -> 'ALL'). - A store owner explicitly choosing the “Albanian Lek” currency (
'ALL'-> 'ALL').
Selecting the Albanian Lek would silently trigger the wildcard condition, causing the exporter to dump every transaction across all currencies instead of filtering strictly for Lek.
This is a classic Sentinel Value Collision, a specific form of the In-Band Signaling problem. It occurs whenever control flags share the exact same channel and format as actual domain data.
I know and I agree 100% with you that like me you are not going to use this currency, but you also must agree with me that this is still a sleeping bug in the code. 🙃
The Fix: Breaking the Domain Overlap
While you could write conditional checks comparing raw inputs against normalized values, the cleanest architectural fix is eliminating the possibility of a collision at the source.
Since ISO 4217 currency codes are strictly 3-letter alphabetic strings, changing the sentinel option value from ‘‘ to all' solves the problem permanently:all-currencies'
namespace App\Exporter;
class TransactionExporter
{
public const ALL_CURRENCIES = 'all-currencies';
…
Because 'all-currencies' contains a hyphen and exceeds three letters, it can never collide with a valid ISO currency code – no matter how many normalization or uppercase passes it goes through.
Key Takeaways
- Watch out for short control words: Generic strings like
all,none, ornofrequently collide with real-world standards (such as country codeNOfor Norway or currencyALLfor Albania). - Namespace non-data options: Append contextual suffixes (e.g.,
all-currencies,__all__,select-all) to keep control flags completely outside the domain data space. - Audit in-band signals: Whenever control instructions and user inputs travel down the exact same variable path, verify that a valid data point cannot masquerade as a system command.
