Air-Quality-Web/logic/Validator.php

206 lines
5.6 KiB
PHP

<?php
namespace AirQuality;
/**
* Makes validating user input easy.
*/
class Validator {
/**
* The data that we test against.
* @var array
*/
private $target_data;
/**
* An array of tests that we should run against the above target data.
* @var callable[]
*/
private $tests = [];
/**
* Creates a new validator instance.
* @param array $in_target_data The target data to test against.
*/
public function __construct($in_target_data) {
$this->target_data = $in_target_data;
}
/**
* Checks that the given key exists.
* @param string $key The key that should exist.
* @return void
*/
public function exists(string $key) {
$this->add_test(
$key,
function($data) { return isset($data); },
400,
"Error: The field $key wasn't found in your request."
);
}
/**
* Ensures that the value associated with the specified key is a number.
* @param string $key The key to check the value of.
* @return void
*/
public function is_numberish(string $key) {
$this->add_test(
$key,
function($data) { return is_numeric($data); },
400,
"Error: The field $key in your request isn't a number."
);
}
/**
* Checks that the value associated with the given key is a least a given
* number of characters long.
* This test is unicode aware.
* @param string $key The key to check the value of.
* @param int $min_length The minimum length required.
* @return void
*/
public function is_min_length(string $key, int $min_length) {
$this->add_test(
$key,
function($data) use ($min_length) { return mb_strlen($data) >= $min_length; },
400,
"Error: The field $key is too short - it must be a minimum of $min_length characters."
);
}
/**
* Checks that the value associated with the given key is at most a given
* number of characters long.
* This test is unicode aware.
* @param string $key The key to check the value of.
* @param int $max_length The maximum length allowed.
* @return void
*/
public function is_max_length(string $key, int $max_length) {
$this->add_test(
$key,
function($data) use ($max_length) { return mb_strlen($data) <= $max_length; },
400,
"Error: The field $key is too long - it must be a maximum of $max_length characters."
);
}
/**
* Ensures that the value associated with the given key is one of a
* predefined set of values.
* @param string $key The key to check the associated value of.
* @param array $values The array of values to test against.
* @param int $error_code The HTTP response code to return if the test fails.
*/
public function is_preset_value(string $key, array $values, int $error_code) {
$this->add_test(
$key,
function($data) use ($values) {
return in_array($data, $values);
},
$error_code,
"Error: The value for the field $key is not valid. Valid values: " . implode(", ", $values) . "."
);
}
/**
* Ensures that 2 keys have the same value.
* @param string $key_a The first key.
* @param string $key_b The second key.
* @param string $message The message to return if the test fails.
* @return void
*/
public function are_equal($key_a, $key_b, $message) {
$key_b_data = $this->target_data[$key_b];
$this->add_test(
$key_a,
function($data) use($key_b_data) {
return $data === $key_b_data;
},
400,
$message
);
}
/**
* Ensures the value attached to a given key matches the specified regular
* expression.
* @param string $key The key to check the value of.
* @param string $regex The regular expression to test the value with.
* @param string $message Thee message to return if the test fails.
* @return void
*/
public function matches_regex($key, $regex, $message) {
$this->add_test(
$key,
function($data) use($regex) {
return preg_match($regex, $data) === 1;
},
400,
$message
);
}
/**
* Ensures the value associated with the specified key is a valid datetime.
* @param string $key The key of the value to check.
* @return void
*/
public function is_datetime($key) {
$this->add_test(
$key,
function($data) {
return strtotime($data) !== false;
},
400,
"The datetime provided in the '$key' parameter is invalid."
);
}
/**
* Add a custom test directly.
* It's suggested that you don't call this directly - insteaad edit this
* class' code to allow everyone to benefit from the improvement :-)
* @param string $key The key to test the value of.
* @param callable $test The test to run.
* @param int $response_code The HTTP response code to return if the test fails.
* @param string $message The message to send if the test fails.
*/
public function add_test(string $key, callable $test, int $response_code, string $message) {
$new_test = [
"key" => $key,
"test" => $test,
"response_code" => $response_code,
"message" => $message
];
$this->tests[] = $new_test;
}
/**
* Sends an error to the client and then exits.
* Don't put code after calling this method - it won't get executed!
* @param int $code The HTTP response code to send.
* @param string $message The plain-text message to send.
*/
protected function send_error(int $code, string $message) {
http_response_code($code);
header("content-type: text/plain");
exit($message);
}
/**
* Runs all the tests added to the validator so far.
* Note that this won't return if a test fails.
* @return void
*/
public function run() {
foreach($this->tests as $test) {
if(!isset($this->target_data[$test["key"]]) || !$test["test"]($this->target_data[$test["key"]])) {
$this->send_error($test["response_code"], $test["message"]);
}
}
}
}