> */ private array $callbacks = array(); /** * Registers a callback for a field key or a field type. * * The callback receives the value and the field, and returns null to accept * it or a reason to refuse it. * * @param string $target Field key, or a field type such as 'email'. * @param callable $callback Check to run. */ public function add( string $target, callable $callback ): void { if ( '' === $target ) { return; } $this->callbacks[ $target ][] = $callback; } /** * Determines whether this rule applies to a field. * * Always true: the filter runs for every field even when nothing is * registered, which costs one `apply_filters()` and keeps the extension * point predictable. * * @param Field $field Field being validated. * * @return bool Always true. */ public function supports( Field $field ): bool { return true; } /** * Runs every registered check against the value. * * @param Field $field Field being validated. * @param mixed $value Sanitised value. * * @return ValidationError|null The first failure, or null when the value passes. */ public function validate( Field $field, mixed $value ): ?ValidationError { foreach ( $this->matching( $field ) as $callback ) { $error = $this->normalise( call_user_func( $callback, $value, $field ) ); if ( $error instanceof ValidationError ) { return $error; } } /** * Filters the validity of a single submitted value. * * Return a message, a `WP_Error` or a {@see ValidationError} to refuse * the value; return null to accept it. * * @since 1.0.0 * * @param mixed $error Reason to refuse the value, null to accept it. * @param mixed $value Sanitised value. * @param Field $field Field being validated. */ return $this->normalise( apply_filters( 'formpilot_validate_field', null, $value, $field ) ); } /** * Returns the callbacks registered for a field. * * @param Field $field Field being validated. * * @return array The callbacks. */ private function matching( Field $field ): array { return array_merge( $this->callbacks[ $field->field_key() ] ?? array(), $this->callbacks[ $field->type() ] ?? array() ); } /** * Turns whatever a callback returned into an error or nothing. * * @param mixed $result Value returned by a callback or filter. * * @return ValidationError|null The failure, or null when the value passes. */ private function normalise( mixed $result ): ?ValidationError { if ( $result instanceof ValidationError ) { return $result; } if ( $result instanceof WP_Error ) { return new ValidationError( '', (string) $result->get_error_code(), $result->get_error_message() ); } if ( is_string( $result ) && '' !== trim( $result ) ) { return new ValidationError( '', 'invalid', $result ); } return null; } }