در این صفحه
شروع سریع#
<?php
require 'vendor/autoload.php';
use RtlyKit\Validation\NationalCode;
var_dump(NationalCode::isValid('0499370899')); // bool(true)
var_dump(NationalCode::isValid('۰۴۹۹۳۷۰۸۹۹')); // bool(true)، رقمهای فارسی
var_dump(NationalCode::isValid('049-937-0899')); // bool(true)، خط تیره حذف میشود
$result = NationalCode::validate('0499370899');
echo json_encode($result->details(), JSON_UNESCAPED_UNICODE);
// {"normalized":"0499370899","location":{"province":"تهران","city":"شهرری"}}
این کلاس قرارداد Validator را دارد. پس validate() یک Result میدهد و برای ورودی بد خطا پرتاب نمیکند. توابع کمکی RtlyKit\is_national_code() و RtlyKit\validate_national_code() هستند:
use function RtlyKit\is_national_code;
use function RtlyKit\validate_national_code;
var_dump(is_national_code('۰۴۹۹۳۷۰۸۹۹')); // bool(true)
print_r(validate_national_code('0499370898')->errors()); // Array ( [0] => invalid_checksum )
چه چیزی بررسی میشود#
بررسیها به همین ترتیب انجام میشوند و با اولین مشکل متوقف میشوند:
- نوع و اندازه. رشته،
intوfloatِ صحیح قبول میشود. چیز دیگرinvalid_typeمیدهد و رشتهٔ بیشتر از ۴۰۹۶ بایتinput_too_long. - یکسانسازی. رقمهای فارسی و عربی انگلیسی میشوند. فاصله، خط تیره و نیمفاصله (ZWNJ) حذف میشوند و دو سر رشته تمیز میشود.
- قالب. دقیقاً ۱۰ رقم، وگرنه
invalid_format. - رقمهای تکراری.
0000000000تا9999999999در حساب جور درمیآیند، ولی کد واقعی نیستند. برای همینrepeated_digitsمیدهند. - رقم کنترل (قاعدهٔ پیمانهٔ ۱۱ در بخش بعد)، وگرنه
invalid_checksum.
الگوریتم رقم کنترل#
نُه رقم اول را به ترتیب در وزنهای ۱۰ تا ۲ ضرب کنید و جمع بزنید. باقیماندهٔ جمع بر ۱۱ را بگیرید. اگر باقیمانده ۰ یا ۱ بود، رقم کنترل خود باقیمانده است. در غیر این صورت رقم کنترل ۱۱ منهای باقیمانده است.
برای 0499370899 حاصلضربها ۰، ۳۶، ۷۲، ۶۳، ۱۸، ۳۵، ۰، ۲۴ و ۱۸ هستند و جمعشان ۲۶۶ است. باقیمانده ۲۶۶ بر ۱۱ برابر ۲ است. پس رقم کنترل باید ۱۱ − ۲ = ۹ باشد، و رقم آخر همین است.
print_r(NationalCode::validate('0499370898')->errors()); // [invalid_checksum]
print_r(NationalCode::validate('1111111111')->errors()); // [repeated_digits]
print_r(NationalCode::validate('12345')->errors()); // [invalid_format]
print_r(NationalCode::validate('abc')->errors()); // [invalid_format]
NationalCode::validate(499370899) فقط نه رقم 499370899 را میبیند و invalid_format میدهد. بین ورودی و اعتبارسنج (از جمله در ستون پایگاه داده یا عدد JSON) مقدار را به عدد صحیح تبدیل نکنید.ورودیهایی که قبول میشوند#
| ورودی | نتیجه |
|---|---|
'0499370899' | معتبر |
'۰۴۹۹۳۷۰۸۹۹' (رقمهای فارسی) | معتبر |
'٠٤٩٩٣٧٠٨٩٩' (رقمهای عربی-هندی) | معتبر |
'049-937-0899'، ' 0499370899 ' | معتبر. جداکننده و فاصله حذف میشود |
null، []، 1.5 | invalid_type |
12.0 | مثل '12' خوانده میشود، پس invalid_format |
| ۴۰۹۷ نویسه | input_too_long |
فقط فاصله، خط تیره و ZWNJ جداکننده حساب میشوند. اسلش، نقطه و حرف حذف نمیشوند و invalid_format میدهند.
اگر فقط شکل تمیز را میخواهید، بدون اعتبارسنجی، از NationalCode::normalize() استفاده کنید:
echo NationalCode::normalize(' ۰۴۹-۹۳۷ ۰۸۹۹ '); // 0499370899
جزئیات Result#
| کلید | نوع | معنا |
|---|---|---|
normalized | رشته | کد بعد از یکسانسازی (برای invalid_type و input_too_long خالی است) |
location | آرایه یا null | برای کد معتبری که پیششمارهٔ آن در جدول هست {province, city} است، وگرنه null |
کدهای خطا: invalid_format، repeated_digits، invalid_checksum، invalid_type و input_too_long. معنی هر کدام در جدول کدهای خطا است.
سرنخ محل صدور#
سه رقم اول کد ملی پیششمارهای است که ثبت احوال داده است. NationalCode::getLocation() آن را به استان و شهر وصل میکند. اگر کد نامعتبر باشد یا پیششماره در جدول نباشد، null میدهد:
print_r(NationalCode::getLocation('0499370899'));
// Array ( [province] => تهران [city] => شهرری )
var_dump(NationalCode::getLocation('0499370898')); // NULL (کد نامعتبر)
$a = NationalCode::validate('0012345679'); // پیششمارهٔ 001
echo json_encode($a->details()['location'], JSON_UNESCAPED_UNICODE);
// {"province":"تهران","city":"تهران مرکزی"}
$b = NationalCode::validate('9991234561'); // رقم کنترل درست، پیششمارهٔ 999 در جدول نیست
var_dump($b->isValid(), $b->details()['location']); // bool(true) NULL
اعتبارسنجی چه چیزی را نمیگوید#
- رقم کنترل درست یعنی عدد خوشقالب است. نمیگوید این کد واقعاً صادر شده یا مال کسی است.
- شخص حقوقی شناسهٔ ۱۱ رقمیِ جدا دارد. این اعتبارسنج آن را پوشش نمیدهد و با
invalid_formatرد میکند. - شمارهٔ شناسایی اتباع خارجی (مثل آمایش) طرح دیگری دارد و پوشش داده نمیشود.
در فرم#
پیام را از روی کد خطا بسازید و مقدار یکسانشده را ذخیره کنید:
$messages = [
'invalid_format' => 'کد ملی باید ۱۰ رقم باشد.',
'repeated_digits' => 'این کد ملی معتبر نیست.',
'invalid_checksum' => 'این کد ملی معتبر نیست.',
];
$result = NationalCode::validate($_POST['national_code'] ?? null);
if (! $result->isValid()) {
$key = $result->errors()[0];
echo $messages[$key] ?? 'ورودی نامعتبر است.';
} else {
$code = $result->details()['normalized']; // همین را ذخیره کنید
}
در Laravel از قانون و ترجمههای آماده استفاده کنید. اعتبارسنجی و cast در Laravel را ببینید.