مرور اعتبارسنج‌ها

شش اعتبارسنج ایرانی چطور کار می‌کنند. قرارداد مشترک Validator، شیء Result، کدهای خطای پایدار، یکسان‌سازی ورودی و سقف اندازه.

در این صفحه
  1. شش اعتبارسنج
  2. دو راه برای صدا زدن
  3. قرارداد Validator
  4. شیء Result
    1. کلیدهای جزئیات هر اعتبارسنج
  5. کدهای خطا
  6. ورودی چطور پردازش می‌شود
  7. یکسان‌سازی
  8. جدول‌های جست‌وجو و منبع داده
  9. ادامه

شش اعتبارسنج#

RTLY-Kit برای داده‌هایی که برنامه‌های ایرانی هر روز با آن‌ها کار می‌کنند شش اعتبارسنج دارد. همه ساختار یکسانی دارند. وقتی یکی را یاد بگیرید، بقیه را هم می‌شناسید.

کلاسچه چیزی را بررسی می‌کندصفحه
NationalCode۱۰ رقم، رقم کنترل به پیمانهٔ ۱۱، سرنخی از محل صدورکد ملی
ShebaIR و ۲۴ رقم، ISO 7064 mod-97-10، پیدا کردن بانکشبا و کارت بانکی
BankCard۱۶ رقم، چک‌سام Luhn، تشخیص بانک از روی BINشبا و کارت بانکی
Mobileقالب 09xxxxxxxxx در پنج شکل ورودی، اپراتور و تخصیص پیش‌شمارهموبایل، کدپستی، پلاک
PostalCode۱۰ رقم، رقم اول غیر از 0موبایل، کدپستی، پلاک
VehiclePlateقالب پلاک سواری، تقسیم‌شده به بخش‌هاموبایل، کدپستی، پلاک

همه در فضای‌نام RtlyKit\Validation هستند. رقم‌های فارسی و عربی را همان‌طور که کاربر تایپ کرده قبول می‌کنند. پس ورودی فرم را بدون تمیزکاری مستقیم به آن‌ها بدهید.

دو راه برای صدا زدن#

هر اعتبارسنج هم جواب بولی می‌دهد و هم جواب کامل. اگر فقط «بله یا نه» می‌خواهید، isValid() را بزنید. اگر می‌خواهید دلیل رد شدن را به کاربر بگویید، validate() را بزنید.

<?php
require 'vendor/autoload.php';

use RtlyKit\Validation\NationalCode;
use function RtlyKit\is_national_code;

var_dump(NationalCode::isValid('۰۴۹۹۳۷۰۸۹۹'));   // bool(true)
var_dump(is_national_code('0499370899'));        // bool(true)

$result = NationalCode::validate('0499370898');
var_dump($result->isValid());                    // bool(false)
print_r($result->errors());                      // Array ( [0] => invalid_checksum )

توابع کمکی (is_national_code()، validate_sheba() و مانند این‌ها) در فضای‌نام خودشان هستند و با use function RtlyKit\is_national_code; وارد می‌شوند. نام‌های کوتاه سراسری اختیاری است. توابع کمکی و سراسری را ببینید.

قرارداد Validator#

هر اعتبارسنج اینترفیس RtlyKit\Contracts\Validator را پیاده می‌کند. این اینترفیس دو متد استاتیک دارد:

  • validate(mixed $value): Result
  • isValid(mixed $value): bool، که کوتاه‌شدهٔ validate($value)->isValid() است

اعتبارسنج‌ها حالت ندارند و برای همین استاتیک‌اند. پس می‌توانید اعتبارسنج را موقع اجرا با class-string انتخاب کنید، مثلاً از روی یک آرایهٔ تنظیمات:

use RtlyKit\Contracts\Validator;
use RtlyKit\Validation\Mobile;
use RtlyKit\Validation\NationalCode;
use RtlyKit\Validation\PostalCode;

/** @var array<string, class-string<Validator>> $rules */
$rules = [
    'national_code' => NationalCode::class,
    'mobile'        => Mobile::class,
    'postal'        => PostalCode::class,
];
$input = ['national_code' => '0499370899', 'mobile' => '0912', 'postal' => '1234567890'];

foreach ($rules as $field => $class) {
    $result = $class::validate($input[$field]);
    echo $field, ': ', $result->isValid() ? 'ok' : implode(',', $result->errors()), "\n";
}
// national_code: ok
// mobile: invalid_format
// postal: ok
خطا پرتاب نمی‌کند. هیچ‌کدام از این دو متد برای ورودی بد خطا نمی‌دهد. هر مقداری، از null و آرایه و شیء تا رشتهٔ خیلی بلند و UTF-8 خراب، یک Result نامعتبر با کد خطای پایدار می‌دهد. خطا فقط برای اشتباه برنامه‌نویس در بخش‌های دیگر کتابخانه است. مدیریت خطا را ببینید.

شیء Result#

RtlyKit\Validation\Result یک مقدار تغییرناپذیر با سه متد خواندن است:

متدخروجی
isValid()bool
errors()list<string>. کدهای خطای پایدار. اگر معتبر باشد خالی است
details()array<string, mixed>. چیزهایی که اعتبارسنج در راه فهمیده

جزئیات هم برای نتیجهٔ معتبر هست و هم برای نامعتبر (تا هر جا که اعتبارسنج جلو رفته باشد). پس می‌توانید کنار پیام خطا، مقدار یکسان‌شده یا نام بانک را هم نشان بدهید:

use RtlyKit\Validation\BankCard;

$result = BankCard::validate('6037991234567890');
var_dump($result->isValid());   // bool(false)
print_r($result->errors());     // Array ( [0] => invalid_checksum )
echo json_encode($result->details(), JSON_UNESCAPED_UNICODE);
// {"normalized":"6037991234567890","bin":"603799","bank_name":"بانک ملی ایران"}

کلیدهای جزئیات هر اعتبارسنج#

اعتبارسنجکلیدهای جزئیات
NationalCodenormalized، location ({province, city} یا null)
Shebanormalized، bank_code، bank_name
BankCardnormalized، bin، bank_name
Mobilenormalized، operator، allocated (bool)
PostalCodenormalized
VehiclePlatenormalized و، اگر قالب بخورد: two_digit، letter، three_digit، region

مقدارهایی که از جدول می‌آیند (location، bank_name، operator) اگر ردیفی پیدا نشود null هستند. پیدا نشدن یعنی «نمی‌دانیم»، نه «نامعتبر».

کدهای خطا#

کدهای خطا پایدار و ماشین‌خوان‌اند و بین نسخه‌ها عوض نمی‌شوند. می‌توانید هر کدام را به پیام یا ترجمهٔ خودتان وصل کنید. بخش Laravel هم دقیقاً همین کار را می‌کند. اعتبارسنجی و cast در Laravel را ببینید.

کدمعناکجا می‌آید
invalid_typeمقدار رشته، عدد صحیح یا عدد اعشاریِ صحیح و متناهی نیستهمه
input_too_longرشته از ۴۰۹۶ بایت بلندتر استهمه
invalid_formatشکل ورودی درست نیست (نویسه‌ها، تعداد رقم، الگو)همه. در PostalCode برای رقم اول 0
invalid_lengthتعداد رقم‌ها ۱۰ نیستPostalCode
repeated_digitsهمهٔ رقم‌ها یکی هستند (1111111111)NationalCode، BankCard
invalid_checksumرقم کنترل جور درنمی‌آیدNationalCode، Sheba، BankCard
invalid_regionیکی از بخش‌های عددیِ پلاک همه صفر استVehiclePlate

الان هر نتیجه حداکثر یک خطا دارد (اولین بررسی که رد شد). ولی errors() فهرست می‌دهد تا بعداً بشود چند خطا را با هم گزارش کرد.

ورودی چطور پردازش می‌شود#

پیش از هر قاعده، مقدار از یک مرحلهٔ مشترک رد می‌شود:

  • رشته‌ها تا ۴۰۹۶ بایت قبول می‌شوند. هر رقم فارسی یا عربی دو بایت است، پس سقف حدود ۲۰۰۰ رقم از این نوع است. رشتهٔ بلندتر input_too_long می‌دهد.
  • int با (string) به رشته تبدیل می‌شود.
  • float باید متناهی و صحیح باشد و قدرمطلقش از 1015 کمتر باشد. بدون نمای علمی به رشته تبدیل می‌شود. NaN، INF، 1.5 و عددهای خیلی بزرگ invalid_type می‌دهند.
  • بقیه، یعنی null، bool، آرایه و شیء، invalid_type می‌دهند.
use RtlyKit\Validation\NationalCode;
use RtlyKit\Validation\PostalCode;

print_r(NationalCode::validate(null)->errors());                  // [invalid_type]
print_r(NationalCode::validate(1.5)->errors());                   // [invalid_type]
print_r(NationalCode::validate(str_repeat('1', 4097))->errors()); // [input_too_long]
print_r(NationalCode::validate("\xff\xfe")->errors());            // [invalid_format]
var_dump(PostalCode::validate(1234567890)->isValid());            // bool(true)
صفر اول و عدد صحیح. اگر کد ملی یا کدپستی را عدد صحیح بدهید، صفرهای اولش از بین می‌رود. 499370899 با 0499370899 یکی نیست و اعتبارسنج فقط نه رقم می‌بیند. این شماره‌ها را از فرم یا ستون پایگاه داده تا اعتبارسنج به شکل رشته نگه دارید.

یکسان‌سازی#

بعد از آن مرحله، هر اعتبارسنج رشته را با متد عمومی normalize() خودش یکسان می‌کند. قاعده‌های مشترک:

  • رقم‌های فارسی (۰-۹) و عربی-هندی (٠-٩) با Digits::toEnglish() انگلیسی می‌شوند.
  • جداکننده‌هایی که کاربر می‌تایپد حذف می‌شوند: فاصله، خط تیره و نیم‌فاصله (ZWNJ). Mobile و BankCard و PostalCode بیشتر پاک می‌کنند و فقط رقم‌ها را نگه می‌دارند.
  • در Sheba حروف بزرگ می‌شوند و اگر فقط ۲۴ رقم باشد، پیشوند IR اضافه می‌شود.

مقدار یکسان‌شده همیشه در details()['normalized'] هست. همان را ذخیره کنید، نه ورودی خام را. این‌طور یک شماره هیچ‌وقت با دو املای مختلف ذخیره نمی‌شود.

جدول‌های جست‌وجو و منبع داده#

چهار اعتبارسنج یک جست‌وجو هم دارند: بانک از روی BIN کارت، بانک از روی کد شبا، محل صدور از روی پیش‌شمارهٔ کد ملی و اپراتور از روی پیش‌شمارهٔ موبایل. این‌ها امکان کمکی‌اند و معتبر بودن به آن‌ها بستگی ندارد.

  • BIN کارت بانکی: ۳۹ مورد. کد بانک در شبا: ۳۸ مورد. پیش‌شمارهٔ کد ملی: ۵۴۷ مورد.
  • هر مورد وقتی وارد جدول می‌شود که دست‌کم دو منبع مستقل با هم موافق باشند. اگر مورد در جدول نباشد، یعنی ناشناخته است.
  • کد بانک‌های شبا با مشخصات رسمی IBAN که بانک مرکزی منتشر کرده (۱۹ کد) برابر است. بقیهٔ جدول‌ها از چند صفحهٔ عمومی گرفته شده‌اند و با هم سازگارند.
خوب است بدانید. بانک‌ها ادغام می‌شوند، نامشان عوض می‌شود و شماره‌ها قابل انتقال بین اپراتورها هستند. پس نتیجهٔ جست‌وجو را به شکل «اشاره» نشان دهید و تصمیم مالی یا حقوقی را روی آن بنا نکنید. منبع‌ها و تاریخ بررسی‌ها در دقت و داده است.

ادامه#