التحقق من الصحة في PHP. جمال أم نودلز؟

باختياري أفضل مدقق PHP من بين عشرة أدوات شائعة ، واجهت معضلة. ما هو الأهم بالنسبة لي؟ الالتزام بجميع شرائع SOLID / OOP أم قابلية الاستخدام ووضوح الكود؟ ما الذي سيفضل مستخدمو إطار عمل Comet ؟ إذا كنت تعتقد أن السؤال أبعد ما يكون عن البساطة - مرحبًا بك تحت المقطع في رحلة طويلة عبر مقتطفات التعليمات البرمجية :)





بالإضافة إلى المخاوف المتعلقة بأداء واجهة برمجة تطبيقات REST والخدمات المصغرة ، أشعر بقلق شديد بشأن قابلية قراءة الكود الذي نكتبه يوميًا لحل مهام العمل ، بما في ذلك التحقق من صحة البيانات.



أريد عرض أجزاء من التعليمات البرمجية من معاييري الخاصة حتى تتمكن من تقدير مدى اتساع الأساليب لحل نفس المشكلة. لنتخيل أن مجموعة البيانات التالية قد وصلت إلينا:



$form = [
    'name'           => 'Elon Mask', 
    'name_wrong'     => 'Mask',
    'login'          => 'mask', 
    'login_wrong'    => 'm@sk', 
    'email'          => 'elon@tesla.com', 
    'email_wrong'    => 'elon@tesla_com', 
    'password'       => '1q!~|w2o<z', 
    'password_wrong' => '123456',
    'date'           => '2020-06-05 15:52:00',
    'date_wrong'     => '2020:06:05 15-52-00',
    'ipv4'           => '192.168.1.1',
    'ipv4_wrong'     => '402.28.6.12',
    'uuid'           => '70fcf623-6c4e-453b-826d-072c4862d133',
    'uuid_wrong'     => 'abcd-xyz-6c4e-453b-826d-072c4862d133',
    'extra'          => 'that field out of scope of validation',
    'empty'          => ''
];


هدفنا هو تشغيل هذه المصفوفة من خلال مجموعة من قواعد التحقق من الصحة ، مما ينتج عنه قائمة بجميع الحقول التي تحتوي على أخطاء ورسائل قياسية للتوضيح للمستخدم.



معيار الصناعة وأيقونة OOP الخالصة - Symfony بالطبع





use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Translation\MessageSelector;

$validator = Validation::createValidator();

$constraint = new Assert\Collection([    
    'name' => new Assert\Regex('/^[A-Za-z]+\s[A-Za-z]+$/u'),   	
    'login' => new Assert\Regex('/^[a-zA-Z0-9]-_+$/'),
    'email' => new Assert\Email(),
    'password' => [
        new Assert\NotBlank(),
        new Assert\Length(['max' => 64]),
        new Assert\Type(['type' => 'string'])
    ],
    'agreed' => new Assert\Type(['type' => 'boolean'])
]);

$violations = $validator->validate($form, $constraint);

$errors = [];
if (0 !== count($violations)) {
    foreach ($violations as $violation) {
        $errors[] = $violation->getPropertyPath() . ' : ' . $violation->getMessage();
    }
} 

return $errors;


كود الشفرة في نقية PHP



$errors = [];

if (!preg_match('/^[A-Za-z]+\s[A-Za-z]+$/u', $form['name']))
    $errors['name'] = 'should consist of two words!';
if (!preg_match('/^[A-Za-z]+\s[A-Za-z]+$/u', $form['name_wrong']))
    $errors['name_wrong'] = 'should consist of two words!';
if (!preg_match('/^[a-zA-Z0-9-_]+$/', $form['login']))
    $errors['login'] = 'should contain only alphanumeric!';
if (!preg_match('/^[a-zA-Z0-9]-_+$/', $form['login_wrong']))
    $errors['login_wrong'] = 'should contain only alphanumeric!';

if (filter_var($form['email'], FILTER_VALIDATE_EMAIL) != $form['email'])
    $errors['email'] = 'provide correct email!';
if (filter_var($form['email_wrong'], FILTER_VALIDATE_EMAIL) != $form['email_wrong'])
    $errors['email_wrong'] = 'provide correct email!';

if (!is_string($form['password']) ||
    $form['password'] == '' ||
    strlen($form['password']) < 8 ||
    strlen($form['password']) > 64 
)
    $errors['password'] = 'provide correct password!';

if (!is_string($form['password_wrong']) ||
    $form['password_wrong'] == '' ||
    strlen($form['password_wrong']) < 8 ||
    strlen($form['password_wrong']) > 64 
)
    $errors['password_wrong'] = 'provide correct password!';

if (!preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/', $form['date']))
    $errors['date'] = 'provide correct date!';
if (!preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/', $form['date_wrong']))
    $errors['date_wrong'] = 'provide correct date!';

if (filter_var($form['ipv4'], FILTER_VALIDATE_IP) != $form['ipv4'])
    $errors['ipv4'] = 'provide correct ip4!';
if (filter_var($form['ipv4_wrong'], FILTER_VALIDATE_IP) != $form['ipv4_wrong'])
    $errors['ipv4_wrong'] = 'provide correct ip4!';

if (!preg_match('/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i', $form['uuid']))
    $errors['uuid'] = 'provide correct uuid!';
if (!preg_match('/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i', $form['uuid_wrong']))
    $errors['uuid_wrong'] = 'provide correct uuid!';

if (!isset($form['agreed']) || !is_bool($form['agreed']) || $form['agreed'] != true)
    $errors['agreed'] = 'you should agree with terms!';

return $errors;


يعتمد الحل على إحدى مكتبات التحقق من الاحترام الأكثر شيوعًا



use Respect\Validation\Validator as v;
use Respect\Validation\Factory;

Factory::setDefaultInstance(
    (new Factory())
        ->withRuleNamespace('Validation')
        ->withExceptionNamespace('Validation')
);

$messages = [];

try {
    v::attribute('name', v::RespectRule())
        ->attribute('name_wrong', v::RespectRule())
        ->attribute('login', v::alnum('-_'))
        ->attribute('login_wrong', v::alnum('-_'))
        ->attribute('email', v::email())
        ->attribute('email_wrong', v::email())
        ->attribute('password', v::notEmpty()->stringType()->length(null, 64))
        ->attribute('password_wrong', v::notEmpty()->stringType()->length(null, 64))
        ->attribute('date', v::date())
        ->attribute('date_wrong', v::date())
        ->attribute('ipv4', v::ipv4())
        ->attribute('ipv4_wrong', v::ipv4())
        ->attribute('uuid', v::uuid())
        ->attribute('uuid_wrong', v::uuid())
        ->attribute('agreed', v::trueVal())
        ->assert((object) $form);
} catch (\Exception $ex) {
    $messages = $ex->getMessages();
}

return $messages;


معروف أيضًا باسم Valitron



use Valitron\Validator;

Validator::addRule('uuid', function($field, $value) {
    return (bool) preg_match('/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i', $value);
}, 'UUID should confirm RFC style!');

$rules = [
    'required'  => [ 'login', 'agreed' ],
    'regex'     => [ ['name', '/^[A-Za-z]+\s[A-Za-z]+$/'] ],
    'lengthMin' => [ [ 'password', '8'], [ 'password_wrong', '8'] ],
    'lengthMax' => [ [ 'password', '64'], [ 'password_wrong', '64'] ],
    'slug'      => [ 'login', 'login_wrong' ],
    'email'     => [ 'email', 'email_wrong' ],
    'date'      => [ 'date', 'date_wrong' ],
    'ipv4'      => [ 'ipv4', 'ipv4_wrong' ],
    'uuid'      => [ 'uuid', 'uuid_wrong' ],
    'accepted'  => 'agreed'
];

$validator = new Validator($form);
$validator->rules($rules);
$validator->rule('accepted', 'agreed')->message('You should set {field} value!');
$validator->validate();

return $validator->errors());


جميل سيريوس




$validator = new \Sirius\Validation\Validator;

$validator
    ->add('name', 'required | \Validation\SiriusRule')
    ->add('login', 'required | alphanumhyphen', null, 'Only latin chars, underscores and dashes please.')
    ->add('email', 'required | email', null, 'Give correct email please.')
    ->add('password', 'required | maxlength(64)', null, 'Wrong password.')
    ->add('agreed', 'required | equal(true)', null, 'Where is your agreement?');

$validator->validate($form);

$errors = [];
foreach ($validator->getMessages() as $attribute => $messages) {
    foreach ($messages as $message) {
        $errors[] = $attribute . ' : '. $message->getTemplate();
    }
}

return $errors;


وهذه هي طريقة التحقق من صحتها في Laravel



use Illuminate\Validation\Factory as ValidatorFactory;
use Illuminate\Translation\Translator;
use Illuminate\Translation\ArrayLoader;
use Symfony\Component\Translation\MessageSelector;
use Illuminate\Support\Facades\Validator as FacadeValidator;

$rules = array(
    'name' => ['regex:/^[A-Za-z]+\s[A-Za-z]+$/u'],
    'name_wrong' => ['regex:/^[A-Za-z]+\s[A-Za-z]+$/u'],
    'login' => ['required', 'alpha_num'],
    'login_wrong' => ['required', 'alpha_num'],
    'email' => ['email'],
    'email_wrong' => ['email'],
    'password' => ['required', 'min:8', 'max:64'],
    'password_wrong' => ['required', 'min:8', 'max:64'],
    'date' => ['date'],
    'date_wrong' => ['date'],
    'ipv4' => ['ipv4'],
    'ipv4_wrong' => ['ipv4'],
    'uuid' => ['uuid'],
    'uuid_wrong' => ['uuid'],
    'agreed' => ['required', 'boolean']
);

$messages = [
    'name_wrong.regex' => 'Username is required.',
    'password_wrong.required' => 'Password is required.',
    'password_wrong.max' => 'Password must be no more than :max characters.',
    'email_wrong.email' => 'Email is required.',
    'login_wrong.required' => 'Login is required.',
    'login_wrong.alpha_num' => 'Login must consist of alfa numeric chars.',
    'agreed.required' => 'Confirm radio box required.',
);

$loader = new ArrayLoader();
$translator = new Translator($loader, 'en');
$validatorFactory = new ValidatorFactory($translator);

$validator = $validatorFactory->make($form, $rules, $messages);

return $validator->messages();


التحقق من صحة Rakit الماس غير متوقع



$validator = new \Rakit\Validation\Validator;
$validator->addValidator('uuid', new \Validation\RakitRule);

$validation = $validator->make($form, [
    'name'           => 'regex:/^[A-Za-z]+\s[A-Za-z]+$/u',
    'name_wrong'     => 'regex:/^[A-Za-z]+\s[A-Za-z]+$/u',
    'email'          => 'email',
    'email_wrong'    => 'email',
    'password'       => 'required|min:8|max:64',
    'password_wrong' => 'required|min:8|max:64',
    'login'          => 'alpha_dash',
    'login_wrong'    => 'alpha_dash',
    'date'           => 'date:Y-m-d H:i:s',
    'date_wrong'     => 'date:Y-m-d H:i:s',
    'ipv4'           => 'ipv4',
    'ipv4_wrong'     => 'ipv4',
    'uuid'           => 'uuid',
    'uuid_wrong'     => 'uuid',
    'agreed'         => 'required|accepted'
]); 	

$validation->setMessages([
    'uuid'     => 'UUID should confirm RFC rules!',
    'required' => ':attribute is required!',
    // etc
]);

$validation->validate();

return $validation->errors()->toArray();


وماذا في ذلك؟ أي مثال على الكود هو الأكثر وصفًا واصطلاحًا وصحيحًا و "صحيحًا" بشكل عام؟ خياري الشخصي هو في أرصفة المذنب: github.com/gotzmann/comet أخيرًا



، استطلاع صغير للأجيال القادمة.



All Articles