iSaumya iSaumya - 3 months ago 14
PHP Question

How to omit counting certain p tags within p tag counting function in wordpress

I need some help with my 2 WordPress function that I use inside my

functions.php
file to push ad codes within the blog article based on paragraph count.

What code do you currently use?

Here is the code that I currently using inside my
functions.php
file:

/*Add ad after 20 paragraph of post if there is more than 21 paragraph*/
add_filter( 'the_content', 'ad_20', 15 );

function ad_20( $content ) {
global $post;
if( check_paragraph_count_blog( $content ) > 21 ) {

$ad_code = '...ad code goes here...';

if ( $post->post_type == 'post' ) {
return prefix_insert_after_paragraph( $ad_code, 20, $content );
}


}
return $content;

}

// Parent Function that makes the magic happen
function prefix_insert_after_paragraph( $insertion, $paragraph_id, $content ) {
$closing_p = '</p>';
$paragraphs = explode( $closing_p, $content );
foreach ($paragraphs as $index => $paragraph) {

if ( trim( $paragraph ) ) {
$paragraphs[$index] .= $closing_p;
}

if ( $paragraph_id == $index + 1 ) {
$paragraphs[$index] .= $insertion;
}
}

return implode( '', $paragraphs );
}

//Check paragraph count on a blog post
function check_paragraph_count_blog( $content ) {
global $post;
if ( $post->post_type == 'post' ) {
$count = substr_count( $content, '</p>' );
return $count;
} else {
return 0;
}
}


What's the problem with your code?

Well, my code works fine without any error but it doesn't follow the full purpose that I want from this code.

What do you want your code to do?

The main problem with the code that I'm currently using and posted above is that both the
prefix_insert_after_paragraph()
function &
check_paragraph_count_blog()
function check for all
p
tags regardless of where they are located. But this is not what I want, I want the following:


  • Don't consider the
    p
    tags present within
    <code>
    ,
    <pre>
    ,
    <code class="some-language-name">
    ,
    <pre class="some-language-name>
    .

  • Also don't consider
    p
    tags present within certain
    div
    tags, like for example
    <div class="callout some-class some-other-class">
    .



What's the problem with those certain
div
tags?


Well, I use several shortcode inside my article to show up some well designed note, callouts etc. Now if the counter consider those divs for counting then it may show up the ads within the shortcode design making the entire look and feel bad.

Sample Paragraph Input

<p>At the time of creating any blog or news based websites most webmasters gives the least amount of importance to the commenting system of their website, without even understanding the importance of it. Eventually comment section of a website is the only place where people interact with the author when they are exited or happy with the article and helps to grow the whole website community. In most cases they end up using some third party commenting system like Disqus or Spot.im etc. without even realizing what a blunder they are making. I’ve seen many websites (both big & popular as well as small websites) using Disqus commenting system, without even realizing the consequences. And by the time you will realize it, your site would have become so big & popular they you can’t take the risk of changing your commenting system. If you are thinking why, keep reading.</p>

<p><a href="I want to omit this P from counting"><img src="I want to omit this p from counting"></a></p>

<p>As creating websites has become very easy now-a-days many non-techy people can make a websites too, but they don’t get the insights of an experienced personal. Before writing this article I’ve used disqus for months to research it thoroughly and at the same time I’ve also tried Spot.im (a new player in this arena) but in both cases I’ve come up with the same conclusion. Never ever use these third party commenting system on your website. Here are the 7 facts about Disqus and similar commenting system for which I will suggest you to stay away from them.</p>


What do you want from us?

I need your help guys. It would be really helpful if someone can provide me a rewritten version of the
prefix_insert_after_paragraph()
and
check_paragraph_count_blog()
function which will do the
p
tag counting and checking by omitting the condition I've described above.

Thank you in advance, looking forward to your help.




Some Update About the Answer Posted Below

The answer posted below works just fine without any problem but please note that it can only be used once. For example if you want to push 3 ads within your blog post and hence created 3 functions like
ad_10()
,
ad_20()
and
ad_30()
, the below code will only work in any one of them. If you put it in more than 1 function within your WordPress
functions.php
you might get blank content. Something to keep in mind.

Answer

Using DOMDocument - and not regexes - you can easily handle the job. The idea is to selecting all p tags that are not within those specific elements or in other words all p tags that doesn't belong to such a parent.

It's all done by a XPath query:

//p[
    not(
        ancestor::div[contains(@class, 'callout') or contains(@class, 'callin')]
            or ancestor::pre
            or ancestor::code
            or a/img       # As per comments
        )
]

If you see you can find that it's a negated query which will look for all p elements which aren't a child of divs with callout or callin classes (you may add more classes following similar syntax), pre or code elements (Note: all pre and code elements)

By the way you don't need any other functions, all things are done in ad_20()

Regexes are not a tool made for this kind of complex situations (HTML parsing). I don't say you can't parse HTML with that. You can but unless you know perfectly what you are doing.

Live demo

add_filter('the_content', 'ad_20', 15);
function ad_20($content) {

    global $post;

    $adCode = '...ad code goes here...';
    // Ad code will be added right after 20th paragraph
    $paragraphNumber = 20;
    // Convert to HTML entities
    $content = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');

    if ($post->post_type == 'post') {
        libxml_use_internal_errors(true);
        // Initializing a new DOM object
        $dom = new DOMDocument;
        // Load HTML content
        $dom->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
        // Initializing a new XPath object
        $xpath = new DOMXPath($dom);
        // Query all `p` tags that their parent is not those specific elements
        $paragraphs = $xpath->query('//p[not(ancestor::div[contains(@class, \'callout\') or contains(@class, \'callin\')] or ancestor::pre or ancestor::code or a/img)]');

        // If we have a number of satisfying paragraphs
        if ($paragraphs->length > $paragraphNumber) {
            // Loading and importing javascript code
            // <span> is important
            $script = '<span>.........code.........</span>';
            $newDom = new DOMDocument;
            $newDom->loadHTML($script, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
            $node = $newDom->getElementsByTagName('span')->item(0);
            $adNode = $dom->importNode($node, true);
            // Add our ad node after `$paragraphNumber`th paragraph
            $paragraphs->item($paragraphNumber)->parentNode->insertBefore($adNode, $paragraphs->item($paragraphNumber));
        }

        libxml_use_internal_errors(false); 
        return $dom->saveHTML();
    }

    return $content;
}
Comments