$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(
'#()#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 `