اعتبار سنجی
اطلاعات ارسال شده از سمت کاربر می بایست قبل از هرگونه عملیات، اعتبار سنجی شوند . بررسی رشته های دریافتی برای اطمینان از عدم وجود کد و یا کد های مخرب برنامه نویسی یکی از مهم ترین کارهایی است که در این بخش باید کنترل شوند .
فقط فیلد هایی که باید دریافت کنید را مشخص کنید تا مابقی فیلد ها ( که ممکن است توسط افراد مخرب در مقدار های ارسالی گنجانده شده اند ) نادیده گرفته شوند . میتوانید نوع و یا مقداری که انتطار دارید توسط کاربر از هر فیلد دریافت کنید را مشخص کنید . اعتبار سنج به صورت خودکار هر مقدار و یا نوعی به غیر از آنچه مشخص شده دریافت کنید ، با پرتاب یک استثناء از جنس packages\base\InputValidationException
از ادا مه عملیات جلوگیری خواهد کرد .
برای این موضوع میتوانید از متد checkinputs
کلاس packages\base\controller
استفاده کنید .
متد checkinputs
آرگومان ورودی این متد آرایه ای میباشد که کلیدهای آن نام فیلد های فرم هستند و هرکدام از کلیدها ارایهای دریافت میکند که نوع، مقدار پیشفرض، مقدار های مورد انتظار، اختیاری بودن و یا مجاز به خالی بودن برای هر فیلد را به صورت جداگانه مشخص میکند. خروجی این متد یک آرایه با همان کلید های داده شده و مقدار های اعتبار سنجی شده است.
نوع داده
با استفاده از کلید type
نوع داده دریافتی مشخص میشود.
$this->checkinputs(array(
'id' => array(
'type' => 'number',
),
));
انواع داده های اعتبارسنجی
در فریمورک برای انواع مختلف داده ها کلاس هایی برای اعتبار سنجی تعریف شده است که داده های زیر را بررسی میکنند.
- داده های رشته ای
- داده های عددی
- داده های boolean
- تاریخ
- شماره همراه
- ایمیل
- ip ورژن 4
- آدرس اینترنتی (url)
- فایل
- تصویر
- دادههایی از جنس کلاس های Model
- توابع
اگر داده های مورد اعتبارسجی نوعی بجز موارد بالا باشد برنامه نویس میتواند اعتبارسنج جدید ایجاد نمایید.
برای تعریف اعتبارسنج جدید باید کلاسی تعریف کرد که از اینترفیس packages\base\Validator\IValidator
implements شده باشد.
در اینترفیس IValidator متد getTypes()
برای مشخص کردن نام اعتبارسنج و متد validate()
برای عملیات اعتبارسنجی در نظرگرفته شده است.
برای مشاهده نمونه کدهای اعتبارسنج میتوانید به کلاس های اعتبارسنج تعریف شده فریمورک در این پوشه مراجعه کنید.
استفاده از اعتبارسنج جدید
زمان استفاده از اعتبارسنج جدید با استفاده از متد addValidator()
اعتبار سنج جدید معرفی میشود.
این متد باید قبل از فراخوانی متد checkinputs()
فراخوانی شود.
همچنین میتوان بجای فراخوانی متد addValidator()
در ایندکس type namespace کلاس اعتبارسنج داده شود.
درمثال زیر اعتبارسنج جدید (IBANValidator) معرفی شده است.
مقدار داده شده به ایندکس type نامی است که در متد getTypes()
کلاس اعتبارسنج تعیین شده است.
اعتبار سنج جدید نوشته شده
<?php
namespace packages\packagename\Validators;
use packages\packagename\Bank;
use packages\base\{InputValidationException, Validator\IValidator, db\DuplicateRecord};
class IBANValidator implements IValidator {
/**
* Get alias types
*
* @return string[]
*/
public function getTypes(): array {
return ['iban'];
}
/**
* Validate data to be a IBAN code.
*
* @throws packages\base\InputValidationException
* @param string $input
* @param array $rule
* @param mixed $data
* @return array
*/
public function validate(string $input, array $rule, $data): array {
if (!is_array($data)) {
throw new InputValidationException($input);
}
if (!$data) {
if (!isset($rule['empty']) or !$rule['empty']) {
throw new InputValidationException($input);
}
if (isset($rule['default'])) {
return $rule['default'];
}
}
foreach ($data as $key => $value) {
if (!isset($value["id"])) {
throw new InputValidationException($input . "[{$key}][id]");
}
if (!isset($value["account"])) {
throw new InputValidationException($input . "[{$key}][account]");
}
}
foreach ($data as $key => $value) {
foreach ($data as $key2 => $value2) {
if ($key != $key2 and $value["id"] == $value2["id"]) {
throw new DuplicateRecord($input . "[{$key}][id]");
}
}
$bank = Bank::byId($value["id"]);
if (!$bank) {
throw new InputValidationException($input . "[{$key}][id]");
}
$data[$key]["bank"] = $bank;
}
return $data;
}
}
1 مثال :
<?php
namespace packages\packagename\controllers;
use packages\base\{Controller, Response, Validator};
use packages\packagename\{Validators\IBANValidator};
class Banks extends Controller {
public function update(): Response {
Validator::addValidator(IBANValidator::class);
$inputs = $this->checkinputs(array(
"banks" => array(
"type" => 'iban',
),
));
foreach ($inputs["banks"] as $item) {
$item["bank"]->account = $item["account"];
$item["bank"]->save();
}
$this->response->setStatus(true);
return $this->response;
}
}
مثال 2 :
<?php
namespace packages\packagename\controllers;
use packages\base\{Controller, Response};
use packages\packagename\{Validators\IBANValidator};
class Banks extends Controller {
public function update(): Response {
$inputs = $this->checkinputs(array(
"banks" => array(
"type" => IBANValidator::class,
),
));
foreach ($inputs["banks"] as $item) {
$item["bank"]->account = $item["account"];
$item["bank"]->save();
}
$this->response->setStatus(true);
return $this->response;
}
}
خالی بودن
با استفاده از کلید empty
مجوز خالی بودن یا نبودن فیلد مشخص میشود. مقدار این کلید true یا false میتواند باشد.
مقدار true برای مجوز خالی بودن است. مقدار پیش فرض این کلید false میباشد.
اختیاری بودن
با استفاده از کلید optional
اجازه وجود و یا عدم وجود فیلد ورودی داده میشود .
مقدار پیش فرض این کلید false میباشد .
اگر این کلید false باشد و فیلد ورودی تعریف نشده باشد استثنا inputValidationException پرتاب میشود.
مقدار پیشفرض
با استفاده از کلید default
برای فیلد مورد نظر مقدار پیش فرض مشخص میشود.
اگر فیلد مجوز خالی بودن داشته باشد در صورت خالی بودن مقدار پیشفرض به عنوان value در نظر گرفته میشود.
مقادیر ثابت
اگر داده های دریافت شده دارای مقادیر مشخص و ثابتی هستند با استفاده از کلید values
میتوان مقادیر را مشخص کرد. این کلید برای input های checkbox, select,... که دارای تعداد value های ثابت هستند کاربرد بیشتری دارد.
داده های رشته ای
برای اعتبارسنجی داده های رشته ای از مقدار string
برای کلید type استفاده میشود.
برای دادههایی که از نوع string باشند میتوان کلید های زیر را برای مدیریت اعتبارسنجی آن تعریف کرد.
عبارت منظم
اگر فیلدی که دریافت میکنیم باید از قاعده ی مشخصی پیروی کند با استفاده از کلید regex
قاعده آن مشخص میشود.
مثال
<?php
namespace packages\packagename\controllers;
use packages\base\{Controller, Response};
class Order extends Controller {
public function validateDomain(): Response {
$this->checkinputs(array(
'domain' => array(
'type' => 'string',
'regex' => '/^([a-z0-9\\-]+\\.)+[a-z]{2,12}$/i', // Thrown InpuvalidationException in response if given domain is not match to this pattern
),
));
$this->response->setStatus(true);
return $this->response;
}
}
حذف whitespace
کلید trim
مانند متد trim() در php عمل میکند. این کلید میتواند مقادیر true یا false داشته باشد.
اگر trim مقدار دهی نشود در فریمورک بطور خودکار whitespace های داده حذف میشوند. برای جلوگیری از حذف whitespace ها باید مقدار trim را برابر با false قرار دهید.
کدها و تگ ها
اگر داده دریافتی کد باشد باید مقدار کلید htmlTags
را برابر با true قرار دهید در غیر اینصورت کد ها به صورت اسکی خود تبدیل میشوند.
بطور مثال اگر داده ورودی <h1> salam </h1>
باشد و مقدار htmlTags برابر true باشد داده ورودی به <h1>salam</h1>
تبدیل میشود.
مقدار پیشفرض این کلید false است.
رشته های چند خطی
اگر داده ورودی چند خطی باشد، اگر بخواهیم \n را از داده حذف کنیم باید به کلید multiLine
مقدار false میدهیم. اگر این کلید مقدار دهی نشود داده ها میتوانند چند خطی باشند.
مثال :
<?php
namespace packages\packagename\controllers;
use packages\blog\Comment;
use packages\base\{Controller, Response};
class Blog extends Controller {
public function comment(): Response {
$inputs = $this->checkinputs(array(
'name' => array(
'type' => 'string',
'multiLine' => false,
),
'message' => array(
'type' => 'string',
),
));
$model = new Comment();
$model->name = $inputs["name"];
$model->message = $inputs["message"];
$model->save();
$this->response->setStatus(true);
return $this->response;
}
}
داده های عددی
برای اعتبارسنجی داده های عددی از مقادیر
number
, int
, int8
, int16
, int32
, int64
, uint
, uint8
, uint16
, uint32
, uint64
, float
برای کلید type استفاده میشود.
هریک از موارد فوق، رنج عدد و همچنین مثبت و منفی بودن آن را مشخص میکند که قوانین آن در جدول زیر قابل مشاهده است.
نوع | رنج عددی مجاز |
---|---|
number | تمامی اعداد مثبت و منفی را میپذیرد |
int | تمامی اعداد مثبت و منفی را میپذیرد |
int8 | -128 تا 127 |
int16 | -32768 تا 32767 |
int32 | -2147483648 تا 2147483647 |
int64 | تمامی اعداد مثبت و منفی را میپذیرد |
uint8 | 0 تا 255 |
uint16 | 0 تا 65535 |
uint32 | 0 تا 4294967295 |
uint64 | تمامی اعداد مثبت را میپذیرد |
float | اعداد اعشاری |
علاوه بر قوانینی که برای هریک از مقادیر فوق وجود دارد میتوان بصورت دستی رنج عددی را مشخص کرد. این تنظیمات با استفاده از دو کلید min
و max
انجام پذیر میباشد.
نوع های float
و number
مقدار ورودی صفر را نمیپذیرند و استتثنا InputValidationException
پرتاب میشود . برای جلوگیری از این استثنا و پذیرش عدد صفر، باید کلید zero
با مقدار true تعریف کرد.
نکته 1 : اگر فیلد از نوع عددی باشد و empty آن برابر true باشد زمانی که مقدار این فیلد خالی باشد، مقدار null برای فیلد در نظر گرفته میشود.
نکته 2 : اگر عدد وارد شده خارج از رنج مجاز عددی باشد استثنا از جنس InputValidationException
با پیغام min-value
یا max-value
پرتاب میشود.
نکته 3 : اگر داده وارد شده عدد نباشد استثنا از جنس InputValidationException
با پیغام not-a-number
پرتاب میشود.
نکته 4 : اگر عدد وارد شده برابر با value تعریف شده نباشد استثنا از جنس InputValidationException
با پیغام not-defined-value
پرتاب میشود.
مثال 1
<?php
namespace packages\packagename\controllers;
use packages\cronjob\Task;
use packages\base\{Controller, Response};
class Cronjobs extends Controller {
public function store(): Response {
$inputs = $this->checkinputs(array(
'hour' => array(
'type' => 'number',
'values' => range(0, 24),
),
'minuts' => array(
'type' => 'string',
'min' => 0,
'max' => 60,
),
'command' => array(
'type' => 'string',
'htmlTags' => true,
),
'port' => array(
'type' => 'uint16',
'optional' => true,
'default' => 22,
),
));
$model = new Task();
$model->hour = $inputs["hour"];
$model->minuts = $inputs["minuts"];
$model->command = $inputs["command"];
$model->port = $inputs["port"];
$model->save();
$this->response->setStatus(true);
return $this->response;
}
}
مثال 2
<?php
namespace packages\packagename\controllers;
use packages\base\{Controller, Response, Options};
class Chats extends Controller {
public function update(): Response {
$inputs = $this->checkinputs(array(
'prev_messages_count' => array(
'type' => 'number',
'min' => -100,
'max' => 100,
'zero' => true,
),
));
Options::save("packages.packagename.chats.prev_messages_count", $inputs['prev_messages_count']);
$this->response->setStatus(true);
return $this->response;
}
}
**توجه :**در مثال فوق اگر index zero
.تعریف نشود عدد صفر را نمیپذیرد.
داده های boolean
برای اعتبارسنجی داده های boolean از کلید bool
برای مقدار type استفاده میشود.
مقادیر ورودی میتواند 0، 1، true و یا false باشد.
نکته : اگر مقدار empty برابر true باشد، درصورتی که فیلد خالی باشد مقدار آن برابر false در نظر گرفته میشود.
مثال
<?php
namespace packages\packagename\controllers;
use packages\base\{Controller, Response, Options};
class Chats extends Controller {
public function update(): Response {
$inputs = $this->checkinputs(array(
'status' => array(
'type' => 'bool',
),
));
Options::save("packages.packagename.chats.one-on-one-chats.status", $inputs['status']);
$this->response->setStatus(true);
return $this->response;
}
}
تاریخ
برای اعتبار سنجی تاریخ از مقدار date
برای کلید type استفاده میشود. این اعتبارسنج ورودی های تاریخ را به شکل YYYY/MM/DD و یا همراه با زمان بصورت YYYY/MM/DD HH:II:SS میپذیرد.
توجه : سال باید بصورت چهاررقمی وارد شود. روز و ماه میتواند یک یا دو رقمی وارد شود.
برای تاریخ کلید unix
تعریف شده است اگر این کلید تعریف نشود و یا مقدار false داشته باشد خروجی اعتبارسنج، تاریخ وارد شده میباشد و اگر مقدار true داشته باشد خروجی آن timestamp تاریخ وارد شده است.
مثال
<?php
namespace packages\packagename\controllers;
use packages\shop\category\Special;
use packages\base\{Controller, Response, Date, Options, InputValidationException};
class Shop extends Controller {
public function updateSpecialSells(): Response {
$inputs = $this->checkinputs(array(
'start_at' => array(
'type' => 'date',
'unix' => true,
'optional' => true,
'default' => Date::time(),
),
'end_at' => array(
'type' => 'date',
'unix' => true,
),
));
if ($inputs["start_at"] < Date::time()) {
throw new InputValidationException("start_at");
}
if ($inputs["end_at"] <= $inputs["start_at"]) {
throw new InputValidationException("end_at");
}
$models = Special::where("status", Special::ACTIVE)->get();
foreach ($models as $model) {
$model->start_at = $inputs["start_at"];
$model->end_at = $inputs["end_at"];
$model->save();
}
$this->response->setStatus(true);
return $this->response;
}
}
شماره همراه
برای اعتبارسنجی شماره موبایل از مقدار cellphone
برای کلید type استفاده میشود.
نکته: در حال حاضر فقط اعتبار سنجی شماره های همراه اپراتور های ایران در جالنو به صورت پیشفرض وجود دارد.
همچنین بطور پیش فرض کد ایران 98 تنظیم شده است. برای تغییر آن میتوانید تنظیم با نام packages.base.validators.default_cellphone_country_code
در فایل تنظیمات جالنو در مسیر packages/base/libraries/config/config.php
را به مقدار مورد نظر تغییر دهید..
اعتبارسنج ورودی ها را با فرمت های 9131101234 , 09131101234 , 989131101234 , 9809131101234 , +989131101234 و 98989131101234 را گرفته و پس از بررسی شماره وارد شده را بصورت 989131234567 برمیگرداند.
مثال
<?php
namespace packages\packagename\controllers;
use packages\packagename\User as Medel;
use packages\base\{Controller, Response, InputValidationException, Password, Session};
class Users extends Controller {
public function login(): Response {
$inputs = $this->checkinputs(array(
'username' => array(
'type' => 'cellphone',
),
'password' => array(),
));
$model = new Medel();
$model->where("username", $inputs["username"]);
$user = $user->getOne();
if (!$user) {
throw new InputValidationException("username");
}
if (!Password::verify($inputs["password"], $user->password)) {
throw new InputValidationException("password");
}
Session::set("loggin", true);
Session::set("userID", $user->id);
$this->response->setStatus(true);
return $this->response;
}
}
ایمیل
برای اعتبارسنجی ایمیل از مقدار email
برای کلید type استفاده میشود.
مثال
<?php
namespace packages\packagename\controllers;
use packages\packagename\User as Model;
use packages\base\{Controller, Response, InputValidationException, Session};
class Users extends Controller {
public function login(): Response {
$inputs = $this->checkinputs(array(
'username' => array(
'type' => 'email',
),
'password' => array(),
));
$model = new Model();
$model->where("username", $inputs["username"]);
$user = $user->getOne();
if (!$user) {
throw new InputValidationException("username");
}
if (!Password::verify($inputs["password"], $user->password)) {
throw new InputValidationException("password");
}
Session::set("loggin", true);
Session::set("userID", $user->id);
$this->response->setStatus(true);
return $this->response;
}
}
مثال 2
<?php
namespace packages\packagename\controllers;
use packages\packagename\User as Model;
use packages\base\{Controller, Response, InputValidationException, Session};
class Users extends Controller {
public function login(): Response {
$inputs = $this->checkinputs(array(
'username' => array(
'type' => ['email', 'cellphone'],
),
'password' => array(),
));
$model = new Model();
$model->where("username", $inputs["username"]);
$user = $user->getOne();
if (!$user) {
throw new InputValidationException("username");
}
if (!Password::verify($inputs["password"], $user->password)) {
throw new InputValidationException("password");
}
Session::set("loggin", true);
Session::set("userID", $user->id);
$this->response->setStatus(true);
return $this->response;
}
}
ip ورژن 4
برای اعتبارسنجی ip ورژن چهار از مقدار ip4
برای کلید type استفاده میشود.
مثال 1
<?php
namespace packages\packagename\controllers;
use packages\cronjob\Task;
use packages\base\{Controller, Response};
class Cronjobs extends Controller {
public function store(): Response {
$inputs = $this->checkinputs(array(
'hour' => array(
'type' => 'number',
'values' => range(0, 24),
),
'minuts' => array(
'type' => 'string',
'min' => 0,
'max' => 60,
),
'ip' => array(
'type' => 'ip4',
),
'command' => array(
'type' => 'string',
'htmlTags' => true,
),
'port' => array(
'type' => 'uint16',
'optional' => true,
'default' => 22,
),
));
$model = new Task();
$model->hour = $inputs["hour"];
$model->minuts = $inputs["minuts"];
$model->ip = $inputs["ip"];
$model->command = $inputs["command"];
$model->port = $inputs["port"];
$model->save();
$this->response->setStatus(true);
return $this->response;
}
}
آدرس اینترنتی (url)
برای اعتبارسنجی آدرسهای url از مقدار url
برای کلید type استفاده میشود.
اگر در url لازم به نوشتن پروتکل و یا بررسی نوع پروتکل باشیم با تعریف کلید protocols
با مقدار نوع پروتکل مورد نظر برای اعتبارسنجی وجود پروتکل را اجباری میکنیم.
برای protocols میتوان آرایه ای ازپروتکل ها را معرفی کرد.
مثال
<?php
namespace packages\packagename\controllers;
use packages\cronjob\Task;
use packages\base\{Controller, Response};
class Cronjobs extends Controller {
public function store(): Response {
$inputs = $this->checkinputs(array(
'hour' => array(
'type' => 'number',
'values' => range(0, 24),
),
'minuts' => array(
'type' => 'string',
'min' => 0,
'max' => 60,
),
'ip' => array(
'type' => 'ip4',
),
'hostname' => array(
'type' => 'url',
'protocols' => 'https',
),
'command' => array(
'type' => 'string',
'htmlTags' => true,
),
'port' => array(
'type' => 'uint16',
'optional' => true,
'default' => 22,
),
));
$model = new Task();
$model->hour = $inputs["hour"];
$model->minuts = $inputs["minuts"];
$model->command = $inputs["command"];
$model->ip = $inputs["ip"];
$model->hostname = $inputs["hostname"];
$model->port = $inputs["port"];
$model->save();
$this->response->setStatus(true);
return $this->response;
}
}
درمثال فوق اگر آدرس فاقد پروتکل یا پروتکل آن برابر https .نباشد استثنا پرتاب میشود.
فایل
برای اعتبارسنجی فایل ها از مقدار file
برای کلید type استفاده میشود. میتوان از کلید های زیر برای مدیریت اعتبارسنجی فایل ها استفاده کرد.
نوع فایل
از کلید extension
برای مشخص کردن نوع فایل استفاده میشود. مقدار آن میتواند بصورت ارایهای از پسوندهای مجاز باشد.
اگر extension مقداردهی نشود فایل با هر پسوندی پذیرفته میشود.
اندازه فایل
با استفاده از کلیدهای min-size
و max-size
میتوان برای فایل دریافتی محدودیت اندازه مشخص کرد.
ارسال چند فایل
اگر فایل دریافتی بصورت آرایهای از چند فایل باشد با قرار دادن مقدار true برای کلید multiple
به فریمورک اعلام میشود داده دریافتی شامل چند فایل میباشد.
تبدیل فایل دریافتی به شی کلاس File
با تعریف کلید obj
با مقدار true فایل دریافتی به یک شی از کلاس local\Tmp تبدیل میشود. اگر این کلید مقدار دهی نشود برابر false در نظر گرفته میشود، در اینصورت خروجی اعتبارسنج همانند خروجی $_FILES میباشد.
برای اطلاعات بیشتر در رابطه با فایل ها به صفحه فایل مراجعه کنید.
توجه : هنگام کار با فایل ها تگ form باید صفت enctype="multipart/form-data"
را داشته باشد.
1 مثال
<?php
namespace packages\packagename\controllers;
use packages\base\{Controller, Response, Packages};
class Files extends Controller {
public function upload(): Response {
$inputs = $this->checkInputs(array(
"files" => array(
"type" => "file",
"max-size" => 2097152 \\ Byte
"obj" => true,
"extension" => ["pdf", "word"],
"multiple" => true, // You can false or remove this line if you to accept only one file.
),
));
foreach ($inputs["files"] as $file) {
$name = $file->md5();
$localFile = Packages::package("packagename")->file("storage/private/files/{$name}.{$file->getExtension()}");
if ($localFile->exists()) {
continue;
}
$directory = $localFile->getDirectory();
if (!$directory->exists()) {
$directory->make(true);
}
$file->copyTo($localFile);
}
$this->response->setStatus(true);
return $this->response;
}
}
در مثال فوق فقط فایل های pdf یا word پ ذیرفته میشوند و حداکثر حجم آن 2 مگابایت میتواند باشد. و خروجی اعتبارسنج، شی از کلاس local\Tmp میباشد.
تصاویر
در فریمورک برای اعتبارسنجی تصاویر علاوه بر استفاده از اعتبارسنج فایل ها، بطور اختصاصی اعتبارسنجی برای تصاویر در نظر گرفته شده است که برای استفاده از آن، از مقدار image
برای کلید type استفاده میشود.
اعتبارسنج، تصاویر با پسوند های jpeg
, jpg
, png
, gif
, webp
را میپذیرد.
با استفاده از کلید extension
میتوان مشخص کرد تنها تعدادی از پسوند های فوق پذیرفته شود.
برای اعتبارسنجی تصاویر میتوان عواملی مانند سایز تصویر و طول و عرض آن را نیز اعتبارسنجی کرد. از کلید های زیر برای مدیریت اعتبارسنجی تصاویر استفاده میشود.
سایز تصویر
با استفاده از کلیدهای min-size
و max-size
میتوان برای تصویر دریافتی محدودیت اندازه مشخص کرد.