Repository: ampproject/amp-toolbox-php (php)

Issue:
amp-embed heights attribute being stripped
### Bug Description

When using the ZergNet `<amp-embed>` [defined here](https://github.com/ampproject/amphtml/blob/main/ads/vendors/zergnet.md) in the latest version of the plugin, the `heights` attribute is being stripped. This wasn't an issue previously when using 1.4.2.

https://amp.dev/documentation/guides-and-tutorials/develop/style_and_layout/art_direction/#heights

[Playground Link](https://playground.amp.dev/?mode=Responsive#share=PCFET0NUWVBFIGh0bWw+CjxodG1sIOKaoT4KICA8aGVhZD4KICAgIDxtZXRhIGNoYXJzZXQ9InV0Zi04IiAvPgogICAgPHRpdGxlPk15IEFNUCBQYWdlPC90aXRsZT4KICAgIDxsaW5rIHJlbD0iY2Fub25pY2FsIiBocmVmPSJzZWxmLmh0bWwiIC8+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoIiAvPgogICAgPHN0eWxlIGFtcC1ib2lsZXJwbGF0ZT5ib2R5ey13ZWJraXQtYW5pbWF0aW9uOi1hbXAtc3RhcnQgOHMgc3RlcHMoMSxlbmQpIDBzIDEgbm9ybWFsIGJvdGg7LW1vei1hbmltYXRpb246LWFtcC1zdGFydCA4cyBzdGVwcygxLGVuZCkgMHMgMSBub3JtYWwgYm90aDstbXMtYW5pbWF0aW9uOi1hbXAtc3RhcnQgOHMgc3RlcHMoMSxlbmQpIDBzIDEgbm9ybWFsIGJvdGg7YW5pbWF0aW9uOi1hbXAtc3RhcnQgOHMgc3RlcHMoMSxlbmQpIDBzIDEgbm9ybWFsIGJvdGh9QC13ZWJraXQta2V5ZnJhbWVzIC1hbXAtc3RhcnR7ZnJvbXt2aXNpYmlsaXR5OmhpZGRlbn10b3t2aXNpYmlsaXR5OnZpc2libGV9fUAtbW96LWtleWZyYW1lcyAtYW1wLXN0YXJ0e2Zyb217dmlzaWJpbGl0eTpoaWRkZW59dG97dmlzaWJpbGl0eTp2aXNpYmxlfX1ALW1zLWtleWZyYW1lcyAtYW1wLXN0YXJ0e2Zyb217dmlzaWJpbGl0eTpoaWRkZW59dG97dmlzaWJpbGl0eTp2aXNpYmxlfX1ALW8ta2V5ZnJhbWVzIC1hbXAtc3RhcnR7ZnJvbXt2aXNpYmlsaXR5OmhpZGRlbn10b3t2aXNpYmlsaXR5OnZpc2libGV9fUBrZXlmcmFtZXMgLWFtcC1zdGFydHtmcm9te3Zpc2liaWxpdHk6aGlkZGVufXRve3Zpc2liaWxpdHk6dmlzaWJsZX19PC9zdHlsZT48bm9zY3JpcHQ+PHN0eWxlIGFtcC1ib2lsZXJwbGF0ZT5ib2R5ey13ZWJraXQtYW5pbWF0aW9uOm5vbmU7LW1vei1hbmltYXRpb246bm9uZTstbXMtYW5pbWF0aW9uOm5vbmU7YW5pbWF0aW9uOm5vbmV9PC9zdHlsZT48L25vc2NyaXB0PgogICAgPHNjcmlwdCBhc3luYyBzcmM9Imh0dHBzOi8vY2RuLmFtcHByb2plY3Qub3JnL3YwLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgYXN5bmMgY3VzdG9tLWVsZW1lbnQ9ImFtcC1hZCIgc3JjPSJodHRwczovL2Nkbi5hbXBwcm9qZWN0Lm9yZy92MC9hbXAtYWQtMC4xLmpzIj48L3NjcmlwdD4KICAgIDxzdHlsZSBhbXAtY3VzdG9tPgogICAgICBoMSB7CiAgICAgICAgbWFyZ2luOiAxcmVtOwogICAgICB9CiAgICA8L3N0eWxlPgogIDwvaGVhZD4KICA8Ym9keT4KICAgIDxhbXAtZW1iZWQKICAgICAgd2lkdGg9Ijc4MCIKICAgICAgaGVpZ2h0PSIxMDAiCiAgICAgIGhlaWdodHM9IihtYXgtd2lkdGg6NjQ1cHgpIDEwMCUsIChtYXgtd2lkdGg6ODQ1cHgpIDMxJSwgMjMlIgogICAgICBsYXlvdXQ9InJlc3BvbnNpdmUiCiAgICAgIHR5cGU9InplcmduZXQiCiAgICAgIGRhdGEtemVyZ2lkPSI0MjY1OCIKICAgID4KICAgIDwvYW1wLWVtYmVkPgogIDwvYm9keT4KPC9odG1sPgo=)

### Expected Behaviour

The use of the above `<amp-embed>` includes the proper `heights` attribute.

### Screenshots

<img width="360" alt="Screen Shot 2022-03-28 at 2 34 26 PM" src="https://user-images.githubusercontent.com/346399/160463958-a77ff896-90a1-4b00-81d7-e0ad62e40a90.png">

<img width="926" alt="Screen Shot 2022-03-28 at 2 37 51 PM" src="https://user-images.githubusercontent.com/346399/160464460-319c61d2-a18a-49f1-907d-633479706277.png">

The above is the display of the embed when using the following embed code from the example:

```html
<amp-embed
  width="780"
  height="100"
  heights="(max-width:645px) 100%, (max-width:845px) 31%, 23%"
  layout="responsive"
  type="zergnet"
  data-zergid="42658"
>
</amp-embed>
```

### PHP Version

7.4

### Plugin Version

2.2.1

### AMP plugin template mode

Reader

### WordPress Version

5.9

### Site Health

`
### wp-core ###

version: 5.9.2
site_language: en_US
user_language: en_US
timezone: +00:00
permalink: /%year%/%monthnum%/%day%/%postname%/
https_status: true
multisite: true
user_registration: false
blog_public: 1
default_comment_status: open
environment_type: production
user_count: 1
site_count: 1
network_count: 1
dotorg_communication: true

### wp-active-theme ###

name: Twenty Twenty-Two (twentytwentytwo)
version: 1.1
author: the WordPress team
author_website: https://wordpress.org/
parent_theme: none
theme_features: core-block-patterns, post-thumbnails, responsive-embeds, editor-styles, html5, automatic-feed-links, block-templates, widgets-block-editor, wp-block-styles, editor-style
theme_path: /var/www/wordpress/wp-content/themes/twentytwentytwo
auto_update: Disabled

### wp-plugins-active (1) ###

AMP: version: 2.2.1, author: AMP Project Contributors, Auto-updates disabled

### wp-plugins-inactive (1) ###

Zephr: version: 1.0.0, author: Alley (latest version: 1.0.2), Auto-updates disabled

### wp-media ###

image_editor: WP_Image_Editor_GD
imagick_module_version: Not available
imagemagick_version: Not available
imagick_version: Not available
file_uploads: File uploads is turned off
post_max_size: 50M
upload_max_filesize: 50M
max_effective_size: 50 MB
max_file_uploads: 20
gd_version: 2.3.0
gd_formats: GIF, JPEG, PNG, WebP, BMP, XPM
ghostscript_version: 9.50

### wp-server ###

server_architecture: Linux 5.4.0-90-generic aarch64
httpd_software: nginx/1.18.0
php_version: 7.4.28 64bit
php_sapi: fpm-fcgi
max_input_variables: 1000
time_limit: 30
memory_limit: 128M
admin_memory_limit: 256M
max_input_time: 60
upload_max_filesize: 50M
php_post_max_size: 50M
curl_version: 7.68.0 OpenSSL/1.1.1f
suhosin: false
imagick_availability: false
pretty_permalinks: true

### wp-database ###

extension: mysqli
server_version: 10.4.24-MariaDB-1:10.4.24+maria~focal
client_version: mysqlnd 7.4.28
max_allowed_packet: 134217728
max_connections: 151

### wp-constants ###

WP_HOME: undefined
WP_SITEURL: undefined
WP_CONTENT_DIR: /var/www/wordpress/wp-content
WP_PLUGIN_DIR: /var/www/wordpress/wp-content/plugins
WP_MEMORY_LIMIT: 64M
WP_MAX_MEMORY_LIMIT: 256M
WP_DEBUG: true
WP_DEBUG_DISPLAY: true
WP_DEBUG_LOG: false
SCRIPT_DEBUG: false
WP_CACHE: false
CONCATENATE_SCRIPTS: undefined
COMPRESS_SCRIPTS: undefined
COMPRESS_CSS: undefined
WP_ENVIRONMENT_TYPE: Undefined
DB_CHARSET: utf8
DB_COLLATE: undefined

### wp-filesystem ###

wordpress: writable
wp-content: writable
uploads: writable
plugins: writable
themes: writable

### amp_wp ###

amp_slug_query_var: amp
amp_slug_defined_late: false
amp_mode_enabled: reader
amp_reader_theme: legacy
amp_templates_enabled: post, page
amp_serve_all_templates: This option does not apply to Reader mode.
amp_css_transient_caching_disabled: false
amp_css_transient_caching_threshold: 5000 transients per day
amp_css_transient_caching_sampling_range: 14 days
amp_css_transient_caching_transient_count: 12
amp_css_transient_caching_time_series: 
	20220328: 0
amp_libxml_version: 2.9.12

`

### Gutenberg Version

n/a

### OS(s) Affected

n/a

### Browser(s) Affected

all

### Device(s) Affected

_No response_

### Acceptance Criteria

_No response_

### Implementation Brief

_No response_

### QA Testing Instructions

_No response_

### Demo

_No response_

### Changelog Entry

_No response_

Pull request description:
Fix SSR sizer responsiveness issue
Fixes #511 

This PR fixes the responsiveness issue of the `i-amphtml-sizer` generated by the SSR when we have a heights attribute.

Patch:
diff --git a/src/Optimizer/Transformer/ServerSideRendering.php b/src/Optimizer/Transformer/ServerSideRendering.php
index 14dff1e87..d982bd9e4 100644
--- a/src/Optimizer/Transformer/ServerSideRendering.php
+++ b/src/Optimizer/Transformer/ServerSideRendering.php
@@ -590,9 +590,15 @@ private function maybeAddSizerInto(
         if ($layout === Layout::RESPONSIVE) {
             $elementId = $element->getAttribute(Attribute::ID);
             if (!empty($elementId) && array_key_exists($elementId, $this->customSizerStyles)) {
-                $sizer = $this->createResponsiveSizer($document, $width, $height, $this->customSizerStyles[$elementId]);
+                $sizer = $this->createResponsiveSizer(
+                    $document,
+                    $element,
+                    $width,
+                    $height,
+                    $this->customSizerStyles[$elementId]
+                );
             } else {
-                $sizer = $this->createResponsiveSizer($document, $width, $height);
+                $sizer = $this->createResponsiveSizer($document, $element, $width, $height);
             }
         } elseif ($layout === Layout::INTRINSIC) {
             $sizer = $this->createIntrinsicSizer($document, $width, $height);
@@ -607,6 +613,7 @@ private function maybeAddSizerInto(
      * Create a sizer element for a responsive layout.
      *
      * @param Document  $document DOM document to create the sizer for.
+     * @param Element   $element  Element to add a sizer to.
      * @param CssLength $width    Calculated width of the element.
      * @param CssLength $height   Calculated height of the element.
      * @param string    $style    Style to use for the sizer. Defaults to padding-top in percentage.
@@ -614,18 +621,22 @@ private function maybeAddSizerInto(
      */
     private function createResponsiveSizer(
         Document $document,
+        Element $element,
         CssLength $width,
         CssLength $height,
-        $style = 'padding-top:%s%%'
+        $style = ''
     ) {
         $padding       = $height->getNumeral() / $width->getNumeral() * 100;
         $paddingString = rtrim(rtrim(sprintf('%.4F', round($padding, 4)), '0'), '.');
+        $paddingStyle  = ! $element->hasAttribute(Attribute::HEIGHTS)
+            ? sprintf('padding-top:%s%%', $paddingString)
+            : '';
 
-        $style = empty($style) ? 'display:block' : "display:block;{$style}";
+        $style = "display:block;{$paddingStyle};{$style}";
 
         $sizer = $document->createElement(Amp::SIZER_ELEMENT);
         $sizer->setAttribute(Attribute::SLOT, Amp::SERVICE_SLOT);
-        $sizer->addInlineStyle(sprintf($style, $paddingString));
+        $sizer->addInlineStyle($style);
 
         return $sizer;
     }


Test patch:
diff --git a/tests/Optimizer/Transformer/ServerSideRenderingTest.php b/tests/Optimizer/Transformer/ServerSideRenderingTest.php
index 9d176678b..0fbb9decc 100644
--- a/tests/Optimizer/Transformer/ServerSideRenderingTest.php
+++ b/tests/Optimizer/Transformer/ServerSideRenderingTest.php
@@ -253,7 +253,7 @@ public function dataTransform()
             'heights attribute without amp-custom' => [
                 $input('<amp-img height="256" heights="(min-width: 500px) 200px, 80%" layout="responsive" width="320"></amp-img>'),
                 $expectWithoutBoilerplate(
-                    '<amp-img height="256" layout="responsive" width="320" id="i-amp-0" class="i-amphtml-layout-responsive i-amphtml-layout-size-defined" i-amphtml-layout="responsive"><i-amphtml-sizer slot="i-amphtml-svc" style="display:block;padding-top:80%"></i-amphtml-sizer></amp-img>',
+                    '<amp-img height="256" layout="responsive" width="320" id="i-amp-0" class="i-amphtml-layout-responsive i-amphtml-layout-size-defined" i-amphtml-layout="responsive"><i-amphtml-sizer slot="i-amphtml-svc" style="display:block"></i-amphtml-sizer></amp-img>',
                     '<style amp-custom>#i-amp-0>:first-child{padding-top:80%}@media (min-width: 500px){#i-amp-0>:first-child{padding-top:200px}}</style>'
                 ),
                 [],
@@ -265,7 +265,7 @@ public function dataTransform()
                     '<style amp-custom>body h1{color:red;}</style>'
                 ),
                 $expectWithoutBoilerplate(
-                    '<amp-img height="256" layout="responsive" width="320" id="i-amp-0" class="i-amphtml-layout-responsive i-amphtml-layout-size-defined" i-amphtml-layout="responsive"><i-amphtml-sizer slot="i-amphtml-svc" style="display:block;padding-top:80%"></i-amphtml-sizer></amp-img>',
+                    '<amp-img height="256" layout="responsive" width="320" id="i-amp-0" class="i-amphtml-layout-responsive i-amphtml-layout-size-defined" i-amphtml-layout="responsive"><i-amphtml-sizer slot="i-amphtml-svc" style="display:block"></i-amphtml-sizer></amp-img>',
                     '<style amp-custom>body h1{color:red;}#i-amp-0>:first-child{padding-top:80%}@media (min-width: 500px){#i-amp-0>:first-child{padding-top:200px}}</style>'
                 ),
                 [],
@@ -274,7 +274,7 @@ public function dataTransform()
             'bad heights attribute' => [
                 $input('<amp-img height="256" heights=",,," layout="responsive" width="320"></amp-img>'),
                 // This adds an ID as it stores the CSS to inline before the actual error is detected.
-                $expectWithBoilerplate('<amp-img class="i-amphtml-layout-responsive i-amphtml-layout-size-defined" height="256" heights=",,," i-amphtml-layout="responsive" layout="responsive" width="320"><i-amphtml-sizer slot="i-amphtml-svc" style="display:block;padding-top:80%"></i-amphtml-sizer></amp-img>'),
+                $expectWithBoilerplate('<amp-img class="i-amphtml-layout-responsive i-amphtml-layout-size-defined" height="256" heights=",,," i-amphtml-layout="responsive" layout="responsive" width="320"><i-amphtml-sizer slot="i-amphtml-svc" style="display:block"></i-amphtml-sizer></amp-img>'),
                 [
                     Error\CannotRemoveBoilerplate::fromAttributeThrowingException(
                         InvalidHtmlAttribute::fromAttribute(