diff --git a/mailpoet/lib/EmailEditor/Integrations/MailPoet/PersonalizationTagManager.php b/mailpoet/lib/EmailEditor/Integrations/MailPoet/PersonalizationTagManager.php index 86fb518279..70bde1f233 100644 --- a/mailpoet/lib/EmailEditor/Integrations/MailPoet/PersonalizationTagManager.php +++ b/mailpoet/lib/EmailEditor/Integrations/MailPoet/PersonalizationTagManager.php @@ -2,6 +2,7 @@ namespace MailPoet\EmailEditor\Integrations\MailPoet; +use MailPoet\EmailEditor\Engine\PersonalizationTags\Personalization_Tag; use MailPoet\EmailEditor\Engine\PersonalizationTags\Personalization_Tags_Registry; use MailPoet\EmailEditor\Integrations\MailPoet\PersonalizationTags\Subscriber; @@ -16,26 +17,26 @@ class PersonalizationTagManager { public function initialize() { add_filter('mailpoet_email_editor_register_personalization_tags', function( Personalization_Tags_Registry $registry ): Personalization_Tags_Registry { - $registry->register( + $registry->register(new Personalization_Tag( __('First Name', 'mailpoet'), 'mailpoet/subscriber-firstname', __('Subscriber', 'mailpoet'), [$this->subscriber, 'getFirstName'], ['default' => __('subscriber', 'mailpoet')], - ); - $registry->register( + )); + $registry->register(new Personalization_Tag( __('Last Name', 'mailpoet'), 'mailpoet/subscriber-lastname', __('Subscriber', 'mailpoet'), [$this->subscriber, 'getLastName'], ['default' => __('subscriber', 'mailpoet')], - ); - $registry->register( + )); + $registry->register(new Personalization_Tag( __('Email', 'mailpoet'), 'mailpoet/subscriber-email', __('Subscriber', 'mailpoet'), [$this->subscriber, 'getEmail'], - ); + )); return $registry; }); } diff --git a/packages/php/email-editor/src/Engine/PersonalizationTags/class-personalization-tag.php b/packages/php/email-editor/src/Engine/PersonalizationTags/class-personalization-tag.php new file mode 100644 index 0000000000..c03b4de946 --- /dev/null +++ b/packages/php/email-editor/src/Engine/PersonalizationTags/class-personalization-tag.php @@ -0,0 +1,128 @@ + 'user' ) + * ); + * + * @param string $name The name of the tag displayed in the UI. + * @param string $token The token used in HTML_Tag_Processor to replace the tag with its value. + * @param string $category The category of the personalization tag for categorization on the UI. + * @param callable $callback The callback function which returns the value of the personalization tag. + * @param array $attributes The attributes which are used in the Personalization Tag UI. + */ + public function __construct( + string $name, + string $token, + string $category, + callable $callback, + array $attributes = array() + ) { + $this->name = $name; + $this->token = $token; + $this->category = $category; + $this->callback = $callback; + $this->attributes = $attributes; + } + + /** + * Returns the name of the personalization tag. + * + * @return string + */ + public function get_name(): string { + return $this->name; + } + + /** + * Returns the token of the personalization tag. + * + * @return string + */ + public function get_token(): string { + return $this->token; + } + + /** + * Returns the category of the personalization tag. + * + * @return string + */ + public function get_category(): string { + return $this->category; + } + + /** + * Returns the attributes of the personalization tag. + * + * @return array + */ + public function get_attributes(): array { + return $this->attributes; + } + + /** + * Executes the callback function for the personalization tag. + * + * @param mixed $context The context for the personalization tag. + * @param array $args The additional arguments for the callback. + * @return string The value of the personalization tag. + */ + public function execute_callback( $context, $args = array() ): string { + return call_user_func( $this->callback, ...array_merge( array( $context ), array( $args ) ) ); + } +} diff --git a/packages/php/email-editor/src/Engine/PersonalizationTags/class-personalization-tags-registry.php b/packages/php/email-editor/src/Engine/PersonalizationTags/class-personalization-tags-registry.php index a40d6e15a6..8cef0d294e 100644 --- a/packages/php/email-editor/src/Engine/PersonalizationTags/class-personalization-tags-registry.php +++ b/packages/php/email-editor/src/Engine/PersonalizationTags/class-personalization-tags-registry.php @@ -17,7 +17,7 @@ class Personalization_Tags_Registry { /** * List of registered personalization tags. * - * @var array + * @var Personalization_Tag[] */ private $tags = array(); @@ -33,35 +33,25 @@ class Personalization_Tags_Registry { /** * Register a new personalization tag. * - * @param string $name Unique identifier for the callback. - * @param string $tag The tag to be used in the email content. - * @param string $category The category of the personalization tag. - * @param callable $callback The callable function/method. - * @param array $attributes Additional data or settings for the callback (optional). + * @param Personalization_Tag $tag The personalization tag to register. * @return void */ - public function register( string $name, string $tag, string $category, callable $callback, array $attributes = array() ): void { - if ( isset( $this->tags[ $tag ] ) ) { + public function register( Personalization_Tag $tag ): void { + if ( isset( $this->tags[ $tag->get_token() ] ) ) { return; } - $this->tags[ $tag ] = array( - 'tag' => $tag, - 'name' => $name, - 'category' => $category, - 'callback' => $callback, - 'attributes' => $attributes, - ); + $this->tags[ $tag->get_token() ] = $tag; } /** * Retrieve a personalization tag by its tag. * - * @param string $tag The tag of the personalization tag. - * @return array|null The array data or null if not found. + * @param string $token The token of the personalization tag. + * @return Personalization_Tag|null The array data or null if not found. */ - public function get_by_tag( string $tag ): ?array { - return $this->tags[ $tag ] ?? null; + public function get_by_token( string $token ): ?Personalization_Tag { + return $this->tags[ $token ] ?? null; } /** diff --git a/packages/php/email-editor/src/Engine/class-personalizer.php b/packages/php/email-editor/src/Engine/class-personalizer.php index fa2de6a90e..aff823ea6f 100644 --- a/packages/php/email-editor/src/Engine/class-personalizer.php +++ b/packages/php/email-editor/src/Engine/class-personalizer.php @@ -61,12 +61,12 @@ class Personalizer { while ( $content_processor->next_token() ) { if ( $content_processor->get_token_type() === '#comment' ) { $token = $this->parse_token( $content_processor->get_modifiable_text() ); - $tag = $this->tags_registry->get_by_tag( $token['tag'] ); - if ( ! $tag || ! isset( $tag['callback'] ) ) { + $tag = $this->tags_registry->get_by_token( $token['tag'] ); + if ( ! $tag ) { continue; } - $value = call_user_func( $tag['callback'], ...array_merge( array( $this->context ), array( 'args' => $token ['arguments'] ) ) ); + $value = $tag->execute_callback( $this->context, $token['arguments'] ); $content_processor->replace_token( $value ); } elseif ( $content_processor->get_token_type() === '#tag' && $content_processor->get_tag() === 'TITLE' ) { diff --git a/packages/php/email-editor/tests/integration/Engine/Personalizer_Test.php b/packages/php/email-editor/tests/integration/Engine/Personalizer_Test.php index 08e688205b..1abdeba68a 100644 --- a/packages/php/email-editor/tests/integration/Engine/Personalizer_Test.php +++ b/packages/php/email-editor/tests/integration/Engine/Personalizer_Test.php @@ -8,6 +8,7 @@ declare(strict_types = 1); namespace MailPoet\EmailEditor\Engine; +use MailPoet\EmailEditor\Engine\PersonalizationTags\Personalization_Tag; use MailPoet\EmailEditor\Engine\PersonalizationTags\Personalization_Tags_Registry; /** @@ -43,12 +44,14 @@ class Personalizer_Test extends \MailPoetTest { public function testPersonalizeContentWithSingleTag(): void { // Register a tag in the registry. $this->tags_registry->register( - 'first_name', - 'user-firstname', - 'User', - function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- The $args parameter is not used in this test. - return $context['subscriber_name'] ?? 'Default Name'; - } + new Personalization_Tag( + 'first_name', + 'user-firstname', + 'User', + function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- The $args parameter is not used in this test. + return $context['subscriber_name'] ?? 'Default Name'; + } + ) ); $this->personalizer->set_context( array( 'subscriber_name' => 'John' ) ); @@ -62,21 +65,25 @@ class Personalizer_Test extends \MailPoetTest { public function testPersonalizeContentWithMultipleTags(): void { // Register multiple tags in the registry. $this->tags_registry->register( - 'first_name', - 'user/firstname', - 'Subscriber Info', - function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- The $args parameter is not used in this test. - return $context['subscriber_name'] ?? 'Default Name'; - } + new Personalization_Tag( + 'first_name', + 'user/firstname', + 'Subscriber Info', + function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- The $args parameter is not used in this test. + return $context['subscriber_name'] ?? 'Default Name'; + } + ) ); $this->tags_registry->register( - 'email', - 'user/email', - 'Subscriber Info', - function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- The $args parameter is not used in this test. - return $context['subscriber_email'] ?? 'unknown@example.com'; - } + new Personalization_Tag( + 'email', + 'user/email', + 'Subscriber Info', + function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- The $args parameter is not used in this test. + return $context['subscriber_email'] ?? 'unknown@example.com'; + } + ) ); // Set the context for personalization. @@ -120,12 +127,14 @@ class Personalizer_Test extends \MailPoetTest { */ public function testTagWithArguments(): void { $this->tags_registry->register( - 'default_name', - 'user/firstname', - 'Subscriber Info', - function ( $context, $args ) { - return $args['default'] ?? 'Default Name'; - } + new Personalization_Tag( + 'default_name', + 'user/firstname', + 'Subscriber Info', + function ( $context, $args ) { + return $args['default'] ?? 'Default Name'; + } + ) ); $html_content = '

Hello, !

'; @@ -137,12 +146,14 @@ class Personalizer_Test extends \MailPoetTest { */ public function testPersonalizationInTitle(): void { $this->tags_registry->register( - 'default_name', - 'user/firstname', - 'Subscriber Info', - function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- The $args parameter is not used in this test. - return $context['user_name'] ?? 'Default Name'; - } + new Personalization_Tag( + 'default_name', + 'user/firstname', + 'Subscriber Info', + function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- The $args parameter is not used in this test. + return $context['user_name'] ?? 'Default Name'; + } + ) ); $html_content = ' diff --git a/packages/php/email-editor/tests/unit/Engine/PersonalizationTags/Personalization_Tags_Registry_Test.php b/packages/php/email-editor/tests/unit/Engine/PersonalizationTags/Personalization_Tags_Registry_Test.php index 9a09cedbfa..acaa63ca8f 100644 --- a/packages/php/email-editor/tests/unit/Engine/PersonalizationTags/Personalization_Tags_Registry_Test.php +++ b/packages/php/email-editor/tests/unit/Engine/PersonalizationTags/Personalization_Tags_Registry_Test.php @@ -7,6 +7,7 @@ declare(strict_types = 1); +use MailPoet\EmailEditor\Engine\PersonalizationTags\Personalization_Tag; use PHPUnit\Framework\TestCase; use MailPoet\EmailEditor\Engine\PersonalizationTags\Personalization_Tags_Registry; @@ -32,61 +33,63 @@ class PersonalizationTagsRegistryTest extends TestCase { * Register tag and retrieve it. */ public function testRegisterAndGetTag(): void { - $callback = function () { + $callback = function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- Callback parameters are required. return 'Personalized Value'; }; // Register a tag. $this->registry->register( - 'first_name_tag', - 'first_name', - 'Subscriber Info', - $callback, - array( 'description' => 'First name of the subscriber' ) + new Personalization_Tag( + 'first_name_tag', + 'first_name', + 'Subscriber Info', + $callback, + array( 'description' => 'First name of the subscriber' ) + ) ); // Retrieve the tag. - $tag_data = $this->registry->get_by_tag( 'first_name' ); + $tag = $this->registry->get_by_token( 'first_name' ); // Assert that the tag is registered correctly. - $this->assertNotNull( $tag_data ); - $this->assertSame( 'first_name', $tag_data['tag'] ); - $this->assertSame( 'first_name_tag', $tag_data['name'] ); - $this->assertSame( 'Subscriber Info', $tag_data['category'] ); - $this->assertSame( $callback, $tag_data['callback'] ); - $this->assertSame( 'First name of the subscriber', $tag_data['attributes']['description'] ); + $this->assertNotNull( $tag ); + $this->assertSame( 'first_name_tag', $tag->get_name() ); + $this->assertSame( 'first_name', $tag->get_token() ); + $this->assertSame( 'Subscriber Info', $tag->get_category() ); + $this->assertSame( 'Personalized Value', $tag->execute_callback( array(), array() ) ); + $this->assertSame( array( 'description' => 'First name of the subscriber' ), $tag->get_attributes() ); } /** * Try to retrieve a tag that hasn't been registered. */ public function testRetrieveNonexistentTag(): void { - $this->assertNull( $this->registry->get_by_tag( 'nonexistent' ) ); + $this->assertNull( $this->registry->get_by_token( 'nonexistent' ) ); } /** * Register multiple tags and retrieve them. */ public function testRegisterDuplicateTag(): void { - $callback1 = function () { + $callback1 = function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- Callback parameters are required. return 'Value 1'; }; - $callback2 = function () { + $callback2 = function ( $context, $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- Callback parameters are required. return 'Value 2'; }; // Register a tag. - $this->registry->register( 'tag1', 'tag-1', 'Category 1', $callback1 ); + $this->registry->register( new Personalization_Tag( 'tag1', 'tag-1', 'Category 1', $callback1 ) ); // Attempt to register the same tag again. - $this->registry->register( 'tag2', 'tag-2', 'Category 2', $callback2 ); + $this->registry->register( new Personalization_Tag( 'tag2', 'tag-2', 'Category 2', $callback2 ) ); // Retrieve the tag and ensure the first registration is preserved. - $tag_data = $this->registry->get_by_tag( 'tag-1' ); - $this->assertSame( 'tag1', $tag_data['name'] ); - $this->assertSame( 'Category 1', $tag_data['category'] ); - $this->assertSame( $callback1, $tag_data['callback'] ); + $tag = $this->registry->get_by_token( 'tag-1' ); + $this->assertSame( 'tag1', $tag->get_name() ); + $this->assertSame( 'Category 1', $tag->get_category() ); + $this->assertSame( 'Value 1', $tag->execute_callback( array(), array() ) ); } /** @@ -98,8 +101,8 @@ class PersonalizationTagsRegistryTest extends TestCase { }; // Register multiple tags. - $this->registry->register( 'tag1', 'tag-1', 'Category 1', $callback ); - $this->registry->register( 'tag2', 'tag-2', 'Category 2', $callback ); + $this->registry->register( new Personalization_Tag( 'tag1', 'tag-1', 'Category 1', $callback ) ); + $this->registry->register( new Personalization_Tag( 'tag2', 'tag-2', 'Category 2', $callback ) ); // Retrieve all tags. $all_tags = $this->registry->get_all();