wp/wp-includes/class-wp-block-parser.php
changeset 21 48c4eec2b7e6
parent 18 be944660c56a
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     2 /**
     2 /**
     3  * Block Serialization Parser
     3  * Block Serialization Parser
     4  *
     4  *
     5  * @package WordPress
     5  * @package WordPress
     6  */
     6  */
     7 
       
     8 /**
       
     9  * Class WP_Block_Parser_Block
       
    10  *
       
    11  * Holds the block structure in memory
       
    12  *
       
    13  * @since 5.0.0
       
    14  */
       
    15 class WP_Block_Parser_Block {
       
    16 	/**
       
    17 	 * Name of block
       
    18 	 *
       
    19 	 * @example "core/paragraph"
       
    20 	 *
       
    21 	 * @since 5.0.0
       
    22 	 * @var string
       
    23 	 */
       
    24 	public $blockName;
       
    25 
       
    26 	/**
       
    27 	 * Optional set of attributes from block comment delimiters
       
    28 	 *
       
    29 	 * @example null
       
    30 	 * @example array( 'columns' => 3 )
       
    31 	 *
       
    32 	 * @since 5.0.0
       
    33 	 * @var array|null
       
    34 	 */
       
    35 	public $attrs;
       
    36 
       
    37 	/**
       
    38 	 * List of inner blocks (of this same class)
       
    39 	 *
       
    40 	 * @since 5.0.0
       
    41 	 * @var WP_Block_Parser_Block[]
       
    42 	 */
       
    43 	public $innerBlocks;
       
    44 
       
    45 	/**
       
    46 	 * Resultant HTML from inside block comment delimiters
       
    47 	 * after removing inner blocks
       
    48 	 *
       
    49 	 * @example "...Just <!-- wp:test /--> testing..." -> "Just testing..."
       
    50 	 *
       
    51 	 * @since 5.0.0
       
    52 	 * @var string
       
    53 	 */
       
    54 	public $innerHTML;
       
    55 
       
    56 	/**
       
    57 	 * List of string fragments and null markers where inner blocks were found
       
    58 	 *
       
    59 	 * @example array(
       
    60 	 *   'innerHTML'    => 'BeforeInnerAfter',
       
    61 	 *   'innerBlocks'  => array( block, block ),
       
    62 	 *   'innerContent' => array( 'Before', null, 'Inner', null, 'After' ),
       
    63 	 * )
       
    64 	 *
       
    65 	 * @since 4.2.0
       
    66 	 * @var array
       
    67 	 */
       
    68 	public $innerContent;
       
    69 
       
    70 	/**
       
    71 	 * Constructor.
       
    72 	 *
       
    73 	 * Will populate object properties from the provided arguments.
       
    74 	 *
       
    75 	 * @since 5.0.0
       
    76 	 *
       
    77 	 * @param string $name         Name of block.
       
    78 	 * @param array  $attrs        Optional set of attributes from block comment delimiters.
       
    79 	 * @param array  $innerBlocks  List of inner blocks (of this same class).
       
    80 	 * @param string $innerHTML    Resultant HTML from inside block comment delimiters after removing inner blocks.
       
    81 	 * @param array  $innerContent List of string fragments and null markers where inner blocks were found.
       
    82 	 */
       
    83 	function __construct( $name, $attrs, $innerBlocks, $innerHTML, $innerContent ) {
       
    84 		$this->blockName    = $name;
       
    85 		$this->attrs        = $attrs;
       
    86 		$this->innerBlocks  = $innerBlocks;
       
    87 		$this->innerHTML    = $innerHTML;
       
    88 		$this->innerContent = $innerContent;
       
    89 	}
       
    90 }
       
    91 
       
    92 /**
       
    93  * Class WP_Block_Parser_Frame
       
    94  *
       
    95  * Holds partial blocks in memory while parsing
       
    96  *
       
    97  * @internal
       
    98  * @since 5.0.0
       
    99  */
       
   100 class WP_Block_Parser_Frame {
       
   101 	/**
       
   102 	 * Full or partial block
       
   103 	 *
       
   104 	 * @since 5.0.0
       
   105 	 * @var WP_Block_Parser_Block
       
   106 	 */
       
   107 	public $block;
       
   108 
       
   109 	/**
       
   110 	 * Byte offset into document for start of parse token
       
   111 	 *
       
   112 	 * @since 5.0.0
       
   113 	 * @var int
       
   114 	 */
       
   115 	public $token_start;
       
   116 
       
   117 	/**
       
   118 	 * Byte length of entire parse token string
       
   119 	 *
       
   120 	 * @since 5.0.0
       
   121 	 * @var int
       
   122 	 */
       
   123 	public $token_length;
       
   124 
       
   125 	/**
       
   126 	 * Byte offset into document for after parse token ends
       
   127 	 * (used during reconstruction of stack into parse production)
       
   128 	 *
       
   129 	 * @since 5.0.0
       
   130 	 * @var int
       
   131 	 */
       
   132 	public $prev_offset;
       
   133 
       
   134 	/**
       
   135 	 * Byte offset into document where leading HTML before token starts
       
   136 	 *
       
   137 	 * @since 5.0.0
       
   138 	 * @var int
       
   139 	 */
       
   140 	public $leading_html_start;
       
   141 
       
   142 	/**
       
   143 	 * Constructor
       
   144 	 *
       
   145 	 * Will populate object properties from the provided arguments.
       
   146 	 *
       
   147 	 * @since 5.0.0
       
   148 	 *
       
   149 	 * @param WP_Block_Parser_Block $block              Full or partial block.
       
   150 	 * @param int                   $token_start        Byte offset into document for start of parse token.
       
   151 	 * @param int                   $token_length       Byte length of entire parse token string.
       
   152 	 * @param int                   $prev_offset        Byte offset into document for after parse token ends.
       
   153 	 * @param int                   $leading_html_start Byte offset into document where leading HTML before token starts.
       
   154 	 */
       
   155 	function __construct( $block, $token_start, $token_length, $prev_offset = null, $leading_html_start = null ) {
       
   156 		$this->block              = $block;
       
   157 		$this->token_start        = $token_start;
       
   158 		$this->token_length       = $token_length;
       
   159 		$this->prev_offset        = isset( $prev_offset ) ? $prev_offset : $token_start + $token_length;
       
   160 		$this->leading_html_start = $leading_html_start;
       
   161 	}
       
   162 }
       
   163 
     7 
   164 /**
     8 /**
   165  * Class WP_Block_Parser
     9  * Class WP_Block_Parser
   166  *
    10  *
   167  * Parses a document and constructs a list of parsed block objects
    11  * Parses a document and constructs a list of parsed block objects
   203 	 * @var WP_Block_Parser_Frame[]
    47 	 * @var WP_Block_Parser_Frame[]
   204 	 */
    48 	 */
   205 	public $stack;
    49 	public $stack;
   206 
    50 
   207 	/**
    51 	/**
   208 	 * Empty associative array, here due to PHP quirks
       
   209 	 *
       
   210 	 * @since 4.4.0
       
   211 	 * @var array empty associative array
       
   212 	 */
       
   213 	public $empty_attrs;
       
   214 
       
   215 	/**
       
   216 	 * Parses a document and returns a list of block structures
    52 	 * Parses a document and returns a list of block structures
   217 	 *
    53 	 *
   218 	 * When encountering an invalid parse will return a best-effort
    54 	 * When encountering an invalid parse will return a best-effort
   219 	 * parse. In contrast to the specification parser this does not
    55 	 * parse. In contrast to the specification parser this does not
   220 	 * return an error on invalid inputs.
    56 	 * return an error on invalid inputs.
   221 	 *
    57 	 *
   222 	 * @since 5.0.0
    58 	 * @since 5.0.0
   223 	 *
    59 	 *
   224 	 * @param string $document Input document being parsed.
    60 	 * @param string $document Input document being parsed.
   225 	 * @return WP_Block_Parser_Block[]
    61 	 * @return array[]
   226 	 */
    62 	 */
   227 	function parse( $document ) {
    63 	public function parse( $document ) {
   228 		$this->document    = $document;
    64 		$this->document = $document;
   229 		$this->offset      = 0;
    65 		$this->offset   = 0;
   230 		$this->output      = array();
    66 		$this->output   = array();
   231 		$this->stack       = array();
    67 		$this->stack    = array();
   232 		$this->empty_attrs = json_decode( '{}', true );
    68 
   233 
    69 		while ( $this->proceed() ) {
   234 		do {
    70 			continue;
   235 			// twiddle our thumbs.
    71 		}
   236 		} while ( $this->proceed() );
       
   237 
    72 
   238 		return $this->output;
    73 		return $this->output;
   239 	}
    74 	}
   240 
    75 
   241 	/**
    76 	/**
   250 	 *
    85 	 *
   251 	 * @internal
    86 	 * @internal
   252 	 * @since 5.0.0
    87 	 * @since 5.0.0
   253 	 * @return bool
    88 	 * @return bool
   254 	 */
    89 	 */
   255 	function proceed() {
    90 	public function proceed() {
   256 		$next_token = $this->next_token();
    91 		$next_token = $this->next_token();
   257 		list( $token_type, $block_name, $attrs, $start_offset, $token_length ) = $next_token;
    92 		list( $token_type, $block_name, $attrs, $start_offset, $token_length ) = $next_token;
   258 		$stack_depth = count( $this->stack );
    93 		$stack_depth = count( $this->stack );
   259 
    94 
   260 		// we may have some HTML soup before the next block.
    95 		// we may have some HTML soup before the next block.
   396 	 * @internal
   231 	 * @internal
   397 	 * @since 5.0.0
   232 	 * @since 5.0.0
   398 	 * @since 4.6.1 fixed a bug in attribute parsing which caused catastrophic backtracking on invalid block comments
   233 	 * @since 4.6.1 fixed a bug in attribute parsing which caused catastrophic backtracking on invalid block comments
   399 	 * @return array
   234 	 * @return array
   400 	 */
   235 	 */
   401 	function next_token() {
   236 	public function next_token() {
   402 		$matches = null;
   237 		$matches = null;
   403 
   238 
   404 		/*
   239 		/*
   405 		 * aye the magic
   240 		 * aye the magic
   406 		 * we're using a single RegExp to tokenize the block comment delimiters
   241 		 * we're using a single RegExp to tokenize the block comment delimiters
   441 		 * Fun fact! It's not trivial in PHP to create "an empty associative array" since all arrays
   276 		 * Fun fact! It's not trivial in PHP to create "an empty associative array" since all arrays
   442 		 * are associative arrays. If we use `array()` we get a JSON `[]`
   277 		 * are associative arrays. If we use `array()` we get a JSON `[]`
   443 		 */
   278 		 */
   444 		$attrs = $has_attrs
   279 		$attrs = $has_attrs
   445 			? json_decode( $matches['attrs'][0], /* as-associative */ true )
   280 			? json_decode( $matches['attrs'][0], /* as-associative */ true )
   446 			: $this->empty_attrs;
   281 			: array();
   447 
   282 
   448 		/*
   283 		/*
   449 		 * This state isn't allowed
   284 		 * This state isn't allowed
   450 		 * This is an error
   285 		 * This is an error
   451 		 */
   286 		 */
   468 	 * Returns a new block object for freeform HTML
   303 	 * Returns a new block object for freeform HTML
   469 	 *
   304 	 *
   470 	 * @internal
   305 	 * @internal
   471 	 * @since 3.9.0
   306 	 * @since 3.9.0
   472 	 *
   307 	 *
   473 	 * @param string $innerHTML HTML content of block.
   308 	 * @param string $inner_html HTML content of block.
   474 	 * @return WP_Block_Parser_Block freeform block object.
   309 	 * @return WP_Block_Parser_Block freeform block object.
   475 	 */
   310 	 */
   476 	function freeform( $innerHTML ) {
   311 	public function freeform( $inner_html ) {
   477 		return new WP_Block_Parser_Block( null, $this->empty_attrs, array(), $innerHTML, array( $innerHTML ) );
   312 		return new WP_Block_Parser_Block( null, array(), array(), $inner_html, array( $inner_html ) );
   478 	}
   313 	}
   479 
   314 
   480 	/**
   315 	/**
   481 	 * Pushes a length of text from the input document
   316 	 * Pushes a length of text from the input document
   482 	 * to the output list as a freeform block.
   317 	 * to the output list as a freeform block.
   483 	 *
   318 	 *
   484 	 * @internal
   319 	 * @internal
   485 	 * @since 5.0.0
   320 	 * @since 5.0.0
   486 	 * @param null $length how many bytes of document text to output.
   321 	 * @param null $length how many bytes of document text to output.
   487 	 */
   322 	 */
   488 	function add_freeform( $length = null ) {
   323 	public function add_freeform( $length = null ) {
   489 		$length = $length ? $length : strlen( $this->document ) - $this->offset;
   324 		$length = $length ? $length : strlen( $this->document ) - $this->offset;
   490 
   325 
   491 		if ( 0 === $length ) {
   326 		if ( 0 === $length ) {
   492 			return;
   327 			return;
   493 		}
   328 		}
   504 	 * @param WP_Block_Parser_Block $block        The block to add to the output.
   339 	 * @param WP_Block_Parser_Block $block        The block to add to the output.
   505 	 * @param int                   $token_start  Byte offset into the document where the first token for the block starts.
   340 	 * @param int                   $token_start  Byte offset into the document where the first token for the block starts.
   506 	 * @param int                   $token_length Byte length of entire block from start of opening token to end of closing token.
   341 	 * @param int                   $token_length Byte length of entire block from start of opening token to end of closing token.
   507 	 * @param int|null              $last_offset  Last byte offset into document if continuing form earlier output.
   342 	 * @param int|null              $last_offset  Last byte offset into document if continuing form earlier output.
   508 	 */
   343 	 */
   509 	function add_inner_block( WP_Block_Parser_Block $block, $token_start, $token_length, $last_offset = null ) {
   344 	public function add_inner_block( WP_Block_Parser_Block $block, $token_start, $token_length, $last_offset = null ) {
   510 		$parent                       = $this->stack[ count( $this->stack ) - 1 ];
   345 		$parent                       = $this->stack[ count( $this->stack ) - 1 ];
   511 		$parent->block->innerBlocks[] = (array) $block;
   346 		$parent->block->innerBlocks[] = (array) $block;
   512 		$html                         = substr( $this->document, $parent->prev_offset, $token_start - $parent->prev_offset );
   347 		$html                         = substr( $this->document, $parent->prev_offset, $token_start - $parent->prev_offset );
   513 
   348 
   514 		if ( ! empty( $html ) ) {
   349 		if ( ! empty( $html ) ) {
   525 	 *
   360 	 *
   526 	 * @internal
   361 	 * @internal
   527 	 * @since 5.0.0
   362 	 * @since 5.0.0
   528 	 * @param int|null $end_offset byte offset into document for where we should stop sending text output as HTML.
   363 	 * @param int|null $end_offset byte offset into document for where we should stop sending text output as HTML.
   529 	 */
   364 	 */
   530 	function add_block_from_stack( $end_offset = null ) {
   365 	public function add_block_from_stack( $end_offset = null ) {
   531 		$stack_top   = array_pop( $this->stack );
   366 		$stack_top   = array_pop( $this->stack );
   532 		$prev_offset = $stack_top->prev_offset;
   367 		$prev_offset = $stack_top->prev_offset;
   533 
   368 
   534 		$html = isset( $end_offset )
   369 		$html = isset( $end_offset )
   535 			? substr( $this->document, $prev_offset, $end_offset - $prev_offset )
   370 			? substr( $this->document, $prev_offset, $end_offset - $prev_offset )
   551 		}
   386 		}
   552 
   387 
   553 		$this->output[] = (array) $stack_top->block;
   388 		$this->output[] = (array) $stack_top->block;
   554 	}
   389 	}
   555 }
   390 }
       
   391 
       
   392 /**
       
   393  * WP_Block_Parser_Block class.
       
   394  *
       
   395  * Required for backward compatibility in WordPress Core.
       
   396  */
       
   397 require_once __DIR__ . '/class-wp-block-parser-block.php';
       
   398 
       
   399 /**
       
   400  * WP_Block_Parser_Frame class.
       
   401  *
       
   402  * Required for backward compatibility in WordPress Core.
       
   403  */
       
   404 require_once __DIR__ . '/class-wp-block-parser-frame.php';