$values Field name => value. * @return string */ public static function refill( string $html, array $values ): string { if ( ! $values ) { return $html; } $processor = new \WP_HTML_Tag_Processor( $html ); $select = ''; $textareas = array(); while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) { $tag = $processor->get_tag(); if ( $processor->is_tag_closer() ) { $select = 'SELECT' === $tag ? '' : $select; continue; } $name = (string) $processor->get_attribute( 'name' ); if ( 'SELECT' === $tag ) { $select = $name; } elseif ( 'OPTION' === $tag && isset( $values[ $select ] ) ) { $processor->set_attribute( 'selected', (string) $processor->get_attribute( 'value' ) === $values[ $select ] ); } elseif ( 'TEXTAREA' === $tag && isset( $values[ $name ] ) ) { // Filled after this pass: how set_modifiable_text() escapes // differs between the WordPress versions this plugin supports. $processor->set_attribute( 'data-airo-wp-refill', (string) count( $textareas ) ); $textareas[] = $values[ $name ]; } elseif ( 'INPUT' === $tag && '' !== $name && null === $processor->get_attribute( 'data-airo-wp-system' ) ) { $type = strtolower( (string) $processor->get_attribute( 'type' ) ); if ( in_array( $type, array( 'checkbox', 'radio' ), true ) ) { $own = $processor->get_attribute( 'value' ); $processor->set_attribute( 'checked', isset( $values[ $name ] ) && ( is_string( $own ) ? $own : 'on' ) === $values[ $name ] ); } elseif ( isset( $values[ $name ] ) && ! in_array( $type, array( 'hidden', 'password', 'file', 'submit', 'button', 'image', 'reset' ), true ) ) { $processor->set_attribute( 'value', $values[ $name ] ); } } } return self::fill_textareas( $processor->get_updated_html(), $textareas ); } /** * Write answers into the textareas refill() tagged. * * Escaping with esc_textarea() makes `` and every `&` inert. A leading * newline is doubled, because HTML drops the first one after the tag. * * @param string $html HTML with `data-airo-wp-refill` markers. * @param string[] $textareas Answers by marker index. * @return string */ private static function fill_textareas( string $html, array $textareas ): string { if ( ! $textareas ) { return $html; } $filled = preg_replace_callback( '#(]*?)\s+data-airo-wp-refill="(\d+)"([^>]*>).*?()#is', static function ( $matches ) use ( $textareas ) { $value = $textareas[ (int) $matches[2] ] ?? ''; $lead = 1 === strspn( $value, "\n\r", 0, 1 ) ? "\n" : ''; return $matches[1] . $matches[3] . $lead . esc_textarea( $value ) . $matches[4]; }, $html ); // On a PCRE failure (backtrack limit on a huge form), show the form // without the textarea answers rather than no form at all. return null === $filled ? $html : $filled; } /** * The visible label of a field in rendered form HTML, or ''. * * Finds the control named `$name`, then the `