$form a definition from FormDefinitions::normalize() * @param array $input usually the unslashed $_POST * @return array{values: array{name:string, email:string, topic:string, message:string, ack:bool}|null, errors: list, spam: bool} */ public static function validate(array $form, array $input): array { if (self::scalar($input[self::HONEYPOT] ?? '') !== '') { return ['values' => null, 'errors' => [], 'spam' => true]; } $errors = []; $name = !empty($form['show_name']) ? FormDefinitions::line(self::scalar($input['name'] ?? ''), self::NAME_MAX) : ''; $email = strtolower(trim(self::scalar($input['email'] ?? ''))); if ($email === '' || strlen($email) > self::EMAIL_MAX || preg_match('/[\x00-\x20\x7F<>,;"\\\\]/', $email) === 1 || filter_var($email, FILTER_VALIDATE_EMAIL) === false) { $errors[] = 'email'; $email = ''; } $topic = ''; $topics = (array) ($form['topics'] ?? []); if ($topics !== []) { $topic = FormDefinitions::line(self::scalar($input['topic'] ?? ''), FormDefinitions::TOPIC_MAX); if (!in_array($topic, $topics, true)) { $errors[] = 'topic'; $topic = ''; } } $message = FormDefinitions::text(self::scalar($input['message'] ?? ''), self::MESSAGE_MAX + 1); $len = mb_strlen($message); if ($len < self::MESSAGE_MIN || $len > self::MESSAGE_MAX || preg_match('/\p{L}/u', $message) !== 1) { $errors[] = 'message'; } $ack = !empty($input['ack']); if (!empty($form['ack']) && !$ack) { $errors[] = 'ack'; } if ($errors !== []) { return ['values' => null, 'errors' => $errors, 'spam' => false]; } return ['values' => ['name' => $name, 'email' => $email, 'topic' => $topic, 'message' => $message, 'ack' => $ack], 'errors' => [], 'spam' => false]; } /** A request value as a string; arrays and objects (a forged field name[]=x) become ''. */ private static function scalar(mixed $v): string { return is_string($v) ? $v : (is_int($v) ? (string) $v : ''); } }