Helpers and globals

The 27 namespaced helper functions, how to import them, and the opt-in Globals::register() that adds short global names without ever replacing yours.

On this page
  1. Why helpers live in a namespace
  2. Using the helpers
  3. All helpers
    1. Calendars
    2. Digits and numbers
    3. Text
    4. Validation: yes or no
    5. Validation: structured result
    6. Holidays and prayer times
  4. Opt-in global names: Globals::register()
    1. Collision demo
  5. Which style should I use?

Why helpers live in a namespace#

Names such as is_mobile(), ordinal() or to_english() are short and common. PHP cannot override a global function. If two packages, or your application and a package, declare the same global name, the second declaration is a fatal error. A library that defines globals without checking can break an application that worked before.

RTLY-Kit avoids this by design:

  • All 27 helpers are defined in the RtlyKit namespace. Composer loads them automatically, and they cannot collide with anything.
  • Short global names are opt-in through \RtlyKit\Globals::register(), which defines only names that are still free.
  • The Laravel service provider does not register globals either. See Laravel setup.

Using the helpers#

Import the ones you need with use function and call them by their short name. Or call them fully qualified:

use function RtlyKit\jdate;
use function RtlyKit\is_national_code;

echo jdate('2025-03-21')->format('Y/m/d');       // 1404/01/01
var_dump(is_national_code('0013542419'));         // bool(true)
echo \RtlyKit\to_persian_digits(1404);            // ۱۴۰۴

// Several at once (PHP group use):
use function RtlyKit\{hdate, hebrew_date, number_to_words};
echo hdate('2025-03-21')->format('Y/m/d');       // 1446/09/21
echo hebrew_date('2025-03-21')->format('Y/m/d'); // 5785/06/21
echo number_to_words(1404);                       // یک هزار و چهارصد و چهار

The helpers are thin wrappers. Each calls the matching class, so you can use the class directly when you need more control, for example Jalali::create() with a time zone.

All helpers#

Parameter and return types are those of the source. Helpers with mixed input never throw. Invalid input gives false or an invalid Result.

Calendars#

HelperSignatureCalls
jdatejdate(DateTimeInterface|string|int|null $time = null, ?DateTimeZone $timezone = null): JalaliJalali::make(), see Jalali
hdatehdate(DateTimeInterface|string|int|null $time = null, ?DateTimeZone $timezone = null): HijriHijri::make(), see Hijri
hebrew_datehebrew_date(DateTimeInterface|string|int|null $time = null, ?DateTimeZone $timezone = null): HebrewHebrew::make(), see Hebrew

Digits and numbers#

HelperSignatureExample
to_persian_digits(string|int|float $value): stringto_persian_digits(1404) gives ۱۴۰۴
to_english_digits(string $value): stringto_english_digits('۱۴۰۴') gives 1404
to_persian(string|int|float $value): stringshort alias of to_persian_digits. to_persian('12.5') gives ۱۲.۵
to_english(string $value): stringshort alias of to_english_digits. to_english('٣٤') gives 34
number_to_words(int|float|string $number, string $locale = 'fa'): stringnumber_to_words(25, 'ar') gives خمسة وعشرون
format_number(int|float|string $number): stringformat_number(1234567) gives ۱٬۲۳۴٬۵۶۷
ordinal(int|float $number): stringordinal(3) gives سوم

See digits and formatting, number words and Arabic number words. The helper number_to_words() takes no options. For gender, case or vowel marks, call NumberToWords::convert() with ArabicOptions.

Text#

HelperSignatureExample
normalize_text(string $text): stringnormalize_text('كتاب ي') gives کتاب ی (Arabic kaf and yeh become Persian)
contains_rtl(string $text): boolcontains_rtl('abc سلام') gives true
text_direction(string $text): stringtext_direction('سلام') gives rtl, text_direction('hello') gives ltr

See text tools.

Validation: yes or no#

Each returns bool and accepts mixed.

HelperSignatureExample
is_national_code(mixed $value): boolis_national_code('0013542419') gives true
is_sheba(mixed $value): boolis_sheba('IR062960000000100324200001') gives true
is_bank_card(mixed $value): boolis_bank_card('6037997535328737') gives true
is_mobile(mixed $value): boolis_mobile('09123456789') gives true
is_postal_code(mixed $value): boolis_postal_code('1676543210') gives true
is_vehicle_plate(mixed $value): boolis_vehicle_plate('12ب345-67') gives true

Validation: structured result#

Each returns a RtlyKit\Validation\Result with the methods valid(), invalid(), isValid(), errors() and details(). See Validators overview.

HelperSignature
validate_national_code(mixed $value): Result
validate_sheba(mixed $value): Result
validate_bank_card(mixed $value): Result
validate_mobile(mixed $value): Result
validate_postal_code(mixed $value): Result
validate_vehicle_plate(mixed $value): Result
$r = \RtlyKit\validate_national_code('1234567890');
var_dump($r->isValid());   // bool(false)

Holidays and prayer times#

HelperSignatureNotes
is_iran_holiday(Jalali|int $year, ?int $month = null, ?int $day = null): boolwraps IranHolidays::isHoliday() and so uses the default holiday calendar. is_iran_holiday(1404, 11, 22) gives true. See holidays and holiday calendar.
prayer_times(string $city = 'tehran', string $method = 'Tehran'): arraytimes for today in the time zone of the city, as {"fajr", "sunrise", "dhuhr", "asr", "maghrib", "isha"}. An unknown city or method throws InvalidPrayerConfigException. See prayer times.

Together that is 3 + 7 + 3 + 6 + 6 + 2 = 27 helpers, the same list as Globals::NAMES.

Opt-in global names: Globals::register()#

If you prefer the short global names, for example in a legacy code base, a template engine or a script, call this once during bootstrap:

$skipped = \RtlyKit\Globals::register();

What it guarantees:

  • Only free names are defined. A name is defined only if no function with that name exists. An existing function is never replaced.
  • It reports what it skipped. The return value is a list<string> of the names that were not defined because another function already uses them. An empty list means everything was registered.
  • It never throws and never causes a redeclaration error.
  • You can call it again. A name that is RTLY-Kit's own earlier definition is not reported as skipped, so a second call returns the same list as the first.
  • Namespaced functions always work, whether or not a global name was skipped.
  • \RtlyKit\Globals::NAMES is the constant that lists all 27 names.

Collision demo#

The application already has its own is_mobile() (a user-agent check). The package does not break it, does not replace it, and tells you about it:

require 'vendor/autoload.php';

// The application already owns a global function with the same name.
function is_mobile(string $ua): bool { return str_contains($ua, 'Mobi'); }

$skipped = \RtlyKit\Globals::register();
var_dump($skipped);
// array(1) { [0]=> string(9) "is_mobile" }

var_dump(is_mobile('Mozilla/5.0 (iPhone) Mobile'));   // bool(true)   the application's own function
var_dump(\RtlyKit\is_mobile('09123456789'));           // bool(true)   the Iranian phone-number check, always available
echo jdate('2025-03-21')->format('Y/m/d'), "\n";       // 1404/01/01   free name: registered as a global
var_dump(\RtlyKit\Globals::register());                // same list again, array(1) { [0]=> string(9) "is_mobile" }

A sensible habit is to log the list and carry on:

$skipped = \RtlyKit\Globals::register();
if ($skipped !== []) {
    error_log('RTLY-Kit globals skipped: ' . implode(', ', $skipped));
}
Note. PHP hoists the top-level function declarations of a file. A global that is declared later in the same file as the call still counts as already defined. If a name matters to you, declare it before you call register(), or rely on the namespaced function.

Which style should I use?#

  • Libraries and new applications: namespaced imports (use function RtlyKit\jdate;). They are explicit, easy to search for and free of collisions.
  • Legacy code, scripts and templates: call Globals::register() once and keep the short style.
  • Laravel: either way. The provider adds no globals. Call Globals::register() in AppServiceProvider::register() if you want them, or import the namespaced ones in Blade and classes.