راهنمای ارتقا

تغییرهایی که پیش از نسخهٔ ۱٫۰ ممکن است روی کد شما اثر بگذارند، با مثال قبل و بعد و گام‌های مهاجرت، و نکته‌های مهم تغییرنامهٔ سری 0.x.

در این صفحه
  1. از 0.1.x به 0.2.0
  2. از ساخت‌های اولیه (قبل از 0.1.0): توابع کمکی در فضای‌نام
    1. ۱. توابع کمکی سراسری دیگر پیش‌فرض تعریف نمی‌شوند
    2. ۲. تغییرهای دیگر که باید بدانید
  3. نکته‌های مهم تغییرنامه برای سری 0.x
    1. رفتارهایی که عوض شد
    2. چیزهای اضافه‌شده
    3. رفع شده‌ها

قبل از ۱٫۰٫۰، هر نسخهٔ minor ممکن است تغییر ناسازگار داشته باشد. هر مورد اینجا با گام‌های مهاجرت آمده است. سیاست نسخه‌گذاری در پایداری API است. این صفحه از جدیدترین نسخه شروع می‌شود. بخش آخر به ساخت‌های اولیهٔ قبل از 0.1.0 می‌پردازد.

از 0.1.x به 0.2.0#

بیشتر برنامه‌ها نیازی به تغییر ندارند. موارد زیر را نگاه کنید. موردهای ۹ تا ۱۲ خروجی کتابخانه را برای بعضی ورودی‌ها عوض می‌کنند.

  1. ext-mbstring دیگر لازم نیست. composer.json قبلاً این افزونه را می‌خواست. حالا فقط PHP لازم است. کاری لازم نیست. اگر ext-mbstring را فقط برای این بسته به composer.json خودتان اضافه کرده بودید، می‌توانید حذفش کنید.
  2. قالب‌بندی float. Format::withSeparator() و format_number() حالا float را با کوتاه‌ترین عدد اعشاری می‌نویسند که دوباره همان float را بدهد، با حداکثر ۱۵ رقم معنادار. خطای دودویی دیگر دیده نمی‌شود. رشته‌ها عوض نشده‌اند و دقیق می‌مانند. برای رقم بیشتر رشته بدهید.
فراخوانیقبلبعد
withSeparator(-1234567.891)-۱٬۲۳۴٬۵۶۷٫۸۹۱۰۰۰۰۰۰۰۶۱۴۶۷-۱٬۲۳۴٬۵۶۷٫۸۹۱
withSeparator(0.1 + 0.2)۰٫۳۰٫۳ (بدون تغییر)
withSeparator(-0.0)۰۰

اگر نتیجهٔ محاسبهٔ اعشاری را قالب‌بندی می‌کنید، اول گرد کنید (round($x, 2)) یا رشته بدهید.

  1. NumberToWords::fromWords() سخت‌گیر شد. قبلاً واژه‌ها را به هر ترتیبی جمع می‌زد.
ورودیقبلبعد
دو صد102InvalidNumberException (با invalid_number_words)
بیست یک21InvalidNumberException
صد و بیست و120InvalidNumberException
پنج و بیست25InvalidNumberException
سی و پنج3535

بین بخش‌ها «و» بگذارید و ترتیب صدگان، دهگان، یکان را رعایت کنید. هر چه convert() بسازد هنوز درست خوانده می‌شود.

  1. Slugify::make() برای متنی که UTF-8 معتبر نیست RtlyKitException می‌دهد (با invalid_argument و argument برابر text). قبلاً فقط جداکننده بررسی می‌شد. RtlyKitThrowable را بگیرید یا ورودی را اول تمیز کنید.
  2. Hijri::format() و Hebrew::format() الگوی بلندتر از ۲۵۶ بایت را با InvalidDateException رد می‌کنند (با input_too_long، و argument برابر format، و limit برابر ۲۵۶). Jalali::format() از قبل همین‌طور بود. سقف در Hijri::MAX_FORMAT_LENGTH و Hebrew::MAX_FORMAT_LENGTH است.
  3. بک‌اسلش آخر الگوی format در هر سه تقویم نادیده گرفته می‌شود. format('Y\') فقط سال را می‌دهد.
  4. IranHolidays::all() و allTitles() و allFixed() برای هر سال جلالی از ‎-620 تا 9377 کار می‌کنند. قبلاً سال‌های قبل از مبدأ هجری و آخرین روز 9377 invalid_date می‌دادند. جایی که تعطیلات اسلامی را نشود حساب کرد، فقط تعطیلات ثابت می‌آید. برای سال بیرون از بازه InvalidDateException می‌دهند (با date_out_of_range و context شامل year، min و max).
  5. پیام خطاها وقتی ورودی شما را تکرار می‌کنند آن را به ۴۰ نویسه کوتاه می‌کنند و UTF-8 معتبر می‌مانند. اگر روی متن پیام شرط می‌گذارید، به‌جایش روی getErrorCode() شرط بگذارید.
  6. تاریخ‌های رسمی تعطیلات ۱۳۸۰ تا ۱۴۰۵. در این سال‌های جلالی، متدهای استاتیک (IranHolidays و is_iran_holiday()) حالا تاریخ‌های منتشرشده را می‌دهند، نه تخمین ام‌القری. بقیهٔ سال‌ها عوض نشده‌اند.
روزقبل (تخمین)بعد (رسمی)
1404/01/10عید فطرتعطیل نیست
1404/01/11تعطیل عید فطرعید فطر
1404/01/01جشن نوروز + شهادت امام علیجشن نوروز
1404/12/29ملی شدن صنعت نفت + عید فطرملی شدن صنعت نفت
1405/01/01جشن نوروز + تعطیل عید فطرجشن نوروز + عید فطر

در سال‌های رسمی فقط عنوان‌هایی می‌آیند که در جدول رسمی هستند. سال‌های ۱۳۸۰ تا ۱۳۹۳ و ۱۳۹۵ گزارش‌شده هستند: تاریخ‌ها از یک منبع‌اند و فهرست روزها ممکن است کامل نباشد. برای رفتار قبلی در همهٔ سال‌ها، HolidayCalendar::default()->withOfficialData(false) را به کار ببرید. برای اینکه ببینید یک سال از کجا آمده، IranHolidays::sourceOf($year) را بزنید. بیشتر در تقویم تعطیلات.

  1. اوقات شرعی در عرض‌های بالا. PrayerTimes حالا HighLatitudeRule دارد. پیش‌فرض AngleBased است. فقط جایی اثر می‌کند که وقت صبح یا عشا نباشد، یا از طلوع یا غروب دورتر از حد قاعده بیفتد. جنوب حدود ۴۴ درجهٔ شمالی چیزی عوض نمی‌شود. شمال حدود ۴۴ تا ۴۶ درجه، نزدیک انقلاب تابستانی (ژوئن)، بعضی مقدارها فرق می‌کنند. وقتی که قبلاً null بود حالا مقدار دارد.
مکان و روز (MWL)قبل (مثل None)بعد (پیش‌فرض)
استکهلم، 2026-06-21صبح null، عشا nullصبح 01:54، عشا 23:40
مونیخ، 2026-06-21صبح 01:50، عشا 00:12صبح 02:51، عشا 23:32

برای خروجی قبلی، $prayer->withHighLatitudeRule(HighLatitudeRule::None) را بزنید. nextPrayer() هم حالا نمازی را که بعد از نیمه‌شب می‌افتد پیدا می‌کند. عرض‌های جغرافیایی بالا را ببینید.

  1. اعتبارسنجی موبایل. بررسی شکل همان است، پس اعتبار هیچ ورودی عوض نمی‌شود. دو چیز تازه است. هر نتیجه کلید allocated دارد و Mobile::isAllocated() آن را به شکل bool می‌دهد. جدول اپراتور هم به طرح شماره‌گذاری نزدیک‌تر شد: کلید شاتل موبایل از 998 به 09981 و 09982 محدود شد، کلید آپتل از 99910 به 9991 گسترده شد، و 0923 و 0931 و 0932 و 0934 اضافه شدند.
شمارهاپراتور قبلاپراتور بعد
09981234567شاتل موبایلشاتل موبایل
09983112345شاتل موبایلnull (تخصیص‌یافته، ولی دارنده تأیید نشده)
09231234567nullرایتل
09321234567nullتالیا

اگر کل آرایهٔ details() را مقایسه می‌کنید، منتظر کلید اضافه‌ی allocated باشید.

  1. عدد به حروف عربی. convert($n, 'ar') بدون گزینه برای هر عدد زیر 109 همان متن قبلی را می‌دهد. حالا عدد تا 1027 هم قبول می‌کند، که قبلاً number_too_large می‌داد. گزینه‌های تازه و عدد ترتیبی در عدد به حروف عربی است.
  2. پلتفرم‌ها. PHP 8.2 تا 8.5، Laravel 11 و 12 و 13 و Carbon 3 پشتیبانی می‌شوند. پایداری API را ببینید.

از ساخت‌های اولیه (قبل از 0.1.0): توابع کمکی در فضای‌نام#

۱. توابع کمکی سراسری دیگر پیش‌فرض تعریف نمی‌شوند#

تغییر ناسازگار. ساخت‌های اولیه همین که Composer بسته را بارگذاری می‌کرد، jdate() و to_persian() و is_national_code() و بقیه را سراسری تعریف می‌کردند. این می‌توانست با کد شما یا بستهٔ دیگری تداخل کند، پس برداشته شد. توابع کمکی حالا در فضای‌نام RtlyKit هستند و چیزی سراسری تعریف نمی‌شود.

۲۷ تابع کمکی: jdate، hdate، hebrew_date، to_persian_digits، to_english_digits، to_persian، to_english، is_national_code، is_sheba، is_bank_card، is_mobile، is_postal_code، is_vehicle_plate، validate_national_code، validate_sheba، validate_bank_card، validate_mobile، validate_postal_code، validate_vehicle_plate، number_to_words، normalize_text، contains_rtl، text_direction، is_iran_holiday، prayer_times، format_number و ordinal.

راه الف: هر چه لازم دارید import کنید (پیشنهاد ما).

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

echo jdate('2026-03-21')->format('Y/m/d');   // 1405/01/01

یا با نام کامل صدا بزنید: \RtlyKit\jdate('2026-03-21').

راه ب: نام‌های کوتاه سراسری قبلی را برگردانید. یک بار Globals::register() را صدا بزنید، مثلاً در فایل راه‌اندازی:

\RtlyKit\Globals::register();

echo jdate('2026-03-21')->format('Y/m/d');   // 1405/01/01، مثل قبل کار می‌کند

register() فقط نام‌های آزاد را تعریف می‌کند. تابع موجود را عوض نمی‌کند و خطا پرتاب نمی‌کند. نام‌هایی را که چون جای دیگری استفاده شده بودند رد کرده، برمی‌گرداند. چند بار صدا زدنش مشکلی ندارد:

$skipped = \RtlyKit\Globals::register();
if ($skipped !== []) {
    error_log('RTLY-Kit globals skipped: '.implode(', ', $skipped));   // برای این‌ها از \RtlyKit\name() استفاده کنید
}

service provider در Laravel هم تابع سراسری ثبت نمی‌کند. در Blade بنویسید {{ \RtlyKit\jdate($post->created_at)->format('j F Y') }}، یا Globals::register() را در AppServiceProvider::register() صدا بزنید. بیشتر در توابع کمکی و سراسری.

۲. تغییرهای دیگر که باید بدانید#

تغییرچه کار کنید
اعتبارسنج‌ها هر مقداری را قبول می‌کنند. هر شش تا RtlyKit\Contracts\Validator را دارند (validate(mixed): Result و isValid(mixed): bool). ورودی غیررشته‌ای (null، آرایه، شیء) یک Result نامعتبر با invalid_type می‌دهد. ورودی بیش از ۴۰۹۶ بایت input_too_long می‌دهد. برای ورودی بد هیچ‌وقت خطا پرتاب نمی‌کنند.try و بررسی نوع دور اعتبارسنج را بردارید. مرور اعتبارسنج‌ها را ببینید.
خطاها کد دارند. getErrorCode() (یک enum به نام ErrorCode) و getContext() روی هر خطای کتابخانه هست.کاری لازم نیست. روی کد شرط بگذارید، نه روی متن پیام. مدیریت خطا را ببینید.
Slugify::make() خطا می‌دهد (RtlyKitException با input_too_long یا invalid_argument) اگر جداکننده UTF-8 معتبر نباشد یا از ۶۴ بایت بلندتر باشد.فقط وقتی مهم است که جداکننده را کاربر تعیین کند. RtlyKitThrowable را بگیرید.
تقویم‌ها قرارداد مشترک دارند. Jalali و Hijri و Hebrew اینترفیس RtlyKit\Contracts\CalendarDate را دارند. مقایسه‌ها و diffIn*() یک CalendarDate یا هر DateTimeInterface قبول می‌کنند، make() یک CalendarDate قبول می‌کند، و equals() و isBefore() و isAfter() نام‌های تازه هستند.فراخوانی‌های قبلی کار می‌کنند. تبدیل و مقایسه را ببینید.
داده‌های مرجع جابه‌جا شدند به resources/data/*.php.اگر کلاس‌های جدول قبلی را مستقیم می‌خواندید (هیچ‌وقت API نبودند)، به کلاس‌های عمومی مثل Hijri یا Sheba::getBankName() یا NationalCode::getLocation() بروید.
عشای مکه در رمضان عوض شد. PrayerTimes::METHOD_MAKKAH حالا در رمضان عشا را مغرب + ۱۲۰ دقیقه می‌دهد (روش ام‌القری) و در بقیهٔ زمان + ۹۰ دقیقه. ساخت قبلی همیشه ۹۰ می‌داد.عشای رمضان ۳۰ دقیقه دیرتر می‌شود. این رفع باگ است. مقدارهای مورد انتظار ذخیره‌شده را به‌روز کنید. نمونه پایین‌تر است.

رفتار مکه (2026-03-01 در رمضان است و 2026-03-21 نیست):

use RtlyKit\Prayer\PrayerTimes;

$mecca = PrayerTimes::forCity('mecca', PrayerTimes::METHOD_MAKKAH);
foreach (['2026-03-01', '2026-03-21'] as $d) {
    $t = $mecca->getTimes(new DateTimeImmutable($d));
    echo $d, ' maghrib ', $t['maghrib'], ' isha ', $t['isha'], "\n";
}
// 2026-03-01 maghrib 18:25 isha 20:25
// 2026-03-21 maghrib 18:32 isha 20:02

نکته‌های مهم تغییرنامه برای سری 0.x#

این‌ها نکته‌هایی از تغییرنامه‌اند که کاربران می‌بینند. فهرست کامل در CHANGELOG.md مخزن است.

رفتارهایی که عوض شد#

  • ورودی تقویم بیرون از بازه خطا می‌دهد. سال، زمان یونیکس و مقدار خیلی بزرگ add*() و sub*() بیرون از بازه، به‌جای TypeError یا تاریخ سرریزشده، InvalidDateException می‌دهند. بازه‌ها: جلالی ‎-620 تا 9377، هجری 1 تا 9665، عبری 3762 تا 13759 (سقف‌ها).
  • قاعدهٔ رشته در make() ثابت شد. سال کمتر از 1700 (در عبری 3000 یا بیشتر) در تقویم همان کلاس خوانده می‌شود و بقیه میلادی است. رشتهٔ خالی خطا می‌دهد و null یعنی همین الان. رشتهٔ میلادی ISO با سال کمتر از 1700 جلالی یا هجری خوانده می‌شود، پس برای چنین تاریخی DateTimeImmutable بدهید (پرسش‌های متداول).
  • تعطیلات. تاریخ‌های رسمی برای جلالی ۱۳۸۰ تا ۱۴۰۵ (بالا را ببینید). برای بقیهٔ سال‌ها تعطیلات اسلامی فقط برای ۱۳۰۰ تا ۱۵۰۰ هجری قمری گزارش می‌شود و سال بیرون از بازهٔ جلالی InvalidDateException می‌دهد.
  • ورودی عدد. NumberToWords::convert() و Format::ordinal() float صحیح (3.0) را قبول می‌کنند و برای float کسری و NaN و INF InvalidNumberException می‌دهند.
  • سقف ورودی (امنیت): رشته‌های بیش از ۴۰۹۶ بایت در NumberToWords و Format رد می‌شوند و Format::withSeparator() عدد بیش از ۱۰۰۰ نویسه را رد می‌کند. InvalidNumberException (با input_too_long) می‌دهند.
  • جدول‌های داده فقط موردهای تأییدشده را نگه می‌دارند. NationalCode::getLocation() ۵۴۷ پیش‌شماره را دارد و برای بقیه null می‌دهد. جدول BIN بانک (۳۹)، کد شبا (۳۸) و اپراتور فقط موردهایی را دارند که دو منبع تأییدشان کرده‌اند. نامی که قبلاً می‌آمد ممکن است حالا null باشد. BankCard شماره‌ای را که یک رقم تکراری است رد می‌کند.
  • هستهٔ اوقات شرعی از نو نوشته شد از معادله‌های استاندارد نجومی. فاصله با پیاده‌سازی قبلی در شبکهٔ مقایسه برای رویدادهای عصر حداکثر ۲ دقیقه بود (میانگین ۰٫۵ دقیقه یا کمتر). ساعت تابستانی برای هر رویداد جدا اعمال می‌شود و آرگومان سازندهٔ $elevation تازه است.

چیزهای اضافه‌شده#

  • تقویم عبری با PHP خالص (Hebrew) بدون نیاز به ext-calendar. تقویم هجری حول جدول ام‌القری ۱۳۰۰ تا ۱۵۰۰ بازسازی شد، با HijriVariant::Tabular برای بیرون از آن.
  • اعتبارسنجی ساخت‌یافته: Result (isValid() و errors() و details()) و توابع validate_*() با کدهای خطای پایدار.
  • NumberToWords::fromWords()، عدد تا ۲۱ رقم، عدد منفی، و عدد به حروف عربی (number_to_words($n, 'ar')).
  • Normalizer::fixHalfSpace() و clean()، Detector::isRtlLocale() و isHebrew()، IranHolidays::getTitles() و isBusinessDay() و nextBusinessDay()، و PrayerTimes::nextPrayer().
  • Laravel: facade به نام Jalali و factory در container، شناسایی خودکار، شش قانون اعتبارسنجی با پیام fa و en و ar، و JalaliCast.
  • ماکروهای Carbon: toJalali، jformat، createFromJalali، toHijri، toHebrew، createFromHijri، createFromHebrew.
  • سلسله‌مراتب خطا زیر RtlyKitThrowable، enum به نام ErrorCode، Globals::register()، و قراردادهای CalendarDate و Validator.

رفع شده‌ها#

  • عشای روش مکه در رمضان (بالا توضیح داده شد).
  • Format::ordinal() برای ۳۰ و ۲۳. Format::withSeparator() رقم فارسی و عربی را بدون گرد کردن قبول می‌کند. Mobile شکل‌های 0098 و 98 و 9xxxxxxxxx ساده را قبول می‌کند. Sheba شکل ۲۴ رقمیِ ساده را قبول می‌کند.
  • کار با منطقهٔ زمانی در اوقات شرعی دور ساعت تابستانی، و خطا برای روش یا ضریب عصر ناشناخته.
  • تعریف تکراری تابع hdate().
نکته. تا وقتی کتابخانه زیر ۱٫۰٫۰ است، یک بازهٔ minor ثابت کنید (مثلاً ~0.2.0)، قبل از هر ارتقای minor این صفحه را بخوانید، و تست‌های خودتان را روی تاریخ و اوقات شرعی ذخیره‌شده اجرا کنید.