public function validate( $vars = null ) {
		$vars = $vars ?: $_POST;
		$this->values = [];
		$this->errors = [];
		$arrayInvalids = [];

		$cleaned = filter_var_array( $vars, $this->params );

		foreach ( $this->params as $name => $opt ) {
			$clean = isset( $vars[$name] ) ? $cleaned[$name] : null;

			if ( $clean === false &&
				$opt['filter'] !== \FILTER_VALIDATE_BOOLEAN
			) {
				$this->values[$name] = null;

			} elseif ( is_array( $clean ) &&
				( $opt['flags'] & \FILTER_REQUIRE_ARRAY ) &&
				$opt['filter'] !== \FILTER_VALIDATE_BOOLEAN
			) {
				// Strip invalid value markers from input array
				$this->values[$name] = [];
				foreach ( $clean as $key => $value ) {
					if ( $opt['filter'] !== \FILTER_VALIDATE_BOOLEAN &&
						$value !== false
					) {
						$this->values[$name][$key] = $value;

					} elseif ( $opt['filter'] === \FILTER_VALIDATE_BOOLEAN &&
						$value !== null
					) {
						$this->values[$name][$key] = $value;

					} else {
						// Keep track of invalid keys in case input was
						// required
						if ( !isset( $arrayInvalids[$name] ) ) {
							$arrayInvalids[$name] = [];
						}
						$arrayInvalids[$name][] = "{$name}[{$key}]";
					}
				}

			} else {
				$this->values[$name] = $clean;
			}

			if ( $opt['required'] && $this->values[$name] === null ) {
				$this->errors[] = $name;

			} elseif ( $opt['required'] && isset( $arrayInvalids[$name] ) ) {
				$this->errors = array_merge(
					$this->errors, $arrayInvalids[$name]
				);

			} elseif ( is_callable( $opt['validate'] ) &&
				call_user_func( $opt['validate'], $this->values[$name] ) === false
			) {
				$this->errors[] = $name;
				$this->values[$name] = null;
			}
		}

		$this->customValidationHook();

		return count( $this->errors ) === 0;
	}