PHP DOM in WordPress - add attribute in output buf

2019-08-23 05:55发布

I want to make all input tags on product and cart page with class qty have type="number" and readonly="readonly" attributes through modifying DOM with PHP.

When I limit it to cart and product page, it "only" makes lazy load plugin and Autoptimize plugin stop working for these 2 pages, while other pages are fine. I can't spot any console errors, on admin pages as well.

When I allow it to run for every page, the following happens:

  1. There are no errors in console on product and cart page.
  2. Ajax request for Ajax add to cart plugin breaks.
  3. I get multiple console errors which break layout of many admin pages, moving and hiding portions of content.

These output buffer modifications feel like hacking with resulting errors / incompatibilities. Could it be because of libxml_use_internal_errors(true); which hides warnings and doesn't actually fix anything?

Dom loadHTML doesn't work properly on a server

This is what I have in my functions.php:

add_action( 'template_redirect', 'acau_activate_buffer', 99999 );
function acau_activate_buffer() {
    // cart and product page only, WooCommerce required for below line to work, remove to reproduce issues without WooCommerce      
    if ( ! is_cart() && ! is_product() ) return;
    ob_start();
}

add_action('shutdown', function() {
    // cart and product page only, WooCommerce required for below line to work, remove to reproduce issues without WooCommerce          
    if ( ! is_cart() && ! is_product() ) return;
    $final = '';

    // Collect output from all previous buffers.
    $levels = ob_get_level();

    for ($i = 0; $i < $levels; $i++) {
        $final .= ob_get_clean();
    }

    echo apply_filters('acau_output', $final);

}, -99999);

// Filter final output.
add_filter('acau_output', function($output) {

    $dom = new DOMDocument();
    libxml_use_internal_errors(true);
    $dom->loadHTML(mb_convert_encoding($output, 'HTML-ENTITIES', 'UTF-8'));

    foreach ($dom->getElementsByTagName('input') as $node) {
        $classes = explode (' ', $node->getAttribute('class') );
        if ( in_array ( 'qty', $classes ) ) {
            $node->setAttribute('type', 'number');
            $node->setAttribute('readonly', 'readonly');
        }
    }
    $newHtml = $dom->saveHtml();
    return $newHtml;

});

1条回答
beautiful°
2楼-- · 2019-08-23 06:03

I've found a solution to my issue, based on what Roland from nextendweb posted on WordPress support forums. He filed a bug report:

https://bugs.php.net/bug.php?id=76563

What he suggests would be in my case:

$final .= ob_get_contents();
ob_clean();

instead of:

$final .= ob_get_clean();

because it isn't working the same as it should.

查看更多
登录 后发表回答