Class for efficiently scanning through block structure in a document without parsing the entire block tree and JSON attributes into memory.
Description
<h2>Overview</h2> This class is designed to help analyze and modify block structure in a streaming fashion and to bridge the gap between parsed block trees and the text representing them. Use-cases for this class include but are not limited to: <ul> <li>Counting block types in a document.</li> <li>Queuing stylesheets based on the presence of various block types.</li> <li>Modifying blocks of a given type, i.e. migrations, updates, and styling.</li> <li>Searching for content of specific kinds, e.g. checking for blocks with certain theme support attributes, or block bindings.</li> <li>Adding CSS class names to the element wrapping a block’s inner blocks.</li> </ul> <blockquote> <em>Note!</em> If a fully-parsed block tree of a document is necessary, including all the parsed JSON attributes, nested blocks, and HTML, consider using parse_blocks() instead which will parse the document in one swift pass. </blockquote> For typical usage, jump first to the methods self::next_block(), self::next_delimiter(), or self::next_token(). <h3>Values</h3> As a lower-level interface than parse_blocks() this class follows different performance-focused values: <ul> <li>Minimize allocations so that documents of any size may be processed on a fixed or marginal amount of memory.</li> <li>Make hidden costs explicit so that calling code only has to pay the performance penalty for features it needs.</li> <li>Operate with a streaming and re-entrant design to make it possible to operate on chunks of a document and to resume after pausing.</li> </ul> This means that some operations might appear more cumbersome than one might expect. This design tradeoff opens up opportunity to wrap this in a convenience class to add higher-level functionality. <h2>Concepts</h2> All text documents can be considered a block document containing a combination of “freeform HTML” and explicit block structure. Block structure forms through special HTML comments called <em>delimiters</em> which include a block type and, optionally, block attributes encoded as a JSON object payload. This processor is designed to scan through a block document from delimiter to delimiter, tracking how the delimiters impact the structure of the document.Spans of HTML appear between delimiters. If these spans exist at the top level of the document, meaning there is no containing block around them, they are considered freeform HTML content. If, however, they appear <em>inside</em> block structure they are interpreted as innerHTML for the containing block. <h3>Tokens and scanning</h3> As the processor scans through a document is reports information about the token on which is pauses. Tokens represent spans of text in the input comprising block delimiters and spans of HTML. <ul> <li>self::next_token() visits every contiguous subspan of text in the input document. This includes all explicit block comment delimiters and spans of HTML content (whether freeform or inner HTML).</li> <li>self::next_delimiter() visits every explicit block comment delimiter unless passed a block type which covers freeform HTML content. In these cases it will stop at top-level spans of HTML and report a null block type.</li> <li>self::next_block() visits every block delimiter which <em>opens</em> a block.This includes opening block delimiters as well as void block delimiters. With the same exception as above for freeform HTML block types, this will visit top-level spans of HTML content.</li> </ul> When matched on a particular token, the following methods provide structural and textual information about it: <ul> <li>self::get_delimiter_type() reports whether the delimiter is an opener, a closer, or if it represents a whole void block.</li> <li>self::get_block_type() reports the fully-qualified block type which the delimiter represents.</li> <li>self::get_printable_block_type() reports the fully-qualified block type, but returns core/freeform instead of null for top-level freeform HTML content.</li> <li>self::is_block_type() indicates if the delimiter represents a block of the given block type, or wildcard or pseudo-block type described below.</li> <li>self::opens_block() indicates if the delimiter opens a block of one of the provided block types. Opening, void, and top-level freeform HTML content all open blocks.</li> <li>static::get_attributes() is currently reserved for a future streaming JSON parser class.</li> <li>self::allocate_and_return_parsed_attributes() extracts the JSON attributes for delimiters which open blocks and return the fully-parsed attributes as an associative array. static::get_last_json_error() for when this fails.</li> <li>self::is_html() indicates if the token is a span of HTML which might be top-level freeform content or a block’s inner HTML.</li> <li>self::get_html_content() returns the span of HTML.</li> <li>self::get_span() for the byte offset and length into the input document representing the token.</li> </ul> It’s possible for the processor to fail to scan forward if the input document ends in a proper prefix of an explicit block comment delimiter. For example, if the input ends in <!-- wp: then it <em>might</em> be the start of another delimiter. The parser cannot know, however, and therefore refuses to proceed. static::get_last_error() to distinguish between a failure to find the next token and an incomplete input. <h3>Block types</h3> A block’s “type” comprises an optional <em>namespace</em> and <em>name</em>. If the namespace isn’t provided it will be interpreted as the implicit core namespace. For example, the type gallery is the name of the block in the core namespace, but the type abc/gallery is the <em>fully-qualified</em> block type for the block whose name is still gallery, but in the abc namespace. Methods on this class are aware of this block naming semantic and anywhere a block type is an argument to a method it will be normalized to account for implicit namespaces.Passing paragraph is the same as passing core/paragraph. On the contrary, anywhere this class returns a block type, it will return the fully-qualified and normalized form.For example, for the <!-- wp:group --> delimiter it will return core/group as the block type. There are two special block types that change the behavior of the processor: <ul> <li> The wildcard * represents <em>any block</em>. In addition to matching all block types, it also represents top-level freeform HTML whose block type is reported as null. </li> <li> The core/freeform block type is a pseudo-block type which explicitly matches top-level freeform HTML. </li> </ul> These special block types can be passed into any method which searches for blocks. There is one additional special block type which may be returned from self::get_printable_block_type(). This is the #innerHTML type, which indicates that the HTML span on which the processor is paused is inner HTML for a containing block. <h3>Spans of HTML</h3> Non-block content plays a complicated role in processing block documents. This processor exposes tools to help work with these spans of HTML. <ul> <li>self::is_html() indicates if the processor is paused at a span of HTML but does not differentiate between top-level freeform content and inner HTML.</li> <li>self::is_non_whitespace_html() indicates not only if the processor is paused at a span of HTML, but also whether that span incorporates more than whitespace characters. Because block serialization often inserts newlines between block comment delimiters, this is useful for distinguishing “real” freeform content from purely aesthetic syntax.</li> <li>self::is_block_type() matches top-level freeform HTML content when provided one of the special block types described above.</li> </ul> <h3>Block structure</h3> As the processor traverses block delimiters it maintains a stack of which blocks are open at the given place in the document where it’s paused. This stack represents the block structure of a document and is used to determine where blocks end, which blocks represent inner blocks, whether a span of HTML is top-level freeform content, and more. Investigate the stack with self::get_breadcrumbs(), which returns an array of block types starting at the outermost-open block and descending to the currently-visited block. Unlike {@parse_blocks()}, spans of HTML appear in this structure as the special reported block type #html. Such a span represents inner HTML for a block if the depth reported by self::get_depth() is greater than one. It will generally not be necessary to inspect the stack of open blocks, though depth may be important for finding where blocks end. When visiting a block opener, the depth will have been increased before pausing; in contrast the depth is decremented before visiting a closer. This makes the following an easy way to determine if a block is still open. Example: $depth = $processor->get_depth();
while ( $processor->next_token() && $processor->get_depth() > $depth ) {
continue
}
// Processor is now paused at the token immediately following the closed block. <h4>Extracting blocks</h4> A unique feature of this processor is the ability to return the same output as parse_blocks() would produce, but for a subset of the input document.For example, it’s possible to extract an image block, manipulate that parsed block, and re-serialize it into the original document. It’s possible to do so while skipping over the parse of the rest of the document. self::extract_full_block_and_advance() will scan forward from the current block opener and build the parsed block structure until the current block is closed. It will include all inner HTML and inner blocks, and parse all of the inner blocks. It can be used to extract a block at any depth in the document, helpful for operating on blocks within nested structure. Example: if ( ! $processor->next_block( 'gallery' ) ) {
return $post_content;
}
$gallery_at = $processor->get_span()->start;
$gallery_block = $processor->extract_full_block_and_advance();
$after_gallery = $processor->get_span()->start;
return (
substr( $post_content, 0, $gallery_at ) .
serialize_block( modify_gallery( $gallery_block ) .
substr( $post_content, $after_gallery )
); <h4>Handling of malformed structure</h4> There are situations where closing block delimiters appear for which no open block exists, or where a document ends before a block is closed, or where a closing block delimiter appears but references a different block type than the most-recently opened block does. In all of these cases, the stack of open blocks should mirror the behavior in parse_blocks(). Unlike parse_blocks(), however, this processor can still operate on the invalid block delimiters. It provides a few functions which can be used for building custom and non-spec-compliant error handling. <ul> <li>self::has_closing_flag() indicates if the block delimiter contains the closing flag at the end. Some invalid block delimiters might contain both the void and closing flag, in which case self::get_delimiter_type() will report that it’s a void block.</li> <li>static::get_last_error() indicates if the processor reached an invalid block closing. Depending on the context, parse_blocks() might instead ignore the token or treat it as freeform HTML content.</li> </ul> <h2>Static helpers</h2> This class provides helpers for performing semantic block-related operations. <ul> <li>self::normalize_block_type() takes a block type with or without the implicit core namespace and returns a fully-qualified block type.</li> <li>self::are_equal_block_types() indicates if two spans across one or more input texts represent the same fully-qualified block type.</li> </ul> <h2>Subclassing</h2> This processor is designed to accurately parse a block document. Therefore, many of its methods are not meant for subclassing. However, overall this class supports building higher-level convenience classes which may choose to subclass it. For those classes, avoid re-implementing methods except for the list below. Instead, create new names representing the higher-level concepts being introduced. For example, instead of creating a new method named next_block() which only advances to blocks of a given kind, consider creating a new method named something like next_layout_block() which won’t interfere with the base class method. <ul> <li>static::get_last_error() may be reimplemented to report new errors in the subclass which aren’t intrinsic to block parsing.</li> <li>static::get_attributes() may be reimplemented to provide a streaming interface to reading and modifying a block’s JSON attributes. It should be fast and memory efficient.</li> <li>static::get_last_json_error() may be reimplemented to report new errors introduced with a reimplementation of static::get_attributes().</li> </ul>
Properties · 18
$last_errorstring|nullprivate
Indicates if the last operation failed, otherwise will be null for success.
$last_json_errorintprivate
Indicates failures from decoding JSON attributes.
$source_textstringprotected
Source text provided to processor.
$matched_delimiter_atintprivate
Byte offset into source text where a matched delimiter starts.
$matched_delimiter_lengthintprivate
Byte length of full span of a matched delimiter.
$after_previous_delimiterintprivate
First byte offset into source text following any previously-matched delimiter.
$namespace_atintprivate
Byte offset where namespace span begins.
$name_atintprivate
Byte offset where block name span begins.
$name_lengthintprivate
Byte length of block name span.
$has_closing_flagboolprivate
Whether the delimiter contains the block-closing flag.
$json_atintprivate
Byte offset where JSON attributes span begins.
$json_lengthintprivate
Byte length of JSON attributes span, or 0 if none are present.
$statestringprotected
Internal parser state, differentiating whether the instance is currently matched, on an implicit freeform node, in error, or ready to begin parsing.
$typestringprivate
Indicates what kind of block comment delimiter was matched.
$was_voidboolprivate
Whether the last-matched delimiter acts like a void block and should be popped from the stack of open blocks as soon as the parser advances.
$open_blocks_atint[]private
For every open block, in hierarchical order, this stores the byte offset into the source text where the block type starts, including for HTML spans.
$open_blocks_lengthint[]private
For every open block, in hierarchical order, this stores the byte length of the block’s block type in the source text. For HTML spans this is 0.
$next_stack_op'push'|'void'|'pop'|nullprivate
Indicates which operation should apply to the stack of open blocks after processing any pending spans of HTML.
next_block()Advance to the next block delimiter which opens a block, indicating if one was found.
next_delimiter()Advance to the next block delimiter in a document, indicating if one was found.
next_token()Advance to the next block delimiter or HTML span in a document, indicating if one was found.
get_breadcrumbs()Returns an array containing the names of the currently-open blocks, in order from outermost to innermost, with HTML spans indicated as “#html”.
get_depth()Returns the depth of the open blocks where the processor is currently matched.
extract_full_block_and_advance()Extracts a block object, and all inner content, starting at a matched opening block delimiter, or at a matched top-level HTML span as freeform HTML content.
find_html_comment_end()Returns the byte-offset after the ending character of an HTML comment, assuming the proper starting byte offset.
get_last_error()Indicates if the last attempt to parse a block comment delimiter failed, if set, otherwise `null` if the last attempt succeeded.
get_last_json_error()Indicates if the last attempt to parse a block’s JSON attributes failed.
has_closing_flag()Returns whether the delimiter contains the closing flag.
is_block_type()Indicates if the block delimiter represents a block of the given type.
are_equal_block_types()Given two spans of text, indicate if they represent identical block types.
opens_block()Indicates if the matched delimiter is an opening or void delimiter of the given type, if a type is provided, otherwise if it opens any block or implicit freeform HTML content.
is_html()Indicates if the matched delimiter is an HTML span.
is_non_whitespace_html()Indicates if the matched delimiter is an HTML span and comprises more than whitespace characters, i.e. contains real content.
get_html_content()Returns the string content of a matched HTML span, or `null` otherwise.
get_block_type()Allocates a substring for the block type and returns the fully-qualified name, including the namespace, if matched on a delimiter, otherwise `null`.
get_printable_block_type()Allocates a printable substring for the block type and returns the fully-qualified name, including the namespace, if matched on a delimiter or freeform block, otherwise `null`.
normalize_block_type()Normalizes a block name to ensure that missing implicit “core” namespaces are present.
get_attributes()Returns a lazy wrapper around the block attributes, which can be used for efficiently interacting with the JSON attributes.
allocate_and_return_parsed_attributes()Attempts to parse and return the entire JSON attributes from the delimiter, allocating memory and processing the JSON span in the process.
get_span()Returns the span representing the currently-matched delimiter, if matched, otherwise `null`.
267classWP_Block_Processor{268/**269 * Indicates if the last operation failed, otherwise270 * will be `null` for success.271 *272 * @since 6.9.0273 *274 * @var string|null275 */276private$last_error=null;277278/**279 * Indicates failures from decoding JSON attributes.280 *281 * @since 6.9.0282 *283 * @see \json_last_error()284 *285 * @var int286 */287private$last_json_error=JSON_ERROR_NONE;288289/**290 * Source text provided to processor.291 *292 * @since 6.9.0293 *294 * @var string295 */296protected$source_text;297298/**299 * Byte offset into source text where a matched delimiter starts.300 *301 * Example:302 *303 * 5 10 15 20 25 30 35 40 45 50304 * <!-- wp:group --><!-- wp:void /--><!-- /wp:group -->305 * ╰─ Starts at byte offset 17.306 *307 * @since 6.9.0308 *309 * @var int310 */311private$matched_delimiter_at=0;312313/**314 * Byte length of full span of a matched delimiter.315 *316 * Example:317 *318 * 5 10 15 20 25 30 35 40 45 50319 * <!-- wp:group --><!-- wp:void /--><!-- /wp:group -->320 * ╰───────────────╯321 * 17 bytes long.322 *323 * @since 6.9.0324 *325 * @var int326 */327private$matched_delimiter_length=0;328329/**330* First byte offset into source text following any previously-matched delimiter.331* Used to indicate where an HTML span starts.332*333* Example:334*335*510152025303540455055336*<!-- wp:paragraph --><p>Content</p><⃨!⃨-⃨-⃨ ⃨/⃨w⃨p⃨:⃨p⃨a⃨r⃨a⃨g⃨r⃨a⃨p⃨h⃨ ⃨-⃨-⃨>⃨337* │ ╰─ This delimiter was matched,and after matching,338* │ revealed the preceding HTML span.339* │340* ╰─ The first byte offset after the previous matched delimiter341* is 21. Because the matched delimiter starts at 55, which is after342* this, a span of HTML must exist between these boundaries.343*344* @since 6.9.0345*346* @varint
History
Introduced in 6.9.0. Unchanged from 6.9.7 through 7.1.0.
Signature, return type and hooks compared across 3 parsed releases.
About this page
Parsed data
Generated from the wordpress-develop 6.9.7 tag, from src/wp-includes/class-wp-block-processor.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.