Skip to content
This repository was archived by the owner on Jul 28, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions phpunit/directives/wp-process-directives.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* wp_process_directives test.
*/

require_once __DIR__ . '/../../src/directives/wp-process-directives.php';

require_once __DIR__ . '/../../../gutenberg/lib/experimental/html/wp-html.php';

class Helper_Class {
function process_foo_test( $tags, $context ) {
}
}

/**
* Tests for the wp_process_directives function.
*
* @group directives
* @covers wp_process_directives
*/
class Tests_Directives_WpProcessDirectives extends WP_UnitTestCase {
public function test_correctly_call_attribute_directive_processor_on_closing_tag() {

// PHPUnit cannot stub functions, only classes.
$test_helper = $this->createMock( Helper_Class::class );

$test_helper->expects( $this->exactly( 2 ) )
->method( 'process_foo_test' )
->with(
$this->callback(
function( $p ) {
return 'DIV' === $p->get_tag() && (
// Either this is a closing tag...
$p->is_tag_closer() ||
// ...or it is an open tag, and has the directive attribute set.
( ! $p->is_tag_closer() && 'abc' === $p->get_attribute( 'foo-test' ) )
);
}
)
);

$attribute_directives = array(
'foo-test' => array( $test_helper, 'process_foo_test' ),
);

$markup = '<div>Example: <div foo-test="abc"><img><span>This is a test></span><div>Here is a nested div</div></div></div>';
$tags = new WP_HTML_Tag_Processor( $markup );
wp_process_directives( $tags, 'foo-', array(), $attribute_directives );
}
}
79 changes: 79 additions & 0 deletions src/directives/wp-process-directives.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

require_once __DIR__ . '/class-wp-directive-context.php';

function wp_process_directives( $tags, $prefix, $tag_directives, $attribute_directives ) {
$context = new WP_Directive_Context;

$tag_stack = array();
while ( $tags->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
$tag_name = strtolower( $tags->get_tag() );
if ( array_key_exists( $tag_name, $tag_directives ) ) {
call_user_func( $tag_directives[ $tag_name ], $tags, $context );
} else {
// Components can't have directives (unless we change our mind about this).

// Is this a tag that closes the latest opening tag?
if ( $tags->is_tag_closer() ) {
if ( 0 === count( $tag_stack ) ) {
continue;
}

list( $latest_opening_tag_name, $attributes ) = end( $tag_stack );
if ( $latest_opening_tag_name === $tag_name ) {
array_pop( $tag_stack );

// If the matching opening tag didn't have any attribute directives,
// we move on.
if ( 0 === count( $attributes ) ) {
continue;
}
}
} else {
$attributes = $tags->get_attribute_names_with_prefix( $prefix );
$attributes = array_intersect( $attributes, array_keys( $attribute_directives ) );

// If this is an open tag, and if it either has attribute directives,
// or if we're inside a tag that does, take note of this tag and its attribute
// directives so we can call its directive processor once we encounter the
// matching closing tag.
if (
! is_html_void_element( $tags->get_tag() ) &&
( 0 !== count( $attributes ) || 0 !== count( $tag_stack ) )
) {
$tag_stack[] = array( $tag_name, $attributes );
}
}

foreach ( $attributes as $attribute ) {
call_user_func( $attribute_directives[ $attribute ], $tags, $context );
}
}
}

return $tags;
}

// TODO: Move into `WP_HTML_Tag_Processor` (or `WP_HTML_Processor`).
// See e.g. https://github.com/WordPress/gutenberg/pull/47573.
function is_html_void_element( $tag_name ) {
switch ( $tag_name ) {
case 'AREA':
case 'BASE':
case 'BR':
case 'COL':
case 'EMBED':
case 'HR':
case 'IMG':
case 'INPUT':
case 'LINK':
case 'META':
case 'SOURCE':
case 'TRACK':
case 'WBR':
return true;

default:
return false;
}
}
40 changes: 9 additions & 31 deletions wp-directives.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ function () {

require_once __DIR__ . '/src/directives/class-wp-directive-context.php';
require_once __DIR__ . '/src/directives/class-wp-directive-store.php';
require_once __DIR__ . 'src/directives/wp-process-directives.php';

require_once __DIR__ . '/src/directives/attributes/wp-bind.php';
require_once __DIR__ . '/src/directives/attributes/wp-class.php';
require_once __DIR__ . '/src/directives/attributes/wp-style.php';
require_once __DIR__ . '/src/directives/tags/wp-context.php';
require_once __DIR__ . '/attributes/wp-bind.php';
require_once __DIR__ . '/attributes/wp-class.php';
require_once __DIR__ . '/attributes/wp-style.php';
require_once __DIR__ . '/tags/wp-context.php';

function wp_directives_loader() {
// Load the Admin page.
Expand Down Expand Up @@ -209,7 +210,7 @@ function bhe_inner_blocks( $parsed_block, $source_block, $parent_block ) {
}
add_filter( 'render_block_data', 'bhe_inner_blocks', 10, 3 );

function wp_process_directives( $block_content ) {
function process_directives_in_block( $block_content ) {
// TODO: Add some directive/components registration mechanism.
$tag_directives = array(
'wp-context' => 'process_wp_context_tag',
Expand All @@ -223,35 +224,12 @@ function wp_process_directives( $block_content ) {
);

$tags = new WP_HTML_Tag_Processor( $block_content );

$context = new WP_Directive_Context();
while ( $tags->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
$tag_name = strtolower( $tags->get_tag() );
if ( array_key_exists( $tag_name, $tag_directives ) ) {
call_user_func( $tag_directives[ $tag_name ], $tags, $context );
} else {
// Components can't have directives (unless we change our mind about this).
$attributes = $tags->get_attribute_names_with_prefix( 'wp-' );

foreach ( $attributes as $attribute ) {
if ( ! array_key_exists( $attribute, $attribute_directives ) ) {
continue;
}

call_user_func(
$attribute_directives[ $attribute ],
$tags,
$context
);
}
}
}

return $block_content;
$tags = wp_process_directives( $tags, 'wp-', $tag_directives, $attribute_directives );
return $tags->get_updated_html();
}
add_filter(
'render_block',
'wp_process_directives',
'process_directives_in_block',
10,
1
);
Expand Down