acf_form_front::merge_form_metaprotectedACF 6.8.4

Folds metadata from _acf_form_meta[] inputs into the primary form configuration so the multi-acf_form()-in-one-outer-<form> pattern works.

Non-field request-level args (post_id, return, new_post, kses) stay "last wins" via the primary form.

Метод класса: acf_form_front{}

Хуки из метода

Возвращает

array. The primary form with allowed-key extras and OR'd title/content flags.

Использование

// protected - в коде основоного (родительского) или дочернего класса
$result = $this->merge_form_meta( $form ): array;
$form(массив) (обязательный)
The primary form configuration loaded from _acf_form.

Список изменений

С версии 6.8.4 Введена.

Код acf_form_front::merge_form_meta() ACF 6.8.8

protected function merge_form_meta( array $form ): array {
	// phpcs:disable WordPress.Security.NonceVerification.Missing -- Verified above in check_submit_form().
	if ( empty( $_POST['_acf_form_meta'] ) || ! is_array( $_POST['_acf_form_meta'] ) ) {
		return $form;
	}

	if ( empty( $_POST['_acf_render_id'] ) || ! is_scalar( $_POST['_acf_render_id'] ) ) {
		return $form;
	}
	$expected_render_id = sanitize_text_field( wp_unslash( $_POST['_acf_render_id'] ) );

	// wp_unslash only — sanitize_text_field would desync from the raw
	// $acf_form_value hashed render-side.
	$primary_form_value = ( isset( $_POST['_acf_form'] ) && is_scalar( $_POST['_acf_form'] ) )
		? (string) wp_unslash( $_POST['_acf_form'] ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Must match render-side bytes hashed into the form anchor.
		: '';

	$expected_anchor = hash( 'sha256', $primary_form_value );
	$primary_post_id = isset( $form['post_id'] ) ? (string) $form['post_id'] : '';

	/**
	 * Filters how long a `_acf_form_meta[]` payload remains valid after the page that
	 * emitted it was rendered.
	 *
	 * @since 6.8.4
	 *
	 * @param int $ttl Allowed age of a meta payload, in seconds.
	 */
	$ttl = (int) apply_filters( 'acf/form/meta_ttl', DAY_IN_SECONDS );
	$now = time();

	$valid_metas     = array();
	$primary_present = false;

	foreach ( $_POST['_acf_form_meta'] as $token ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Each $token is sanitized below before use.
		if ( ! is_scalar( $token ) ) {
			continue;
		}

		$decoded = json_decode( acf_decrypt( sanitize_text_field( $token ) ), true );
		if ( ! is_array( $decoded ) ) {
			continue;
		}

		if ( empty( $decoded['render_id'] ) || ! is_string( $decoded['render_id'] ) ) {
			continue;
		}

		if ( ! hash_equals( $expected_render_id, $decoded['render_id'] ) ) {
			continue;
		}

		if ( ! isset( $decoded['issued_at'] ) || ! is_numeric( $decoded['issued_at'] ) ) {
			continue;
		}
		if ( ( $now - (int) $decoded['issued_at'] ) >= $ttl ) {
			continue;
		}

		if ( ! empty( $decoded['form_anchor'] )
			&& is_string( $decoded['form_anchor'] )
			&& hash_equals( $expected_anchor, $decoded['form_anchor'] )
		) {
			$primary_present = true;
		}

		$valid_metas[] = $decoded;
	}

	if ( ! $primary_present ) {
		return $form;
	}

	$extra_keys = array();

	foreach ( $valid_metas as $decoded ) {
		$target_post_id = isset( $decoded['target_post_id'] ) ? (string) $decoded['target_post_id'] : '';
		if ( ! hash_equals( $primary_post_id, $target_post_id ) ) {
			continue;
		}

		if ( ! empty( $decoded['allowed_field_keys'] ) && is_array( $decoded['allowed_field_keys'] ) ) {
			foreach ( $decoded['allowed_field_keys'] as $key ) {
				if ( is_scalar( $key ) ) {
					$extra_keys[] = (string) $key;
				}
			}
		}

		if ( ! empty( $decoded['post_title'] ) ) {
			$form['post_title'] = true;
		}
		if ( ! empty( $decoded['post_content'] ) ) {
			$form['post_content'] = true;
		}
	}
	// phpcs:enable WordPress.Security.NonceVerification.Missing

	if ( $extra_keys ) {
		$form['_additional_allowed_field_keys'] = array_values( array_unique( $extra_keys ) );
	}

	return $form;
}