On this page
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
RtlyKitnamespace. 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#
| Helper | Signature | Calls |
|---|---|---|
jdate | jdate(DateTimeInterface|string|int|null $time = null, ?DateTimeZone $timezone = null): Jalali | Jalali::make(), see Jalali |
hdate | hdate(DateTimeInterface|string|int|null $time = null, ?DateTimeZone $timezone = null): Hijri | Hijri::make(), see Hijri |
hebrew_date | hebrew_date(DateTimeInterface|string|int|null $time = null, ?DateTimeZone $timezone = null): Hebrew | Hebrew::make(), see Hebrew |
Digits and numbers#
| Helper | Signature | Example |
|---|---|---|
to_persian_digits | (string|int|float $value): string | to_persian_digits(1404) gives ۱۴۰۴ |
to_english_digits | (string $value): string | to_english_digits('۱۴۰۴') gives 1404 |
to_persian | (string|int|float $value): string | short alias of to_persian_digits. to_persian('12.5') gives ۱۲.۵ |
to_english | (string $value): string | short alias of to_english_digits. to_english('٣٤') gives 34 |
number_to_words | (int|float|string $number, string $locale = 'fa'): string | number_to_words(25, 'ar') gives خمسة وعشرون |
format_number | (int|float|string $number): string | format_number(1234567) gives ۱٬۲۳۴٬۵۶۷ |
ordinal | (int|float $number): string | ordinal(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#
| Helper | Signature | Example |
|---|---|---|
normalize_text | (string $text): string | normalize_text('كتاب ي') gives کتاب ی (Arabic kaf and yeh become Persian) |
contains_rtl | (string $text): bool | contains_rtl('abc سلام') gives true |
text_direction | (string $text): string | text_direction('سلام') gives rtl, text_direction('hello') gives ltr |
See text tools.
Validation: yes or no#
Each returns bool and accepts mixed.
| Helper | Signature | Example |
|---|---|---|
is_national_code | (mixed $value): bool | is_national_code('0013542419') gives true |
is_sheba | (mixed $value): bool | is_sheba('IR062960000000100324200001') gives true |
is_bank_card | (mixed $value): bool | is_bank_card('6037997535328737') gives true |
is_mobile | (mixed $value): bool | is_mobile('09123456789') gives true |
is_postal_code | (mixed $value): bool | is_postal_code('1676543210') gives true |
is_vehicle_plate | (mixed $value): bool | is_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.
| Helper | Signature |
|---|---|
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#
| Helper | Signature | Notes |
|---|---|---|
is_iran_holiday | (Jalali|int $year, ?int $month = null, ?int $day = null): bool | wraps 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'): array | times 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::NAMESis 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));
}
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()inAppServiceProvider::register()if you want them, or import the namespaced ones in Blade and classes.