Hi

On 2026-08-07 20:46, Weilin Du wrote:
For enums and namespaces: I kept class constants and the global Intl* class name to stay consistent with the existing ext/intl API, such as IntlDateFormatter, IntlListFormatter IntlNumberRangeFormatter, and IntlDatePatternGenerator. I don't want to make IntlRelativeDateTimeFormatter somehow special here just because this is added later. I agree that enums and namespaces would be nicer in isolation, *indeed*. But,  introducing them for only this one formatter would make the API inconsistent with the rest
of ext/intl.

As I had mentioned in the voting thread, I think there is a reasonable middleground to be achieved here:

    final class IntlRelativeDateTimeFormatter
    {
        public function __construct(
            ?string $locale = null,
IntlRelativeDateTimeFormatterStyle $style = IntlRelativeDateTimeFormatterStyle::Long, IntlRelativeDateTimeFormatterCapitalization $capitalizationContext = IntlRelativeDateTimeFormatterCapitalization::None,
            ?NumberFormatter $numberFormatter = null,
        ) {}

public function format(int|float $offset, IntlRelativeDateTimeFormatterUnit $unit): string|false {}

public function formatNumeric(int|float $offset, int $unit): string|false {}

public function combineDateAndTime(string $relativeDate, string $time): string|false {}

        public function getErrorCode(): int {}

        public function getErrorMessage(): string {}
    }

    enum IntlRelativeDateTimeFormatterStyle {
        case Long;
        case Short;
        case Narrow;
    }

    enum IntlRelativeDateTimeFormatterCapitalization {
        case None;
        case MiddleOfSentence;
        case BeginningOfSentence;
        case UiListAndMenu;
        case Standalone;
    }

    enum IntlRelativeDateTimeFormatterUnit {
        case Year;
        case Quarter;
        case Month;
        case Week;
        case Day;
        case Hour;
        case Minute;
        case Second;
        // ...
    }

The practical change for the users is just moving some separators around:

    $formatter->format(-1, IntlRelativeDateTimeFormatter::UNIT_DAY)
    // becomes
    $formatter->format(-1, IntlRelativeDateTimeFormatterUnit::Day)

Basically the first component of the constant name moves in front of the `::` (and underscores are replaced by pascal case).

No namespaces are introduced, leaving a clean place for a redesigned Intl API that doesn't just expose the ICU API as-is, but the main benefits of using enums are preserved: Better discoverability and autocompletion in IDEs. Cleaner documentation (it's possible to document both the enum and the individual cases). Simplified input checks in the internal implementation (error handling is implicitly provided by the engine with the Z_PARAM_ENUM specifier).

I believe that users will value the clearer API signatures provided by enums over the tiny inconsistency of placing the “Unit”, “Style” and “Capitalization” in front of the `::` instead of after - and enums themselves are already used in PHP’s stdlib (e.g. RoundingMode), so users have already encountered them.

Best regards
Tim Düsterhus

Reply via email to