Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Serialize the Interactivity API's store from PHP and hydrate it on the client #8447

Merged
merged 27 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5067e7a
Update Interactivity API
DAreRodz Jan 27, 2023
92d0d9d
Change `wp` prefixes to `woo`
DAreRodz Jan 27, 2023
0199ade
Use `woo` prefix for the directives runtime bundle
DAreRodz Jan 27, 2023
ceedc72
Merge branch 'trunk' into update/interactivity-api-prefixes
DAreRodz Feb 10, 2023
6277a3c
Update Interactivity API runtime
DAreRodz Feb 10, 2023
423c858
Hardcode php from interactivity API
DAreRodz Feb 14, 2023
9af6478
Temporarily add gutenberg plugin as dependency
DAreRodz Feb 14, 2023
36bd2c5
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 20, 2023
78b7db2
Exclude Interactivity API files from phpcs checks
DAreRodz Feb 20, 2023
92f5a85
Update Interactivity API js files
DAreRodz Feb 20, 2023
0c11440
Update Interactivity API php files
DAreRodz Feb 21, 2023
97f39c1
Remove gutenberg from wp-env plugins
DAreRodz Feb 21, 2023
28dd2ad
Fix registered runtime paths
DAreRodz Feb 14, 2023
5453d6b
Fix prefixes when getting attributes in directives
DAreRodz Feb 14, 2023
1874a4c
Fix directive prefix in constants
DAreRodz Feb 21, 2023
e3df31b
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 21, 2023
0cd9a23
Avoid a Fatal error when importing `wp-html`
DAreRodz Feb 21, 2023
68b0a66
Remove TODO comments from Interactivity API files
DAreRodz Feb 21, 2023
17fe079
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 21, 2023
fa0ef8a
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 22, 2023
92c93af
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 23, 2023
a824616
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 28, 2023
f7ee7d1
Add missing prefix to some global functions
DAreRodz Feb 28, 2023
ebd1119
Use true as value for boolean attributes
DAreRodz Feb 28, 2023
2f36582
Add `wp-html` file
DAreRodz Feb 28, 2023
248de47
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 28, 2023
e842182
Change requires in `wp-html` with includes
DAreRodz Feb 28, 2023
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
Prev Previous commit
Next Next commit
Hardcode php from interactivity API
  • Loading branch information
DAreRodz committed Feb 14, 2023
commit 423c858cf4dff718e7ef4b53e465fe6459051d62
22 changes: 22 additions & 0 deletions src/Interactivity/directives/attributes/woo-bind.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

require_once __DIR__ . '/../utils.php';

function process_woo_bind( $tags, $context ) {
if ( $tags->is_tag_closer() ) {
return;
}

$prefixed_attributes = $tags->get_attribute_names_with_prefix( 'woo-bind:' );

foreach ( $prefixed_attributes as $attr ) {
list( , $bound_attr ) = explode( ':', $attr );
if ( empty( $bound_attr ) ) {
continue;
}

$expr = $tags->get_attribute( $attr );
$value = evaluate( $expr, $context->get_context() );
$tags->set_attribute( $bound_attr, $value );
}
}
26 changes: 26 additions & 0 deletions src/Interactivity/directives/attributes/woo-class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

require_once __DIR__ . '/../utils.php';

function process_woo_class( $tags, $context ) {
if ( $tags->is_tag_closer() ) {
return;
}

$prefixed_attributes = $tags->get_attribute_names_with_prefix( 'woo-class:' );

foreach ( $prefixed_attributes as $attr ) {
list( , $class_name ) = explode( ':', $attr );
if ( empty( $class_name ) ) {
continue;
}

$expr = $tags->get_attribute( $attr );
$add_class = evaluate( $expr, $context->get_context() );
if ( $add_class ) {
$tags->add_class( $class_name );
} else {
$tags->remove_class( $class_name );
}
}
}
19 changes: 19 additions & 0 deletions src/Interactivity/directives/attributes/woo-context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

function process_woo_context_attribute( $tags, $context ) {
if ( $tags->is_tag_closer() ) {
$context->rewind_context();
return;
}

$value = $tags->get_attribute( 'woo-context' );
if ( null === $value ) {
// No woo-context directive.
return;
}

$new_context = json_decode( $value, true );
// TODO: Error handling.

$context->set_context( $new_context );
}
30 changes: 30 additions & 0 deletions src/Interactivity/directives/attributes/woo-style.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

require_once __DIR__ . '/../utils.php';

function process_woo_style( $tags, $context ) {
if ( $tags->is_tag_closer() ) {
return;
}

$prefixed_attributes = $tags->get_attribute_names_with_prefix( 'woo-style:' );

foreach ( $prefixed_attributes as $attr ) {
list( , $style_name ) = explode( ':', $attr );
if ( empty( $style_name ) ) {
continue;
}

$expr = $tags->get_attribute( $attr );
$style_value = evaluate( $expr, $context->get_context() );
if ( $style_value ) {
$style_attr = $tags->get_attribute( 'style' );
$style_attr = set_style( $style_attr, $style_name, $style_value );
$tags->set_attribute( 'style', $style_attr );
} else {
// TODO: Do we want to unset styles if they're null?
// $tags->remove_class( $style_name );
}
}
}

73 changes: 73 additions & 0 deletions src/Interactivity/directives/class-woo-directive-context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* Context data implementation.
*
* @package block-hydration-experiments
*/

/**
* This is a data structure to hold the current context.
*
* Whenever encountering a `woo-context` directive, we need to update
* the context with the data found in that directive. Conversely,
* when "leaving" that context (by encountering a closing tag), we
* need to reset the context to its previous state. This means that
* we actually need sort of a stack to keep track of all nested contexts.
*
* Example:
*
* <woo-context data='{ "foo": 123 }'>
* <!-- foo should be 123 here. -->
* <woo-context data='{ "foo": 456 }'>
* <!-- foo should be 456 here. -->
* </woo-context>
* <!-- foo should be reset to 123 here. -->
* </woo-context>
*/
class Woo_Directive_Context {
/**
* The stack used to store contexts internally.
*
* @var array An array of contexts.
*/
protected $stack = array( array() );

/**
* Constructor.
*
* Accepts a context as an argument to initialize this with.
*
* @param array $context A context.
*/
function __construct( $context = array() ) {
$this->set_context( $context );
}

/**
* Return the current context.
*
* @return array The current context.
*/
public function get_context() {
return end( $this->stack );
}

/**
* Set the current context.
*
* @param array $context The context to be set.
* @return void
*/
public function set_context( $context ) {
array_push( $this->stack, array_replace_recursive( $this->get_context(), $context ) );
}

/**
* Reset the context to its previous state.
*
* @return void
*/
public function rewind_context() {
array_pop( $this->stack );
}
}
32 changes: 32 additions & 0 deletions src/Interactivity/directives/class-woo-directive-store.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

class Woo_Directive_Store {
private static array $store = array();

static function get_data() {
return self::$store;
}

static function merge_data( $data ) {
self::$store = array_replace_recursive( self::$store, $data );
}

static function serialize() {
return json_encode( self::$store );
}

static function reset() {
self::$store = array();
}

static function render() {
if ( empty( self::$store ) ) {
return;
}

// TODO: find a better ID for the script tag.
$id = 'store';
$store = self::serialize();
echo "<script id=\"$id\" type=\"application/json\">$store</script>";
}
}
19 changes: 19 additions & 0 deletions src/Interactivity/directives/tags/woo-context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

function process_woo_context_tag( $tags, $context ) {
if ( $tags->is_tag_closer() ) {
$context->rewind_context();
return;
}

$value = $tags->get_attribute( 'data' );
if ( null === $value ) {
// No woo-context directive.
return;
}

$new_context = json_decode( $value, true );
// TODO: Error handling.

$context->set_context( $new_context );
}
51 changes: 51 additions & 0 deletions src/Interactivity/directives/utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

require_once __DIR__ . '/class-woo-directive-store.php';

function store( $data ) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function names in this file might later collide with the ones we're using in the Block Interactivity API. Have you considered prefixing them?

Woo_Directive_Store::merge_data( $data );
}

// TODO: Implement evaluation of complex logical expressions.
function evaluate( string $path, array $context = array() ) {
$current = array_merge(
Woo_Directive_Store::get_data(),
array( 'context' => $context )
);

$array = explode( '.', $path );
foreach ( $array as $p ) {
if ( isset( $current[ $p ] ) ) {
$current = $current[ $p ];
} else {
return null;
}
}
return $current;
}

function set_style( $style, $name, $value ) {
$style_assignments = explode( ';', $style );
$modified = false;
foreach ( $style_assignments as $style_assignment ) {
list( $style_name ) = explode( ':', $style_assignment );
if ( trim( $style_name ) === $name ) {
// TODO: Retain surrounding whitespace from $style_value, if any.
$style_assignment = $style_name . ': ' . $value;
$modified = true;
break;
}
}

if ( ! $modified ) {
$new_style_assignment = $name . ': ' . $value;
// If the last element is empty or whitespace-only, we insert
// the new "key: value" pair before it.
if ( empty( trim( end( $style_assignments ) ) ) ) {
array_splice( $style_assignments, - 1, 0, $new_style_assignment );
} else {
array_push( $style_assignments, $new_style_assignment );
}
}
return implode( ';', $style_assignments );
}
Loading