From 0b47f910955994f624f01d7f2f7618dd32f3f789 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 10 Mar 2026 12:28:06 +0800 Subject: [PATCH 1/6] Add `get_body_html` method --- src/ConvertKit_API_Traits.php | 38 ++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php index efc67ab..0096ebd 100644 --- a/src/ConvertKit_API_Traits.php +++ b/src/ConvertKit_API_Traits.php @@ -1931,6 +1931,12 @@ public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string continue; } + // Remove element if it's rocket-loader.min.js. Including it prevents landing page redirects from working. + if (strpos($element->getAttribute($attribute), 'rocket-loader.min.js') !== false) { + $element->parentNode->removeChild($element); + continue; + } + // If here, the attribute's value is a relative URL, missing the http(s) and domain. // Prepend the URL to the attribute's value. $element->setAttribute($attribute, $url . $element->getAttribute($attribute)); @@ -1947,15 +1953,29 @@ public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string */ public function strip_html_head_body_tags(string $markup) { - $markup = str_replace('', '', $markup); - $markup = str_replace('', '', $markup); - $markup = str_replace('', '', $markup); - $markup = str_replace('', '', $markup); - $markup = str_replace('', '', $markup); - $markup = str_replace('', '', $markup); - $markup = str_replace('', '', $markup); - - return $markup; + return $this->get_body_html($markup); + } + + /** + * Returns the HTML within the DOMDocument's tag as a string. + * + * @param \DOMDocument $dom DOM Document. + * + * @since 2.1.0 + * + * @return string + */ + public function get_body_html(\DOMDocument $dom) { + + $body = $dom->getElementsByTagName( 'body' )->item( 0 ); + + $html = ''; + foreach ( $body->childNodes as $child ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + $html .= $dom->saveHTML( $child ); + } + + return $html; + } /** From bb9579204688810654d27342af9c1ee026c8df18 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 10 Mar 2026 13:03:34 +0800 Subject: [PATCH 2/6] Move WordPress Libraries Methods to Trait --- src/ConvertKit_API.php | 12 +---- src/ConvertKit_API_Traits.php | 19 ++----- tests/ConvertKitMethodsTest.php | 94 +++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 25 deletions(-) create mode 100644 tests/ConvertKitMethodsTest.php diff --git a/src/ConvertKit_API.php b/src/ConvertKit_API.php index a44401b..4857cfa 100644 --- a/src/ConvertKit_API.php +++ b/src/ConvertKit_API.php @@ -328,21 +328,11 @@ public function get_resource(string $url) $this->convert_relative_to_absolute_urls($html->getElementsByTagName('script'), 'src', $url_scheme_host_only); $this->convert_relative_to_absolute_urls($html->getElementsByTagName('form'), 'action', $url_scheme_host_only); - // Save HTML. - $resource = $html->saveHTML(); - - // If the result is false, return a blank string. - if (!$resource) { - throw new \Exception(sprintf('Could not parse %s', $url)); - } - // Remove some HTML tags that DOMDocument adds, returning the output. // We do this instead of using LIBXML_HTML_NOIMPLIED in loadHTML(), because Legacy Forms // are not always contained in a single root / outer element, which is required for // LIBXML_HTML_NOIMPLIED to correctly work. - $resource = $this->strip_html_head_body_tags($resource); - - return $resource; + return $this->get_body_html($html); } /** diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php index 0096ebd..42ba3e8 100644 --- a/src/ConvertKit_API_Traits.php +++ b/src/ConvertKit_API_Traits.php @@ -1914,8 +1914,12 @@ public function get_segments( */ public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string $attribute, string $url) // phpcs:ignore Squiz.Commenting.FunctionComment.IncorrectTypeHint, Generic.Files.LineLength.TooLong { - // Anchor hrefs. + $nodes = []; foreach ($elements as $element) { + $nodes[] = $element; + } + + foreach ($nodes as $element) { // Skip if the attribute's value is empty. if (empty($element->getAttribute($attribute))) { continue; @@ -1943,19 +1947,6 @@ public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string } } - /** - * Strips , and opening and closing tags from the given markup, - * as well as the Content-Type meta tag we might have added in get_html(). - * - * @param string $markup HTML Markup. - * - * @return string HTML Markup - */ - public function strip_html_head_body_tags(string $markup) - { - return $this->get_body_html($markup); - } - /** * Returns the HTML within the DOMDocument's tag as a string. * diff --git a/tests/ConvertKitMethodsTest.php b/tests/ConvertKitMethodsTest.php new file mode 100644 index 0000000..9bd53f5 --- /dev/null +++ b/tests/ConvertKitMethodsTest.php @@ -0,0 +1,94 @@ +api = new ConvertKit_API(); + } + + /** + * Test the convert_relative_to_absolute_urls() method. + * + * @since 2.4.1 + * + * @return void + */ + public function testConvertRelativeToAbsoluteUrls() + { + // Setup HTML in DOMDocument. + $html = new \DOMDocument(); + $html->loadHTML(' + + + + + + Test + + +
Test
+ + '); + + // Define URL to prepend to relative URLs. + $url_scheme_host_only = 'https://example.com'; + + // Convert relative URLs to absolute URLs for elements we want to test. + $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('a'), 'href', $url_scheme_host_only); + $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('link'), 'href', $url_scheme_host_only); + $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('img'), 'src', $url_scheme_host_only); + $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('script'), 'src', $url_scheme_host_only); + $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('form'), 'action', $url_scheme_host_only); + + // Fetch HTML string. + $output = $html->saveHTML(); + + // Assert string contains expected HTML elements that should not be modified. + $this->assertStringContainsString('', $output); + + // Assert string does not contain HTML elements that should be removed. + $this->assertStringNotContainsString('', $output); + + // Assert string contains expected HTML elements that should be modified. + $this->assertStringContainsString('Test', $output); + $this->assertStringContainsString('', $output); + $this->assertStringContainsString('', $output); + $this->assertStringContainsString('
Test
', $output); + } + + /** + * Test that the get_body_html() method returns the expected HTML. + * + * @since 2.4.1 + */ + public function testGetBodyHtml() + { + $content = '

Vantar þinn ungling sjálfstraust í stærðfræði?

This is a test

'; + $html = new \DOMDocument(); + $html->loadHTML(''.$content.''); + $this->assertEquals($content, $this->api->get_body_html($html)); + } +} From 1e623b9ae249a9fc4fe6046571f94d55f23913c3 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 10 Mar 2026 13:52:42 +0800 Subject: [PATCH 3/6] Add `add_subscriber_to_legacy_form` method --- src/ConvertKit_API_Traits.php | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php index 42ba3e8..fc24aa6 100644 --- a/src/ConvertKit_API_Traits.php +++ b/src/ConvertKit_API_Traits.php @@ -308,6 +308,21 @@ public function add_subscriber_to_form(int $form_id, int $subscriber_id, string ); } + /** + * Adds a subscriber to a legacy form by subscriber ID + * + * @param integer $form_id Legacy Form ID. + * @param integer $subscriber_id Subscriber ID. + * + * @since 2.0.0 + * + * @return WP_Error|array + */ + public function add_subscriber_to_legacy_form(int $form_id, int $subscriber_id) + { + return $this->post(sprintf('landing_pages/%s/subscribers/%s', $form_id, $subscriber_id)); + } + /** * List subscribers for a form * @@ -1944,29 +1959,28 @@ public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string // If here, the attribute's value is a relative URL, missing the http(s) and domain. // Prepend the URL to the attribute's value. $element->setAttribute($attribute, $url . $element->getAttribute($attribute)); - } + }//end foreach } /** * Returns the HTML within the DOMDocument's tag as a string. * * @param \DOMDocument $dom DOM Document. - * - * @since 2.1.0 * - * @return string + * @since 2.1.0 + * + * @return string */ - public function get_body_html(\DOMDocument $dom) { - - $body = $dom->getElementsByTagName( 'body' )->item( 0 ); + public function get_body_html(\DOMDocument $dom) + { + $body = $dom->getElementsByTagName('body')->item(0); $html = ''; - foreach ( $body->childNodes as $child ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $html .= $dom->saveHTML( $child ); + foreach ($body->childNodes as $child) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + $html .= $dom->saveHTML($child); } return $html; - } /** From de20bd804979eb75f8e52a657b14072d35cbb2ac Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 10 Mar 2026 14:34:02 +0800 Subject: [PATCH 4/6] PHPStan compat. --- src/ConvertKit_API.php | 1 - src/ConvertKit_API_Traits.php | 10 ++++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ConvertKit_API.php b/src/ConvertKit_API.php index 4857cfa..e0460ec 100644 --- a/src/ConvertKit_API.php +++ b/src/ConvertKit_API.php @@ -274,7 +274,6 @@ public function refresh_token(string $refreshToken, string $redirectURI) * @param string $url URL of HTML page. * * @throws \InvalidArgumentException If the URL is not a valid URL format. - * @throws \Exception If parsing the legacy form or landing page failed. * * @return false|string */ diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php index fc24aa6..030732d 100644 --- a/src/ConvertKit_API_Traits.php +++ b/src/ConvertKit_API_Traits.php @@ -316,7 +316,7 @@ public function add_subscriber_to_form(int $form_id, int $subscriber_id, string * * @since 2.0.0 * - * @return WP_Error|array + * @return false|mixed */ public function add_subscriber_to_legacy_form(int $form_id, int $subscriber_id) { @@ -1952,7 +1952,9 @@ public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string // Remove element if it's rocket-loader.min.js. Including it prevents landing page redirects from working. if (strpos($element->getAttribute($attribute), 'rocket-loader.min.js') !== false) { - $element->parentNode->removeChild($element); + if ($element->parentNode instanceof \DOMNode) { + $element->parentNode->removeChild($element); + } continue; } @@ -1975,6 +1977,10 @@ public function get_body_html(\DOMDocument $dom) { $body = $dom->getElementsByTagName('body')->item(0); + if (! $body instanceof \DOMElement) { + return ''; + } + $html = ''; foreach ($body->childNodes as $child) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $html .= $dom->saveHTML($child); From 392dac603900c44cfd2309c1fcc7772756ee10a1 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 10 Mar 2026 14:36:52 +0800 Subject: [PATCH 5/6] Coding Standards on Tests --- tests/ConvertKitMethodsTest.php | 68 +++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/tests/ConvertKitMethodsTest.php b/tests/ConvertKitMethodsTest.php index 9bd53f5..5152d1c 100644 --- a/tests/ConvertKitMethodsTest.php +++ b/tests/ConvertKitMethodsTest.php @@ -57,11 +57,31 @@ public function testConvertRelativeToAbsoluteUrls() $url_scheme_host_only = 'https://example.com'; // Convert relative URLs to absolute URLs for elements we want to test. - $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('a'), 'href', $url_scheme_host_only); - $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('link'), 'href', $url_scheme_host_only); - $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('img'), 'src', $url_scheme_host_only); - $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('script'), 'src', $url_scheme_host_only); - $this->api->convert_relative_to_absolute_urls($html->getElementsByTagName('form'), 'action', $url_scheme_host_only); + $this->api->convert_relative_to_absolute_urls( + $html->getElementsByTagName('a'), + 'href', + $url_scheme_host_only + ); + $this->api->convert_relative_to_absolute_urls( + $html->getElementsByTagName('link'), + 'href', + $url_scheme_host_only + ); + $this->api->convert_relative_to_absolute_urls( + $html->getElementsByTagName('img'), + 'src', + $url_scheme_host_only + ); + $this->api->convert_relative_to_absolute_urls( + $html->getElementsByTagName('script'), + 'src', + $url_scheme_host_only + ); + $this->api->convert_relative_to_absolute_urls( + $html->getElementsByTagName('form'), + 'action', + $url_scheme_host_only + ); // Fetch HTML string. $output = $html->saveHTML(); @@ -70,25 +90,43 @@ public function testConvertRelativeToAbsoluteUrls() $this->assertStringContainsString('', $output); // Assert string does not contain HTML elements that should be removed. - $this->assertStringNotContainsString('', $output); + $this->assertStringNotContainsString( + '', + $output + ); // Assert string contains expected HTML elements that should be modified. - $this->assertStringContainsString('Test', $output); - $this->assertStringContainsString('', $output); - $this->assertStringContainsString('', $output); - $this->assertStringContainsString('
Test
', $output); + $this->assertStringContainsString( + 'Test', + $output + ); + $this->assertStringContainsString( + '', + $output + ); + $this->assertStringContainsString( + '', + $output + ); + $this->assertStringContainsString( + '
Test
', + $output + ); } /** - * Test that the get_body_html() method returns the expected HTML. - * - * @since 2.4.1 - */ + * Test that the get_body_html() method returns the expected HTML. + * + * @since 2.4.1 + */ public function testGetBodyHtml() { $content = '

Vantar þinn ungling sjálfstraust í stærðfræði?

This is a test

'; $html = new \DOMDocument(); - $html->loadHTML(''.$content.''); + $html->loadHTML(' + + ' . $content . ' + '); $this->assertEquals($content, $this->api->get_body_html($html)); } } From 5ef3974172a9be957a39da70eeeb0c675530e96d Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Fri, 13 Mar 2026 08:10:28 +0800 Subject: [PATCH 6/6] Update code comment on `convert_relative_to_absolute_urls` --- src/ConvertKit_API_Traits.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php index 030732d..2c7f035 100644 --- a/src/ConvertKit_API_Traits.php +++ b/src/ConvertKit_API_Traits.php @@ -1929,6 +1929,7 @@ public function get_segments( */ public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string $attribute, string $url) // phpcs:ignore Squiz.Commenting.FunctionComment.IncorrectTypeHint, Generic.Files.LineLength.TooLong { + // Store DOMNodeList in array, as iteration stops if a node is modified. $nodes = []; foreach ($elements as $element) { $nodes[] = $element;