Validierung in PHP. Schönheit oder Nudeln?

Bei der Auswahl des besten PHP-Validators aus zehn populären Validatoren stand ich vor einem Dilemma. Was ist mir wichtiger? Einhaltung aller SOLID / OOP-Kanons oder Benutzerfreundlichkeit und Klarheit des Codes? Was bevorzugen Benutzer des Comet- Frameworks ? Wenn Sie der Meinung sind, dass die Frage alles andere als einfach ist - willkommen auf einer langen Reise durch Codefragmente :)





Neben Bedenken hinsichtlich der Leistung der REST-API und der Microservices bin ich sehr besorgt über die Lesbarkeit des Codes, den wir täglich eingeben, um Arbeitsprobleme einschließlich der Datenvalidierung zu lösen.



Ich möchte Codeteile aus meinen eigenen Benchmarks anzeigen, damit Sie die Breite der Lösungsansätze für dasselbe Problem einschätzen können. Stellen wir uns vor, der folgende Datensatz ist bei uns angekommen:



$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'          => ''
];


Unser Ziel ist es, dieses Array durch eine Reihe von Validierungsregeln zu führen, was zu einer Liste aller Felder mit Fehlern und Standardnachrichten führt, die dem Benutzer demonstriert werden sollen.



Industriestandard und Symbol für reines OOP - Symfony natürlich





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;


Rasiermesser-Code in reinem 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;


Lösung basierend auf einer der beliebtesten Respect Validation-Bibliotheken



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;


Auch bekannter Name: 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());


Schöner Sirius




$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;


Und so validieren sie in 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 Validation Unerwarteter Diamant



$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();


Na und? Welches Codebeispiel ist das aussagekräftigste, idiomatischste, korrekteste und allgemein „korrekteste“? Meine persönliche Wahl ist an den Docks auf Comet: github.com/gotzmann/comet Zum Schluss



noch eine kleine Umfrage für die Nachwelt.



All Articles