diff --git a/src/wp-includes/bits.php b/src/wp-includes/bits.php new file mode 100644 index 0000000000000..a4edd2dea0dc1 --- /dev/null +++ b/src/wp-includes/bits.php @@ -0,0 +1,125 @@ +` produces `array( 'year' => '2024' )`. + * @param mixed $context Context passed into the Bit from the surrounding system. + * This argument is not yet specified and will always be `null`. + * + * @return mixed An HTML template for rendering into the page, either as a plain string or in array form. + */ +function core_bit_hello_dolly( string $name, string $output_type, ?array $attributes, mixed $context ): mixed { + static $vocalists = array( + 'Julie Dahle Aagård', + 'Mindi Abair', + 'Lorez Alexandria', + 'Karrin Allyson', + 'Michelle Amato', + 'Ernestine Anderson', + 'Ivie Anderson', + ); + + $vocalist = $vocalists[ wp_rand( 0, count( $vocalists ) - 1 ) ]; + + switch ( $output_type ) { + case 'plaintext': + return $vocalist; + + case 'rich-text': + return array( + '%name>', + array( 'name' => $vocalist ), + ); + } +} + +class WP_Hello_Dolly_Bit extends BitProvider { + /** + * @inheritDoc + */ + public function handle_plaintext( string $bit_name, ?array $attributes ): string { + return self::get_random_vocalist(); + } + + /** + * @inheritDoc + */ + public function handle_richtext( string $bit_name, ?array $attributes ): WP_HTML_Template { + $name = self::get_random_vocalist(); + + return new WP_HTML_Template( + '%name>', + array( 'name' => $name ) + ); + } + + /** + * Returns the name of a random Jazz vocalist. + * + * @return string + */ + private function get_random_vocalist(): string { + static $vocalists = array( + 'Julie Dahle Aagård', + 'Mindi Abair', + 'Lorez Alexandria', + 'Karrin Allyson', + 'Michelle Amato', + 'Ernestine Anderson', + 'Ivie Anderson', + ); + + return $vocalists[ wp_rand( 0, count( $vocalists ) - 1 ) ]; + } +} + +abstract class BitProvider { + /** + * Performs initialize of Bit Provider during WordPress bootup. + */ + public function register(): void { + // This is optional. + }; + + /** + * Called to source content in plaintext contexts. For example, when a + * Bit is found within an HTML attribute, or inside a `TITLE` element. + * + * @see WP_HTML_Template + * + * @param string $bit_name Full name with namespace of matched Bit, e.g. "core/post-author". + * @param array|null $attributes Configured attributes found on Bit, if found, otherwise `null`. + * + * @return string Plaintext value for provided content. + */ + abstract public function handle_plaintext( string $bit_name, ?array $attributes ): string; + + /** + * Called to source content in Rich Text (HTML Markup) contexts. For example, + * when a Bit is found within the inner content of an HTML tag. + * + * @see WP_HTML_Template + * + * @param string $bit_name Full name with namespace of matched Bit, e.g. "core/post-author". + * @param array|null $attributes Configured attributes found on Bit, if found, otherwise `null`. + * @return WP_HTML_Template HTML template for provided content: a string or array. + */ + abstract public function handle_richtext( string $bit_name, ?array $attributes ): WP_HTML_Template; +} diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index decc4896c378a..032e08ffc7502 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -2057,6 +2057,8 @@ function render_block( $parsed_block ) { * } */ function parse_blocks( $content ) { + $content = wp_replace_bits( $content ); + /** * Filter to allow plugins to replace the server-side block parser. * diff --git a/src/wp-includes/html-api/class-wp-bits.php b/src/wp-includes/html-api/class-wp-bits.php new file mode 100644 index 0000000000000..a7652bf64da1c --- /dev/null +++ b/src/wp-includes/html-api/class-wp-bits.php @@ -0,0 +1,77 @@ +set_bookmark( 'here' ); + $here = $this->bookmarks['here']; + + $this->deferred_updates[] = new WP_HTML_Text_Replacement( + $here->start, + $here->length, + $new_content + ); + } + + public function flush_updates() { + foreach ( $this->deferred_updates as $update ) { + $this->lexical_updates[] = $update; + } + } + }; + + while ( $processor->next_token() ) { + switch ( $processor->get_token_type() ) { + case '#funky-comment': + $processor->replace_token( 'blarg' ); + break; + + case '#tag': + foreach ( $processor->get_attribute_names_with_prefix( '' ) ?? [] as $name ) { + $value = $processor->get_attribute( $name ); + if ( is_string( $value ) ) { + $new_value = preg_replace_callback( + '~/wp:([^>]+)>~', + static function ( $bit ) { + return 'blarg'; + }, + $value + ); + + if ( $new_value !== $value ) { + $processor->set_attribute( $name, $new_value ); + } + } + } + break; + + case '#comment': + if ( WP_HTML_Tag_Processor::COMMENT_AS_HTML_COMMENT !== $processor->get_comment_type() ) { + break; + } + + $text = $processor->get_modifiable_text(); + if ( 1 === preg_match( '~^/wp:([^>]+)>$~', $text ) ) { + $processor->replace_token( 'Blarg!' ); + break; + } + + $new_value = preg_replace_callback( + '~/wp:([^>]+)>~', + static function ( $bit ) { + return 'blarg'; + }, + $text + ); + + $processor->replace_token( "" ); + break; + } + } + $processor->flush_updates(); + $content = $processor->get_updated_html(); + + return $content; +} diff --git a/src/wp-includes/html-api/class-wp-html-template.php b/src/wp-includes/html-api/class-wp-html-template.php new file mode 100644 index 0000000000000..f181478e41e63 --- /dev/null +++ b/src/wp-includes/html-api/class-wp-html-template.php @@ -0,0 +1,48 @@ +Hello, World!
' ); + * + * // Placeholders for simple substitution. + * new WP_HTML_Template( 'Hello, %name>!
', array( 'name' => $name ) ); + * + * // Spread-operator for sets of attributes. + * new WP_HTML_Template( + * '', + * array( + * 'data-wp-text="context.buttonLabel"', + * 'data-wp-click="actions.clickButton", + * ) + * ); + * + * @param string $template Static HTML template, possibly including placeholders. + * @param array|null $data Optional. Data provided for placeholders, if any. + */ + public function __construct( string $template, array $data = null ) { + $this->template = $template; + $this->data = $data; + } +} diff --git a/src/wp-settings.php b/src/wp-settings.php index a682c2a50380a..2071922cd2da0 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -262,6 +262,8 @@ require ABSPATH . WPINC . '/html-api/class-wp-html-stack-event.php'; require ABSPATH . WPINC . '/html-api/class-wp-html-processor-state.php'; require ABSPATH . WPINC . '/html-api/class-wp-html-processor.php'; +require ABSPATH . WPINC . '/html-api/class-wp-html-template.php'; +require ABSPATH . WPINC . '/html-api/class-wp-bits.php'; require ABSPATH . WPINC . '/class-wp-http.php'; require ABSPATH . WPINC . '/class-wp-http-streams.php'; require ABSPATH . WPINC . '/class-wp-http-curl.php';