Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass entire Reader mode templates through post-processor #3781

Merged
merged 28 commits into from
Nov 20, 2019

Conversation

westonruter
Copy link
Member

@westonruter westonruter commented Nov 16, 2019

Summary

  • Eliminate majority of Reader mode template's unique rendering path, rendering them with the same processing logic in AMP_Theme_Support. This ensures the entire documents are sanitized and all CSS is tree-shaken.
  • Use template_include filter to load a template which initializes the AMP_Post_Template class while output buffering is being done. This ensures themes which extend the Reader mode templates and make use of $this may continue to do so.
  • Deprecate functions and AMP_Content class which are no longer used.
  • Ensure validation error reporting (compatibility tool) remains disabled in Reader mode (for now).
  • Move deprecated functions to deprecated.php.
  • Fix viewing Reader template in Customizer.

Fixes #2202.
Fixes #688.

Checklist

  • My pull request is addressing an open issue (please create one otherwise).
  • My code is tested and passes existing tests.
  • My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).

Eliminate majority of Reader mode template's unique rendering path.
amp.php Outdated
@@ -449,6 +446,7 @@ function amp_force_query_var_value( $query_vars ) {
* @return void
*/
function amp_maybe_add_actions() {
_deprecated_function( __FUNCTION__, '1.5' );
Copy link
Member Author

Choose a reason for hiding this comment

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

The deprecated functions here should be moved to includes/deprecated.php (which interestingly is not being included anywhere!)

Also, it would be good to move all of the non-bootstrapping functions in amp.php into includes/amp-helper-functions.php. This will allow us to use PHP>5.2 in these functions.

Copy link
Collaborator

@schlessera schlessera Nov 18, 2019

Choose a reason for hiding this comment

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

which interestingly is not being included anywhere!

Hehe, does that mean that the current contents of that file can be removed ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, probably. The file was introduced in 9750909 by yours truly but I forgot to include it. So much backwards-incompatibility breakage that didn't happen to anyone!

Copy link
Member Author

Choose a reason for hiding this comment

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

@westonruter
Copy link
Member Author

The AMP_Content class can be hard-deprecated once we have the new API established in #3662.

if ( ! empty( $theme_support['template_dir'] ) ) {
self::add_amp_template_filters();
} elseif ( $is_reader_mode ) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Instead of having a separate condition for reader mode, it may be good to combine with add_amp_template_filters. The thinking is we may want to go ahead and try to load a template as if it is in Transitional mode, but then if we are about to load a single.php or page.php that contains $this, then we can at that point load it via AMP_Post_Template. This will avoid necessarily doing the current hard-coding of single.php and page.php as seen in:

$template = is_page() || $wp_query->is_posts_page ? 'page' : 'single';
$this->load_parts( [ $template ] );

This would allow Reader templates to make use of the full template hierarchy, and ease transition to Transitional templates.

In other words, if someone does:

add_theme_support( 'amp', [ 'template_dir' => 'amp' ] );

This should either allow the templates in the amp directory to be classic AMP_Post_Template-based templates, or regular WordPress theme templates. We can start phasing out the use of AMP_Post_Template, only loading it when we detect it is needed.

In fact, there shouldn't be any real difference between Reader mode and Transitional mode when a template_dir is present.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of detecting $this inside of a template (which requires parsing the entire template file), we could also look into wrapping all templates in a compatibility wrapper object by default, as regular WordPress templates don't use $this. However, I'm not 100% sure whether that cleanly works across global state (which is expected in regular WordPress templates).

Copy link
Member Author

Choose a reason for hiding this comment

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

As opposed to parsing the entire template file, I was intending to just use a simple check to see if '$this->' occurs inside of the file. If so, it could then load it via the AMP_Post_Template. Otherwise, it could do normal include.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You'll still have to read the entire file to find a potential $this. And this reading is separate from includeing the file, which you'll need to do as well, and which would normally happen via opcache and not hit the filesystem, so this adds more filesystem reads.

The compatibility wrapper I suggested would be read once and wrapped around each include. So most of the time, everything would still stay within the opcache. However, it adds complexity.

Copy link
Member Author

@westonruter westonruter Nov 19, 2019

Choose a reason for hiding this comment

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

Doesn't file_get_contents() use caching as well? Per docs:

file_get_contents() is the preferred way to read the contents of a file into a string. It will use memory mapping techniques if supported by your OS to enhance performance.

I'm not sure what these “memory mapping techniques” entail. Update: I suppose memory-mapped files.

In any case, we're doing file_get_contents() for other files with each page load, for example:

<?php echo file_get_contents( AMP__DIR__ . '/assets/css/amp-default.css' ); // phpcs:ignore WordPress.WP.AlternativeFunctions ?>

Why would this be inherently different?

In core I see get_post_embed_html() uses file_get_contents() if SCRIPT_DEBUG is enabled. Also, file_get_contents() is used by load_script_translations() in core.

Copy link
Member Author

Choose a reason for hiding this comment

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

In any case, migrating Reader mode templates to use a normal theme templates is beyond the scope of this PR.

@@ -111,23 +111,24 @@ public function __construct( $post ) {
$this->data = [
Copy link
Member Author

Choose a reason for hiding this comment

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

Instead of assembling all of this data in the constructor, I think it would be better to lazily construct it upon invoking the getter the first time. This would allow us to construct the AMP_Post_Template preemptively without potentially it ever being used (and thus avoiding wasted queries), such as when normal WordPress templates are being used in the amp theme directory.

We could then still pass the AMP_Post_Template object in the existing hooks like amp_post_template_head, and as long as no hooks call the getter, then the data is not fetched.

Copy link
Collaborator

Choose a reason for hiding this comment

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

A "fun" way of doing that (which doesn't require conditionals all over the place) is to not define the $data property at all in the class and then use __get() to add it the first time it is being requested (through whatever means). After that, it is just present, so the __get() won't be hit a second time.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this is what I intended to say. It's just in this class that logic would be in \AMP_Post_Template::get().

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in c74ba97

amp.php Outdated
@@ -667,6 +668,7 @@ function amp_render() {
* @global WP_Query $wp_query
*/
function amp_render_post( $post ) {
_deprecated_function( __FUNCTION__, '1.5' );
Copy link
Member Author

Choose a reason for hiding this comment

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

We need to check if these functions are being used in themes/plugins, and if so, potentially defer this hard deprecation until we have the AMP middleware rendering stack available to use instead (same for AMP_Content).

Copy link
Member Author

Choose a reason for hiding this comment

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

templates/html-end.php Outdated Show resolved Hide resolved
templates/html-start.php Outdated Show resolved Hide resolved
includes/class-amp-theme-support.php Outdated Show resolved Hide resolved
includes/class-amp-theme-support.php Outdated Show resolved Hide resolved
includes/templates/class-amp-content.php Outdated Show resolved Hide resolved
@@ -111,23 +111,24 @@ public function __construct( $post ) {
$this->data = [
Copy link
Collaborator

Choose a reason for hiding this comment

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

A "fun" way of doing that (which doesn't require conditionals all over the place) is to not define the $data property at all in the class and then use __get() to add it the first time it is being requested (through whatever means). After that, it is just present, so the __get() won't be hit a second time.

amp.php Outdated
@@ -449,6 +446,7 @@ function amp_force_query_var_value( $query_vars ) {
* @return void
*/
function amp_maybe_add_actions() {
_deprecated_function( __FUNCTION__, '1.5' );
Copy link
Member Author

Choose a reason for hiding this comment

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

amp.php Outdated
@@ -639,6 +637,7 @@ function amp_add_post_template_actions() {
* @deprecated This function is not used when 'amp' theme support is added.
*/
function amp_prepare_render() {
_deprecated_function( __FUNCTION__, '1.5' );
Copy link
Member Author

Choose a reason for hiding this comment

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

amp.php Outdated
@@ -649,6 +648,8 @@ function amp_prepare_render() {
* @deprecated This function is not used when 'amp' theme support is added.
*/
function amp_render() {
_deprecated_function( __FUNCTION__, '1.5' );
Copy link
Member Author

Choose a reason for hiding this comment

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

One relevant usage: https://wpdirectory.net/search/01DT14Z9DAB3X0VQWZGJPG7XA3

It's in Facebook Instant Articles & Google AMP Pages by PageFrog. However, this plugin has been closed: https://wordpress.org/plugins/pagefrog/

amp.php Outdated
@@ -667,6 +668,7 @@ function amp_render() {
* @global WP_Query $wp_query
*/
function amp_render_post( $post ) {
_deprecated_function( __FUNCTION__, '1.5' );
Copy link
Member Author

Choose a reason for hiding this comment

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

@kienstra
Copy link
Contributor

kienstra commented Nov 20, 2019

Looks good!

Hi @westonruter,
This looks good. It's great to have the post-processor working for the entire Reader mode templates.

For example, with:

add_action(
	'amp_post_template_head',
	static function() {
		?>
		<script>disallowed();</script>
		<?php
	}
);

This removed the script:
script-removed-as-expected

...though before this PR, there was a validation error on the front-end (in the Chrome extension).

Also, the style[amp-custom] looks good:

tree-shaking-done

A basic testing post looked good. It was the same with this PR, and on the develop branch:

testing-here

unset( $wp_query->query_vars[ amp_get_slug() ] );
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice, it's great to see amp.php simplified.

includes/class-amp-theme-support.php Outdated Show resolved Hide resolved
* @return string Template dir.
*/
private function get_template_dir() {
static $template_dir = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Good idea

*
* @param AMP_Post_Template $this
*/
do_action( 'amp_post_template_head', $this );
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice addition of DocBlocks for so many actions that didn't have them before.

@kienstra
Copy link
Contributor

The e2e failures don't look to be related to this PR at all. They're all for Stories.

…ader-mode-templates

* 'develop' of github.com:ampproject/amp-wp:
  Ensure Stories preview meets minimum viewport requirements. (#3797)
  Fix expanded menu when screen width is <1000px for Twenty Twenty theme (#3790)
  Ignore test utils from code coverage
Comment on lines -324 to +346
$amp_content = new AMP_Content(
post_password_required( $this->post ) ? get_the_password_form( $this->post ) : $this->post->post_content,
amp_get_content_embed_handlers( $this->post ),
amp_get_content_sanitizers( $this->post ),
[
'content_max_width' => $this->get( 'content_max_width' ),
]
);
/** This filter is documented in wp-includes/post-template.php */
$content = apply_filters( 'the_content', get_the_content( null, false, $this->post ) );
Copy link
Member Author

Choose a reason for hiding this comment

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

This introduced a regression. Prior to this change, posts having content with page breaks (<!--nextpage-->) would have their pages all displayed together. This needs to be restored, with a big reason being that there has been no multipage pagination in the Reader mode template. It's also not consistent with the previous behavior.

Copy link
Member Author

Choose a reason for hiding this comment

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

Workaround plugin to restore the previous behavior: https://gist.github.com/westonruter/559c7a9cbb9460d1099c3d5d9bfa5ad1

Copy link
Member Author

Choose a reason for hiding this comment

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

Fix: #4547

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Signed the Google CLA
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Send entire templates in Reader/Classic mode through the sanitizers CSS in Reader mode is not minified
4 participants