|
/ Documentation /Developers Documentation/ Analyze ACF Field Content with SureRank

Analyze ACF Field Content with SureRank

If your theme stores page content in ACF fields and bypasses the_content(), SureRank reads an empty post_content and cannot analyze headings, images, or keyword density. Use this filter to fix that.

Hook: surerank_post_analyzer_content

Description: This filter lets you replace or extend the content string that SureRank’s page SEO analyzer reads. Use it to feed Advanced Custom Fields (ACF) data, custom table values, or any external content source into the analyzer when your theme does not use the default post_content field.

Source: inc/analyzer/post-analyzer.php

Usage

Use this filter when your theme stores page content in ACF fields and bypasses the_content() entirely. Without it, the analyzer reads an empty post_content string and cannot grade headings, images, links, or keyword density.

Note: The analyzer runs XPath queries (//h2, //img, //a[@href]) against the content string. Structural checks only fire when the content contains real HTML tags. Plain text is sufficient for keyword and word-count checks only.

Example

The snippet below appends ACF field content as properly structured HTML so all SEO checks can run against it. Save it as a mu-plugin or add it to your child theme’s functions.php.

<?php
/**
 * Feed ACF field content into SureRank's analyzer as HTML
 * so XPath checks (//h2, //img, //a) can see it.
 *
 * Save as: wp-content/mu-plugins/surerank-acf-bridge.php
 */
add_filter( 'surerank_post_analyzer_content', function ( $content, $post ) {
    if ( ! function_exists( 'get_field_objects' ) ) {
        return $content;
    }

    $fields = get_field_objects( $post->ID );
    if ( empty( $fields ) ) {
        return $content;
    }

    // Map ACF field names to heading levels. Adjust to match your field group.
    $heading_map = [
        'hero_title'    => 'h1',
        'section_title' => 'h2',
        'sub_title'     => 'h3',
    ];

    $extra = [];

    foreach ( $fields as $name => $field ) {
        $value = $field['value'] ?? '';
        $type  = $field['type']  ?? '';

        if ( isset( $heading_map[ $name ] ) && is_string( $value ) && $value !== '' ) {
            $tag     = $heading_map[ $name ];
            $extra[] = '<' . $tag . '>' . esc_html( $value ) . '</' . $tag . '>';
            continue;
        }

        if ( $type === 'wysiwyg' && is_string( $value ) ) {
            $extra[] = $value;
            continue;
        }

        if ( in_array( $type, [ 'text', 'textarea' ], true ) && is_string( $value ) && $value !== '' ) {
            $extra[] = '<p>' . esc_html( $value ) . '</p>';
            continue;
        }

        if ( $type === 'image' && is_array( $value ) && ! empty( $value['url'] ) ) {
            $extra[] = sprintf(
                '<img src="%s" alt="%s" />',
                esc_url( $value['url'] ),
                esc_attr( $value['alt'] ?? '' )
            );
            continue;
        }

        if ( $type === 'link' && is_array( $value ) && ! empty( $value['url'] ) ) {
            $extra[] = sprintf(
                '<a href="%s">%s</a>',
                esc_url( $value['url'] ),
                esc_html( $value['title'] ?? $value['url'] )
            );
            continue;
        }
    }

    return $content . "\n" . implode( "\n", $extra );
}, 10, 2 );

Where to Add the Code

You can add the example code to your child theme’s functions.php file or save it as wp-content/mu-plugins/surerank-acf-bridge.php. Using a mu-plugin is recommended because it stays active regardless of which theme is active.

Available Parameters

  • $content: The current content string passed to the analyzer. By default, this is $post->post_content.
  • $post: The full WP_Post object for the post being analyzed. Use it to branch logic per post type or template.

Required Customization

The $heading_map array must be updated to match your actual ACF field names and their intended heading levels. Without this, the analyzer cannot determine which fields represent h1, h2, or h3 elements, and heading checks will not pass.

// Replace these keys with your actual ACF field names
$heading_map = [
    'hero_title'    => 'h1',
    'section_title' => 'h2',
    'sub_title'     => 'h3',
];

Notes

  • Repeater fields and flexible-content layouts are not walked by this snippet. A have_rows() / get_sub_field() loop tailored to your field group structure is needed for those.
  • ACF WYSIWYG values are stored raw. If your theme uses shortcodes inside WYSIWYG fields, wrap the value with do_shortcode( $value ) before pushing to $extra.
  • All field values are escaped with esc_html, esc_attr, and esc_url before being passed in to prevent malformed HTML from corrupting the DOMDocument parse.
  • The filter runs only when the SEO panel is open or a check is recalculated. There is no impact on the front-end page load.
  • This filter does not affect sitemap generation, schema output, or any other part of SureRank outside of the page SEO analyzer.

Was this doc helpful?
What went wrong?

We don't respond to the article feedback, we use it to improve our support content.

Need help? Contact Support
Table of Contents
Scroll to Top