291 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			291 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
|  | <?php | ||
|  | /** | ||
|  |  * Functions related to registering and parsing blocks. | ||
|  |  * | ||
|  |  * @package WordPress | ||
|  |  * @subpackage Blocks | ||
|  |  * @since 5.0.0 | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * Registers a block type. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * | ||
|  |  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a | ||
|  |  *                                   complete WP_Block_Type instance. In case a WP_Block_Type | ||
|  |  *                                   is provided, the $args parameter will be ignored. | ||
|  |  * @param array                $args { | ||
|  |  *     Optional. Array of block type arguments. Any arguments may be defined, however the | ||
|  |  *     ones described below are supported by default. Default empty array. | ||
|  |  * | ||
|  |  *     @type callable $render_callback Callback used to render blocks of this block type. | ||
|  |  * } | ||
|  |  * @return WP_Block_Type|false The registered block type on success, or false on failure. | ||
|  |  */ | ||
|  | function register_block_type( $name, $args = array() ) { | ||
|  | 	return WP_Block_Type_Registry::get_instance()->register( $name, $args ); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Unregisters a block type. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * | ||
|  |  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a | ||
|  |  *                                   complete WP_Block_Type instance. | ||
|  |  * @return WP_Block_Type|false The unregistered block type on success, or false on failure. | ||
|  |  */ | ||
|  | function unregister_block_type( $name ) { | ||
|  | 	return WP_Block_Type_Registry::get_instance()->unregister( $name ); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Determine whether a post or content string has blocks. | ||
|  |  * | ||
|  |  * This test optimizes for performance rather than strict accuracy, detecting | ||
|  |  * the pattern of a block but not validating its structure. For strict accuracy, | ||
|  |  * you should use the block parser on post content. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * @see parse_blocks() | ||
|  |  * | ||
|  |  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post. | ||
|  |  * @return bool Whether the post has blocks. | ||
|  |  */ | ||
|  | function has_blocks( $post = null ) { | ||
|  | 	if ( ! is_string( $post ) ) { | ||
|  | 		$wp_post = get_post( $post ); | ||
|  | 		if ( $wp_post instanceof WP_Post ) { | ||
|  | 			$post = $wp_post->post_content; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return false !== strpos( (string) $post, '<!-- wp:' ); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Determine whether a $post or a string contains a specific block type. | ||
|  |  * | ||
|  |  * This test optimizes for performance rather than strict accuracy, detecting | ||
|  |  * the block type exists but not validating its structure. For strict accuracy, | ||
|  |  * you should use the block parser on post content. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * @see parse_blocks() | ||
|  |  * | ||
|  |  * @param string                  $block_type Full Block type to look for. | ||
|  |  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post. | ||
|  |  * @return bool Whether the post content contains the specified block. | ||
|  |  */ | ||
|  | function has_block( $block_type, $post = null ) { | ||
|  | 	if ( ! has_blocks( $post ) ) { | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ( ! is_string( $post ) ) { | ||
|  | 		$wp_post = get_post( $post ); | ||
|  | 		if ( $wp_post instanceof WP_Post ) { | ||
|  | 			$post = $wp_post->post_content; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return false !== strpos( $post, '<!-- wp:' . $block_type . ' ' ); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Returns an array of the names of all registered dynamic block types. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * | ||
|  |  * @return array Array of dynamic block names. | ||
|  |  */ | ||
|  | function get_dynamic_block_names() { | ||
|  | 	$dynamic_block_names = array(); | ||
|  | 
 | ||
|  | 	$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); | ||
|  | 	foreach ( $block_types as $block_type ) { | ||
|  | 		if ( $block_type->is_dynamic() ) { | ||
|  | 			$dynamic_block_names[] = $block_type->name; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return $dynamic_block_names; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Parses blocks out of a content string, and renders those appropriate for the excerpt. | ||
|  |  * | ||
|  |  * As the excerpt should be a small string of text relevant to the full post content, | ||
|  |  * this function renders the blocks that are most likely to contain such text. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * | ||
|  |  * @param string $content The content to parse. | ||
|  |  * @return string The parsed and filtered content. | ||
|  |  */ | ||
|  | function excerpt_remove_blocks( $content ) { | ||
|  | 	$allowed_blocks = array( | ||
|  | 		// Classic blocks have their blockName set to null.
 | ||
|  | 		null, | ||
|  | 		'core/columns', | ||
|  | 		'core/freeform', | ||
|  | 		'core/heading', | ||
|  | 		'core/html', | ||
|  | 		'core/list', | ||
|  | 		'core/media-text', | ||
|  | 		'core/paragraph', | ||
|  | 		'core/preformatted', | ||
|  | 		'core/pullquote', | ||
|  | 		'core/quote', | ||
|  | 		'core/table', | ||
|  | 		'core/verse', | ||
|  | 	); | ||
|  | 	/** | ||
|  | 	 * Filters the list of blocks that can contribute to the excerpt. | ||
|  | 	 * | ||
|  | 	 * If a dynamic block is added to this list, it must not generate another | ||
|  | 	 * excerpt, as this will cause an infinite loop to occur. | ||
|  | 	 * | ||
|  | 	 * @since 4.4.0 | ||
|  | 	 * | ||
|  | 	 * @param array $allowed_blocks The list of allowed blocks. | ||
|  | 	 */ | ||
|  | 	$allowed_blocks = apply_filters( 'excerpt_allowed_blocks', $allowed_blocks ); | ||
|  | 	$blocks = parse_blocks( $content ); | ||
|  | 	$output = ''; | ||
|  | 	 foreach ( $blocks as $block ) { | ||
|  | 		if ( in_array( $block['blockName'], $allowed_blocks, true ) ) { | ||
|  | 			$output .= render_block( $block ); | ||
|  | 		} | ||
|  | 	} | ||
|  | 	 return $output; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Renders a single block into a HTML string. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * | ||
|  |  * @global WP_Post $post The post to edit. | ||
|  |  * | ||
|  |  * @param array $block A single parsed block object. | ||
|  |  * @return string String of rendered HTML. | ||
|  |  */ | ||
|  | function render_block( $block ) { | ||
|  | 	global $post; | ||
|  | 
 | ||
|  | 	$block_type    = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); | ||
|  | 	$is_dynamic    = $block['blockName'] && null !== $block_type && $block_type->is_dynamic(); | ||
|  | 	$block_content = ''; | ||
|  | 	$index         = 0; | ||
|  | 
 | ||
|  | 	foreach ( $block['innerContent'] as $chunk ) { | ||
|  | 		$block_content .= is_string( $chunk ) ? $chunk : render_block( $block['innerBlocks'][ $index++ ] ); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ( ! is_array( $block['attrs'] ) ) { | ||
|  | 		$block['attrs'] = array(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ( $is_dynamic ) { | ||
|  | 		$global_post   = $post; | ||
|  | 		$block_content = $block_type->render( $block['attrs'], $block_content ); | ||
|  | 		$post          = $global_post; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Filters the content of a single block. | ||
|  | 	 * | ||
|  | 	 * @since 5.0.0 | ||
|  | 	 * | ||
|  | 	 * @param string $block_content The block content about to be appended. | ||
|  | 	 * @param array  $block         The full block, including name and attributes. | ||
|  | 	 */ | ||
|  | 	return apply_filters( 'render_block', $block_content, $block ); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Parses blocks out of a content string. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * | ||
|  |  * @param string $content Post content. | ||
|  |  * @return array Array of parsed block objects. | ||
|  |  */ | ||
|  | function parse_blocks( $content ) { | ||
|  | 	/** | ||
|  | 	 * Filter to allow plugins to replace the server-side block parser | ||
|  | 	 * | ||
|  | 	 * @since 5.0.0 | ||
|  | 	 * | ||
|  | 	 * @param string $parser_class Name of block parser class. | ||
|  | 	 */ | ||
|  | 	$parser_class = apply_filters( 'block_parser_class', 'WP_Block_Parser' ); | ||
|  | 
 | ||
|  | 	$parser = new $parser_class(); | ||
|  | 	return $parser->parse( $content ); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Parses dynamic blocks out of `post_content` and re-renders them. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * @global WP_Post $post The post to edit. | ||
|  |  * | ||
|  |  * @param  string $content Post content. | ||
|  |  * @return string Updated post content. | ||
|  |  */ | ||
|  | function do_blocks( $content ) { | ||
|  | 	// If there are blocks in this content, we shouldn't run wpautop() on it later.
 | ||
|  | 	$priority = has_filter( 'the_content', 'wpautop' ); | ||
|  | 	if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) { | ||
|  | 		remove_filter( 'the_content', 'wpautop', $priority ); | ||
|  | 		add_filter( 'the_content', '_restore_wpautop_hook', $priority + 1 ); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	$blocks = parse_blocks( $content ); | ||
|  | 	$output = ''; | ||
|  | 
 | ||
|  | 	foreach ( $blocks as $block ) { | ||
|  | 		$output .= render_block( $block ); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return $output; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * If do_blocks() needs to remove wp_autop() from the `the_content` filter, this re-adds it afterwards, | ||
|  |  * for subsequent `the_content` usage. | ||
|  |  * | ||
|  |  * @access private | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * | ||
|  |  * @param string $content The post content running through this filter. | ||
|  |  * @return string The unmodified content. | ||
|  |  */ | ||
|  | function _restore_wpautop_hook( $content ) { | ||
|  | 	global $wp_filter; | ||
|  | 	$current_priority = has_filter( 'the_content', '_restore_wpautop_hook' ); | ||
|  | 
 | ||
|  | 	add_filter( 'the_content', 'wpautop', $current_priority - 1 ); | ||
|  | 	remove_filter( 'the_content', '_restore_wpautop_hook', $current_priority ); | ||
|  | 
 | ||
|  | 	return $content; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Returns the current version of the block format that the content string is using. | ||
|  |  * | ||
|  |  * If the string doesn't contain blocks, it returns 0. | ||
|  |  * | ||
|  |  * @since 5.0.0 | ||
|  |  * | ||
|  |  * @param string $content Content to test. | ||
|  |  * @return int The block format version is 1 if the content contains one or more blocks, 0 otherwise. | ||
|  |  */ | ||
|  | function block_version( $content ) { | ||
|  | 	return has_blocks( $content ) ? 1 : 0; | ||
|  | } |