2019-01-13 13:06:32 +00:00
|
|
|
<?php
|
|
|
|
namespace AirQuality;
|
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* Makes validating user input easy.
|
|
|
|
*/
|
2019-01-13 13:06:32 +00:00
|
|
|
class Validator {
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* The data that we test against.
|
|
|
|
* @var array
|
|
|
|
*/
|
2019-01-13 13:06:32 +00:00
|
|
|
private $target_data;
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* An array of tests that we should run against the above target data.
|
|
|
|
* @var callable[]
|
|
|
|
*/
|
2019-01-13 13:06:32 +00:00
|
|
|
private $tests = [];
|
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* Creates a new validator instance.
|
|
|
|
* @param array $in_target_data The target data to test against.
|
|
|
|
*/
|
2019-01-13 13:06:32 +00:00
|
|
|
public function __construct($in_target_data) {
|
|
|
|
$this->target_data = $in_target_data;
|
|
|
|
}
|
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* Checks that the given key exists.
|
|
|
|
* @param string $key The key that should exist.
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function exists(string $key) {
|
2019-01-13 13:06:32 +00:00
|
|
|
$this->add_test(
|
|
|
|
$key,
|
|
|
|
function($data) { return isset($data); },
|
|
|
|
400,
|
|
|
|
"Error: The field $key wasn't found in your request."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* 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) {
|
2019-01-13 13:06:32 +00:00
|
|
|
$this->add_test(
|
|
|
|
$key,
|
|
|
|
function($data) { return is_numeric($data); },
|
|
|
|
400,
|
|
|
|
"Error: The field $key in your request isn't a number."
|
|
|
|
);
|
|
|
|
}
|
2019-06-13 20:53:26 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensures that the value associated with the given key is at most a given
|
|
|
|
* value.
|
|
|
|
* @param string $key The key to test the value of.
|
|
|
|
* @param int|float $max The maximum value allowed.
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function is_max(string $key, $max) {
|
|
|
|
$this->add_test(
|
|
|
|
$key,
|
|
|
|
function($data) use($max) { return floatval($data) <= $max; },
|
|
|
|
400,
|
|
|
|
"Error: The field $key in your request must be at most $max."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Ensures that the value associated with the given key is at least a given
|
|
|
|
* value.
|
|
|
|
* @param string $key The key to test the value of.
|
|
|
|
* @param int|float $min The minimum value allowed.
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function is_min(string $key, $min) {
|
|
|
|
$this->add_test(
|
|
|
|
$key,
|
|
|
|
function($data) use($min) { return floatval($data) >= $min; },
|
|
|
|
400,
|
|
|
|
"Error: The field $key in your request must be at least $min."
|
|
|
|
);
|
|
|
|
}
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* 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) {
|
2019-01-13 13:06:32 +00:00
|
|
|
$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."
|
|
|
|
);
|
|
|
|
}
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* 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) {
|
2019-01-13 13:06:32 +00:00
|
|
|
$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."
|
|
|
|
);
|
|
|
|
}
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2019-02-24 16:55:33 +00:00
|
|
|
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) . "."
|
|
|
|
);
|
|
|
|
}
|
2019-01-13 13:06:32 +00:00
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2019-01-13 13:06:32 +00:00
|
|
|
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
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2019-01-13 13:06:32 +00:00
|
|
|
public function matches_regex($key, $regex, $message) {
|
|
|
|
$this->add_test(
|
|
|
|
$key,
|
|
|
|
function($data) use($regex) {
|
|
|
|
return preg_match($regex, $data) === 1;
|
|
|
|
},
|
|
|
|
400,
|
|
|
|
$message
|
|
|
|
);
|
|
|
|
}
|
2019-06-13 20:53:26 +00:00
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* Ensures the value associated with the specified key is a valid datetime.
|
|
|
|
* @param string $key The key of the value to check.
|
|
|
|
* @return void
|
|
|
|
*/
|
2019-01-14 20:41:25 +00:00
|
|
|
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."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* 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) {
|
2019-01-13 13:06:32 +00:00
|
|
|
$new_test = [
|
|
|
|
"key" => $key,
|
|
|
|
"test" => $test,
|
|
|
|
"response_code" => $response_code,
|
|
|
|
"message" => $message
|
|
|
|
];
|
|
|
|
|
|
|
|
$this->tests[] = $new_test;
|
|
|
|
}
|
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* 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) {
|
2019-01-13 13:06:32 +00:00
|
|
|
http_response_code($code);
|
|
|
|
header("content-type: text/plain");
|
|
|
|
exit($message);
|
|
|
|
}
|
|
|
|
|
2019-06-13 20:46:30 +00:00
|
|
|
/**
|
|
|
|
* Runs all the tests added to the validator so far.
|
|
|
|
* Note that this won't return if a test fails.
|
|
|
|
* @return void
|
|
|
|
*/
|
2019-01-13 13:06:32 +00:00
|
|
|
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"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|