-
Notifications
You must be signed in to change notification settings - Fork 1
Enforcing validation on user input
Adding attributes like required
, min
or type=email
to HTML inputs automatically enables client-side validation in the browser, but to enforce validation on the server, this library has to be triggered in your PHP code, so any validation errors can be handled, and appropriate error messages can be added to the invalid form elements.
There's no set method for adding validation error messages to your HTML, so this library provides a mechanism to apply your own validation logic in a generic way to all form submissions.
With an HTMLDocument
representing the current page (where the form is), and a reference to the <form>
element you wish to validate, call the validate()
function of a Validator
object, passing in the reference to the Form and the submitted user input to check against.
The validate()
function will throw a ValidationException
if there are any errors with the form's validation. You can catch
this exception and output appropriate error messages on the invalid fields.
If any of the submitted user input is invalid according to the provided <form>
, a ValidationException
will be thrown. However, the exception only means that there is invalid data - the Validator
object will give you a list of all the fields that are invalid, with the appropriate error messages for each field.
The general approach to handling invalid fields is to loop over the array of errors within the catch
block. The Validator::getErrorList()
function returns an associative array of all errors, where the key of the array is the name
of the form input, and the value of the array is the error message string.
function example(HTMLDocument $document, Input $input):void {
// First, obtain a reference to the form we wish to validate.
$form = $document->querySelector("#example-form");
try {
// Then "try" to validate the form with the submitted user input:
$validator->validate($form, $input);
}
catch(ValidationException) {
// If there are any validation errors, we can iterate over them to display
// to the page, and return early as to not action the user input.
foreach($validator->getLastErrorList() as $name => $message) {
// Here we can add an attribute to the parent of the input, for displaying
// the error message using CSS, for example.
$errorElement = $form->querySelector("[name=$name]");
$errorElement->parentNode->dataset->validationError = $message;
}
// Return early so user input isn't used when there are validation errors.
return;
}
// Finally, if the input contains no validation errors, continue as usual.
}
In the above example, $document
is assumed to be constructed as a Gt\Dom\HTMLDocument
from PHP.Gt/Dom. In WebEngine applications, this is provided automatically. An example for both WebEngine and plain PHP are available below.
The first parameter of the validate
function is the $form
element. It must be a Gt\Dom\Element
with an elementType
of HTMLFormElement
, representing the <form>
in the Document that you wish to validate.
The second parameter of the validate
function is the user's input. This can be the value of $_POST
, but most frameworks provide an object oriented alternative to the $_POST
superglobal. Any form of iterable<string, string>
, or an associative array, can be used to pass in the user input to the validate function, as long as the key corresponds to the name
of the input field, and the value corresponds to the value
of the input field.
$html = <<<HTML
<!doctype html>
<style>
[data-validation-error] {
border-left: 2px solid red;
}
[data-validation-error]::before {
content: attr(data-validation-error);
color: red;
font-weight: bold;
}
label {
display: block;
padding: 1rem;
}
label span {
display: block;
}
</style>
<form method="post">
<label>
<span>Your name</span>
<input name="name" required />
</label>
<label>
<span>Credit Card number</span>
<input name="credit-card" pattern="\d{16}" required />
</label>
<label>
<span>Expiry month</span>
<input name="expiry-month" type="number" min="1" max="12" required />
</label>
<label>
<span>Expiry year</span>
<input name="expiry-year" type="number" min="18" max="26" required />
</label>
<label>
<span>amount</span>
<input name="amount" type="number" value="100.50" readonly required />
</label>
<button name="do" value="buy">Buy the thing!</button>
</form>
HTML;
function example(HTMLDocument $document, array $input) {
$validator = new Validator();
$form = $document->forms[0];
try {
$validator->validate($form, $input);
}
catch(ValidationException $exception) {
foreach($validator->getErrorList() as $name => $message) {
$errorElement = $form->querySelector("[name=$name]");
$errorElement->parentNode->dataset->validationError = $message;
}
return;
}
echo "Payment succeeded!";
exit;
}
$document = new HTMLDocument($html);
if(isset($_POST["do"]) && $_POST["do"] === "buy") {
example($document, $_POST);
}
echo $document;
In WebEngine applications, the HTMLDocument
, Validator
and Input
are all available via dependency injection, using the do
function's parameters.
example.html:
<!doctype html>
<style>
[data-validation-error] {
border-left: 2px solid red;
}
[data-validation-error]::before {
content: attr(data-validation-error);
color: red;
font-weight: bold;
}
label {
display: block;
padding: 1rem;
}
label span {
display: block;
}
</style>
<form method="post">
<label>
<span>Your name</span>
<input name="name" required />
</label>
<label>
<span>Credit Card number</span>
<input name="credit-card" pattern="\d{16}" required />
</label>
<label>
<span>Expiry month</span>
<input name="expiry-month" type="number" min="1" max="12" required />
</label>
<label>
<span>Expiry year</span>
<input name="expiry-year" type="number" min="18" max="26" required />
</label>
<label>
<span>amount</span>
<input name="amount" type="number" value="100.50" readonly required />
</label>
<button name="do" value="buy">Buy the thing!</button>
</form>
use Gt\Dom\HTMLDocument;
use Gt\DomValidation\Validator;
use Gt\Input\Input;
use Gt\DomValidation\ValidationException;
function do_submit(HTMLDocument $document, Validator $validator, Input $input):void {
// First, obtain a reference to the form we wish to validate.
$form = $document->querySelector("#example-form");
try {
// Then "try" to validate the form with the submitted user input:
$validator->validate($form, $input);
}
catch(ValidationException) {
// If there are any validation errors, we can iterate over them to display
// to the page, and return early as to not action the user input.
foreach($validator->getLastErrorList() as $name => $message) {
// Here we can add an attribute to the parent of the input, for displaying
// the error message using CSS, for example.
$errorElement = $form->querySelector("[name=$name]");
$errorElement->parentNode->dataset->validationError = $message;
}
// Return early so user input isn't used when there are validation errors.
return;
}
// Finally, if the input contains no validation errors, continue as usual.
}
Next, learn about extending custom rules.
DomTemplate is a separately maintained component of PHP.Gt WebEngine.