Sometimes developers just get little things wrong that cost you!

Here’s a problem I ran into recently…

On just about every page-load the page was always taking 2 to 3 seconds to load. It seemed to happen on every page, every post, everywhere—and it was getting annoying. They couldn’t figure out what could be causing it and so they came to me to figure it out…

After looking at their site I couldn’t tell either right off-the-bat—I knew I was going to have to get xDebug out and find the culprit the hard way. Since the issue was a substantial amount of time, I figured it would show up in a cachegrind/profile file—so I got to work…

I cloned the site locally and enabled the profiling trigger in my browser…

Screenshot of xDebug Profile option

FYI, this is how I have my xDebug configured to do low-level profiling:

[xdebug] ; xDebug 3 Config

; === Modes & Behavior ===
xdebug.mode=debug,develop,profile,trace             ; Enables debugging, development aids, profiling, and tracing
xdebug.start_with_request=trigger                   ; Starts only when the special GET/POST or COOKIE trigger is set

; === Client Settings ===
xdebug.discover_client_host=yes                     ; Automatically detects the client IP (e.g., in Docker setups)
xdebug.client_host=localhost                        ; Fallback: where to connect the debugger if discovery fails
xdebug.client_port=9022                             ; Port to connect to the debugging client

; === Output Settings ===
xdebug.output_dir=/Users/aubreypwd/Xdebug/          ; Directory to store all generated output files

; === Profiler ===
xdebug.profiler_output_name = %H.%R.cachegrind.out  ; Custom name format for profiler output files
xdebug.profiler_compression = 0                     ; Disables compression for profiler output

; === Traces ===
xdebug.trace_output_name = %H.%R.trace              ; Custom name format for function trace files
xdebug.trace_format = 1                             ; Format 1: machine-readable for flamegraphs and analysis

; === Logging ===
xdebug.log_level=7                                  ; Disables logging (use 7 for full verbosity)
xdebug.log=/Users/aubreypwd/Xdebug/xdebug.log       ; File to store xDebug internal logs

; === Other ===
xdebug.use_compression = false                      ; Disable zlib compression of debugger protocol messages

…this results in RAW cachegrind and trace files to a folder on my system:

Screenshot of a cachegrind file.

We can open these up in a tool like QCacheGrind and examine what’s taking so long…

Screenshot of using QCacheGrind to examine a long running process.

Just by doing a little digging through the stack, we start to see the culprits:

  • Penci_Pinterest->get_board_name_pins()
    • wp_remote_get()

If we look at that code:

public function get_board_name_pins( $username, $cache_time = 1200 ) {

	$output = array();

	$cache_key = 'penci_pinterest_' . strtolower( $username );

	$pinterest_cache = get_transient( $cache_key );

	if ( ! $pinterest_cache ) {

		$params = array(
			'timeout'    => 60,
			'sslverify'  => false,
			'headers'    => array( 'Accept-language' => 'en' ),
			'user-agent' => 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0'
		);

		$response = wp_remote_get( 'https://www.pinterest.com/' . $username, $params );

		if ( ! is_wp_error( $response ) ) {
			$request_result = wp_remote_retrieve_body( $response );

			preg_match_all( '/jsInit1\'>(.*)<\/script>/', $request_result, $matches );

			if ( ! empty( $matches[1] ) && count( $matches[1] ) ) {
				$pinterest_json = json_decode( $matches[1][0], true );

				if ( ! isset( $pinterest_json['resourceDataCache'][1]['data']['board_feed'] ) ) {
					$output['error'] = esc_html__( 'The pinterest data is not set, please check the ID', 'soledad' );
				} elseif ( isset( $pinterest_json["resourceDataCache"][0]['data']['type'] ) && $pinterest_json["resourceDataCache"][0]['data']['type'] !== 'board' ) {
					$output['error'] = esc_html__( 'Invalid pinterest data for  ' . $username . ' please check the user/board_id', 'soledad' );
				} else {
					$output = (array) $pinterest_json['resourceDataCache'][1]['data']['board_feed'];
				}
			}

			set_transient( $cache_key, $output,  $cache_time );
		}
	} else {
		$output = $pinterest_cache;
	}

…we can see we are making a wp_remote_get() request to www.pinterest.com, which upon using xDebug to examine, always returns an empty array() after about 2-ish seconds—a slow API call. Furthermore, and more importantly, the transient cache we are storing stores the empty array(), which the if ( ! $pinterest_cache ) test sees as falsy the next time the widget loads, resulting in this request happening over and over even if the API returns no data the first time. The coders here obviously didn’t understand that an empty [] array is seen as falsy, so no data means: we keep re-trying over and over—and if the API is slow that leads to a compounded problem on all pages. 

So—how did I get it fixed?

Well, the client wasn’t too fond of just ditching the widget: it served them well after-all—except for this problem! So, here’s some custom-built code I came up with to solve the issue:

<?php
/**
 * Plugin Name:       Soledad Pinterest Widget Performance Fix
 * Plugin URI:        https://aubreypwd.com
 * Description:       This fixes the Soledad theme's Pinterest widget from making HTTP requests to pinterest.com on every pageload.
 * Version:           1.0.0
 * Author:            Aubrey Portwood
 * Author URI:        https://aubreypwd.com
 * Copyright:         (c) Aubrey Portwood 2025
 */

namespace aubreypwd\wp_extend\plugins\soledad_pinterest_widget_fix;

if (
	file_exists( sprintf( '%s/themes/soledad/inc/widgets/pinterest_widget.php', WP_CONTENT_DIR ) )
		&& 'soledad' === wp_get_theme()->get( 'Name' )
) {

	/**
	 * Disable repeat wp_remote_get requests for Penci_Pinterest::get_board_name_pins().
	 *
	 * This disables repeat `wp_remote_get()` requests due to the caching of `[]`
	 * in a transient being seen as falsy in the original code, resulting
	 * in a repeat request each pageload even though `[]` was cached.
	 *
	 * @param mixed $default This should be false, true, or a response.
	 * @param mixed $params  Array a parameters for the request.
	 * @param mixed $url     The URL we are making the request to.
	 *
	 * @return mixed We will make the request ourselves and cache the response properly,
	 *               we will return `\WP_Error` and skip any repeat requests, or we will return
	 *               `$default` when the request isn't our pinterest request, or we will
	 *               return a response if we think the request is legit.
	 */
	function stop_repeat_request( $default, $params, $url ) {

		if ( ! strstr( wp_debug_backtrace_summary(), 'Penci_Pinterest->get_board_name_pins' ) ) {
			return $default; // The request isn't being made by the function we're concerned with.
		}

		if ( ! strstr( $url, 'https://www.pinterest.com/' ) ) {
			return $default; // Not a request to pinterest.com.
		}

		$skip_transient_key = 'penci_pinterest_skip_request';

		if ( 'skip' === get_transient( $skip_transient_key ) ) {
			return new \WP_Error(); // Skip request until our time expires.
		}

		if ( true === isset( $params['__do_not_perform_request'] ) ) {
			return $default; // Allow the below attempt to complete (recursive).
		}

		// Make the request...
		$response = wp_remote_get(
			$url,
			array_merge(
				$params,

				// Tells this function not to make another request (above).
				[ '__do_not_perform_request' => true ]
			)
		);

		// Try and find what wp-content/themes/soledad/inc/widgets/pinterest_widget.php:get_board_name_pins() is looking for...
		preg_match_all( '/jsInit1\'>(.*)<\/script>/', wp_remote_retrieve_body( $response ), $matches );

		// Nothing found...
		if ( empty( $matches[1] ) ) {

			// Skip the next attempt for X seconds, nothing was found (don't repeat request).
			set_transient( $skip_transient_key, 'skip', DAY_IN_SECONDS * 30 );

			// Nothing was found, skip this time and sub-sequent requests for X seconds...
			return new \WP_Error();
		}

		// Something was found pass back the request for pinterest_widget.php to cache the data.
		return $response;
	}
	add_filter( 'pre_http_request', sprintf( '\%s\stop_repeat_request', __NAMESPACE__ ), 10, 3 );
}

It’s a simple plugin—they can easily deactivate once they fix this—that injects itself in the process and uses a secondary cache to store and test the data properly. Sure, it makes the request to the slow API once, but it tests the result—an empty array()—properly, and stops the repeat requests on each page-load until the cache expires after 30 days.

Sometimes plugin developers just get things wrong—that’s a natural part of extending your WordPress websites. Expect it. Most people have to give up on the things developers build because of a simple performance issues like this. With an expert you can get customized solutions to simple specific problems like this though.

Hope this helps!