yiisoft / yii2

Yii 2: The Fast, Secure and Professional PHP Framework
http://www.yiiframework.com
BSD 3-Clause "New" or "Revised" License
14.24k stars 6.91k forks source link

Javascript registered in renderDynamic doesn't appear anywhere when PageCache is used #17673

Closed nagyt234 closed 4 years ago

nagyt234 commented 4 years ago

JS or CSS registered with registerJS, registerJSFile etc will not work in renderDynamic. This problem causes, that it is not possible to use any widgets in renderDynamic on a cached page which has JavaScript or CSS assets. This issue is basically #16051 re-opened with simple step-step reproducing.

What steps will reproduce the problem?

  1. Create a YII2 system with the Basic Template and go there:
    composer create-project --prefer-dist yiisoft/yii2-app-basic basic
    cd basic
  2. Add the following line as line 2 in views/site/index.php:
    $this->renderDynamic('$this->registerJs("alert(\'JS started\')");');
  3. Start the simple WEB server
    php yii serve --port=8888
  4. Go in browser to http://localhost:8888 Te alert box will appear, and you can find the following lines also in the page source:
    <script src="<script>jQuery(function ($) {
    alert('JS started')
    });</script>
  5. Now add PageCache to the Index action. Add the following code in the function behaviors in controllers/SiteController.php:
    'indexCache' => [ 
    'class' => 'yii\filters\PageCache',
    'only' => ['index'],
    'duration' => 86400,    // 1 day
    'enabled' => true,
    ],
  6. Check again the Home page. The JS code and the alert IS NOT INCLUDED anymore.

What is the expected result?

The registered JavaScript should appear in the HTML page.

What do you get instead?

All Javascript registered in renderDynamic is missing.

Additional info

Q A
Yii version 2.0.29
PHP version PHP 7.2.24-1+ubuntu16.04.1+deb.sury.org+1
Operating system Ubuntu 16.04
samdark commented 4 years ago

I confirm the issue. It is about the order of things done. PageCache takes already rendered page with dynamic placeholders such as <![CDATA[YII-DYNAMIC-1]]> and evaluates corresponding dynamic expressions and replaces placeholders with evaluation results. Layout rendering doesn't happen at all so registerJs has no effect.

Potentially it could be solved in web\View::renderBodyEndHtml() and alike methods by returning through renderDynamic() instead of directly. That would result in massive increase of evaluated expressions though making page cache not so effective.

Overall I suspect it's not a good idea to try solving it in Yii 2 but I could be wrong.

samdark commented 4 years ago

@yiisoft/reviewers what do you think?

schmunk42 commented 4 years ago

I'd not change the default behavior for Yii 2.x (as always :smiley:) - if available, it should be optional.

samdark commented 4 years ago

Overall it looks like a massive change in behavior so I agree it's not a good idea to solve it like that.

@nagyt234 I'm closing the issue as "won't fix" but if there's a way I can't see please let me know.

nagyt234 commented 4 years ago

OK, I agree, that to solve this issue generally is very difficult and would decrease the effectiveness of the cache. But than at least the documentation of renderDynamic should mention, that the functions register... have no effect. What could be quite easy, if we would have a 2nd set of functions, like register...Dynamic which could work also with placeholders and with the limitation that assets already on the cached page are not analyzed.

samdark commented 4 years ago

Where would you document that? Actually too many methods that indirectly modify layout do not work with dynamic rendering. Basically everything except meta-tags.

nagyt234 commented 4 years ago

@samdark I would write exactly what you wrote here in [the description of renderDynamic](https://www.yiiframework.com/doc/api/2.0/yii-base-view#renderDynamic()-detail) and probably in The Definitive Guide to Yii 2.0:

Methods that indirectly modify layout (registerJS, registerJSFile, etc.) do not work with dynamic rendering. Basically everything except meta-tags.