في هذه الصفحة
أسلوبان للإخفاق#
تستخدم كل دالة في RTLY-Kit أحد أسلوبين فقط، فتعرف دائمًا ما الذي يجب حمايته:
- أدوات التحقق لا ترمي استثناءً أبدًا.
NationalCodeوShebaوBankCardوMobileوPostalCodeوVehiclePlate(ومعها الدوال المساعدةis_*/validate_*) تقبل أي قيمة وتجيب بـfalseأو بـResultغير صالح. راجع نظرة عامة على أدوات التحقق. - كل ما عدا ذلك يرمي
RtlyKit\Exceptions\RtlyKitException(أو صنفًا فرعيًا منه) عند مدخل لا يستطيع قبوله. ولا يتسربTypeErrorأوValueErrorأو استثناءDateMalformed*من PHP بسبب مدخل خاطئ؛ فإن حدث ذلك فهو خلل في المكتبة، نرجو الإبلاغ عنه.
تسلسل الاستثناءات#
\InvalidArgumentException
└─ RtlyKit\Exceptions\RtlyKitException implements RtlyKitThrowable
├─ InvalidDateException
├─ InvalidNumberException
├─ InvalidPrayerConfigException
└─ UnsupportedLocaleException
RtlyKitThrowable واجهة علامة (marker interface) تعلن عن getErrorCode(): ErrorCode وgetContext(): array. التقطها (أو التقط RtlyKitException) لمعالجة المكتبة كلها بعبارة واحدة. ولأن RtlyKitException ترث \InvalidArgumentException، فإن الشيفرة الحالية التي تستخدم catch (\InvalidArgumentException) وcatch (\LogicException) تبقى تعمل.
| الاستثناء | يُرمى عند |
|---|---|
InvalidDateException | تواريخ مستحيلة، وسنوات خارج النطاق المدعوم، ونصوص فارغة أو لا يمكن تحليلها، وقيم add*() / sub*() غير معقولة، وطوابع زمنية خارج النطاق، وأنماط تنسيق طويلة جدًا |
InvalidNumberException | مدخل غير رقمي أو كسري، وNaN / INF، ومدخل ضخم الحجم، وأعداد أكبر من المسموح، وكلمات أعداد غير معروفة |
InvalidPrayerConfigException | طريقة حساب صلاة غير معروفة، أو مدينة غير معروفة، أو معامل عصر غير صالح |
UnsupportedLocaleException | لغة (locale) لا تدعمها الدالة (مثل number_to_words(5, 'de')) |
RtlyKitException نفسها | وسائط أخرى خاطئة، مثل فاصل Slugify::make() غير صالح أو طويل جداً، أو نص غير صالح بترميز UTF-8، أو جدول بيانات مضمَّن مفقود |
رموز الأخطاء#
رسائل الاستثناءات موجهة للبشر وقد تُعاد صياغتها في أي إصدار. أما رمز الخطأ فهو الجزء الثابت: وهو enum نصي من النوع ErrorCode، وأسماء حالاته وقيمها جزء من الواجهة العامة (راجع استقرار الواجهة البرمجية).
| الحالة | القيمة | المعنى |
|---|---|---|
InvalidArgument | invalid_argument | رمز احتياطي عام لوسيط غير صالح لا يوجد له رمز أكثر تحديدًا |
InvalidDate | invalid_date | تاريخ أو وقت مشوّه أو مستحيل أو لا يمكن تحليله (شهر 13، أو 30 اسفند في سنة غير كبيسة، أو 'not a date'، أو نص فارغ) |
DateOutOfRange | date_out_of_range | سنة أو تاريخ أو طابع زمني أو قيمة add*() / sub*() خارج النطاق المدعوم (راجع الحدود) |
InvalidNumber | invalid_number | لا يمكن قراءة القيمة كعدد (صحيح) |
NumberTooLarge | number_too_large | عدد أكبر من الحجم المدعوم |
NonFiniteNumber | non_finite_number | NaN أو INF حيث يلزم عدد منتهٍ |
InvalidNumberWords | invalid_number_words | كلمات أعداد غير معروفة أو غير سليمة في NumberToWords::fromWords() |
InputTooLong | input_too_long | مدخل نصي يتجاوز حدًّا موثَّقًا للحجم |
InvalidPrayerConfig | invalid_prayer_config | مدينة أو طريقة حساب أو معامل عصر غير معروف |
UnsupportedLocale | unsupported_locale | لغة (locale) غير اللغات المدعومة |
DataUnavailable | data_unavailable | جدول بيانات مضمَّن مفقود أو تالف (مشكلة في التحزيم وليست خطأ من المستخدم) |
Result::errors() (invalid_format وinvalid_checksum وinvalid_type ...) نصوص عادية خاصة بأدوات التحقق وليست حالات من ErrorCode. فالـ enum مخصص للاستثناءات فقط.قراءة الرمز والسياق#
لكل استثناء في المكتبة الدالتان getErrorCode() وgetContext(). السياق مصفوفة من الحقائق المقروءة آليًا عن الإخفاق (حدّ، أو اللغة المخالفة، أو اسم الوسيط). ولا يحتوي أسرارًا أبدًا وقد يكون فارغًا. يمرّر هذا السكربت خمسة مدخلات خاطئة إلى number_to_words():
<?php
require __DIR__.'/vendor/autoload.php';
use function RtlyKit\number_to_words;
use RtlyKit\Exceptions\RtlyKitThrowable;
foreach ([1.5, NAN, 'abc', str_repeat('9', 22), str_repeat('1', 5000)] as $input) {
try {
number_to_words($input);
} catch (RtlyKitThrowable $e) {
echo (new ReflectionClass($e))->getShortName(), ' ',
$e->getErrorCode()->value, ' ',
json_encode($e->getContext()), "\n";
}
}
// InvalidNumberException invalid_number []
// InvalidNumberException non_finite_number []
// InvalidNumberException invalid_number []
// InvalidNumberException number_too_large {"limit":"10^21 - 1"}
// InvalidNumberException input_too_long {"limit":4096}
أنماط الالتقاط#
تفرّع بحسب الرمز لا بحسب الرسالة. استخدم match مع فرع default، لأن حالات جديدة قد تُضاف في إصدار فرعي.
<?php
require __DIR__.'/vendor/autoload.php';
use RtlyKit\Calendar\Jalali;
use RtlyKit\Exceptions\{ErrorCode, InvalidDateException, RtlyKitThrowable};
function category(RtlyKitThrowable $e): string
{
return match ($e->getErrorCode()) {
ErrorCode::InvalidDate, ErrorCode::DateOutOfRange => 'date',
ErrorCode::InputTooLong => 'too-long',
default => 'other',
};
}
try {
Jalali::create(1404, 13, 1); // لا يوجد شهر 13
} catch (RtlyKitThrowable $e) {
echo category($e), "\n"; // date
}
$e = InvalidDateException::because(ErrorCode::DateOutOfRange, 'Year is out of range', ['year' => 99999]);
echo $e->getErrorCode()->value, ' ', json_encode($e->getContext()), "\n";
// date_out_of_range {"year":99999}
try {
Jalali::create(1404, 13, 1);
} catch (\InvalidArgumentException $e) { // الالتقاط بالأسلوب القديم ما زال يعمل
echo get_class($e), ' ', $e instanceof RtlyKitThrowable ? 'is RtlyKitThrowable' : '', "\n";
// RtlyKit\Exceptions\InvalidDateException is RtlyKitThrowable
}
RtlyKitException::because(ErrorCode, string $message, array $context = [], ?Throwable $previous = null) هي الدالة المنشئة المسمّاة التي تستخدمها المكتبة نفسها لرمي استثناء برمز محدد. يمكنك استخدامها لإخفاقاتك أنت أيضاً، لكن الرمز الذي تضيفه بهذه الطريقة مسؤوليتك أنت وليس جزءًا من عقد المكتبة.
عند التسجيل في السجل، اكتب الرمز والسياق بدلًا من نص الرسالة:
catch (RtlyKitThrowable $e) {
error_log($e->getErrorCode()->value.' '.json_encode($e->getContext()));
}
ما لا يرمي أبدًا#
تقبل أدوات التحقق أي قيمة PHP. ولا ترفع استثناءً بسبب مدخل خاطئ مهما كان نوعه أو حجمه:
<?php
require __DIR__.'/vendor/autoload.php';
use function RtlyKit\validate_national_code;
use RtlyKit\Validation\Sheba;
var_dump(Sheba::isValid(null)); // bool(false)
echo json_encode(validate_national_code([])->errors()), "\n"; // ["invalid_type"]
echo json_encode(validate_national_code(str_repeat('1', 5000))->errors()), "\n"; // ["input_too_long"]
echo json_encode(validate_national_code('0013542418')->errors()), "\n"; // ["invalid_checksum"]
var_dump(validate_national_code('0013542419')->isValid()); // bool(true)
| مدخل أداة التحقق | النتيجة |
|---|---|
string حتى 4096 بايت | يُفحص بصورة طبيعية (الأرقام الفارسية والعربية مقبولة) |
int، وfloat منتهٍ وصحيح القيمة | يُحوَّل إلى نص ثم يُفحص (الأصفار البادئة مفقودة أصلًا في int) |
null، وbool، والمصفوفات، والكائنات، وNaN، وINF، والأعداد الكسرية | غير صالح، والخطأ invalid_type |
string أكبر من 4096 بايت | غير صالح، والخطأ input_too_long |
| UTF-8 غير صالح | غير صالح (عادةً invalid_format) |
تتبع قواعد التحقق في Laravel والتفعيل الاختياري Globals::register() القاعدة نفسها: تُرجع القواعد قيمة منطقية، ولا ترمي Globals::register() استثناءً أبدًا (بل تُرجع الأسماء التي تخطّتها).
سقوف المدخلات#
لكل استدعاء يأخذ نصًا غير موثوق سقف للحجم، فلا يكلّف مدخل خبيث الكثير. تجاوز السقف يرفع input_too_long، إما كاستثناء أو كخطأ من أداة التحقق. القائمة الكاملة في الحدود.
| الموضع | السقف | عند التجاوز |
|---|---|---|
| أدوات التحقق | 4096 بايت لكل نص | Result غير صالح، والخطأ input_too_long |
NumberToWords::convert() / fromWords()، ومدخل Format النصي | 4096 بايت لكل نص | InvalidNumberException |
Format::withSeparator() / format_number() | 1000 نويسة في العدد العشري العادي | InvalidNumberException |
فاصل Slugify::make() | 64 بايت، UTF-8 صالح | RtlyKitException |
نمط format() (Jalali وHijri وHebrew) | 256 بايت (MAX_FORMAT_LENGTH في كل فئة) | InvalidDateException |
قاعدة نطاق التقويم#
كل نقطة دخول في التقويم (make وcreate وcreateFromFormat والطوابع الزمنية وadd* / sub* والدوال المساعدة وماكروات Carbon) إما تُرجع تاريخًا صالحًا وإما ترمي InvalidDateException. لا شيء يلتف حول النطاق ولا يُستخدم أي نوع استثناء آخر. النطاقات: الجلالي من -620 إلى 9377، والهجري من 1 إلى 9665، والعبري من 3762 إلى 13759، وكلها ضمن السنوات الميلادية من 1 إلى 9999. راجع الحدود.
مثال كامل#
use function RtlyKit\jdate;
use function RtlyKit\number_to_words;
use RtlyKit\Exceptions\InvalidDateException;
use RtlyKit\Exceptions\RtlyKitThrowable;
try {
$date = jdate($userInput);
$words = number_to_words($userAmount);
} catch (InvalidDateException $e) {
echo "Please enter a valid date.\n";
} catch (RtlyKitThrowable $e) {
error_log($e->getErrorCode()->value.' '.json_encode($e->getContext()));
}