Debugging in ExpressionEngine

In 2017 I gave a presentation at EECONF in Denver about step debugging in PHP, specifically in the ExpressionEngine CMS, however, I didn’t go into debugging issues in template code. ExpressionEngine is a bit of a different beast than other PHP based content management systems. It has a very easy to learn template language, but it is also a bit nuanced. Templates are string based, and have multiple parsing steps. Low did a great job explaining the parse process in this StackExchange thread. Since templates are parsed, you step debug the PHP that is parsing them, not the template code directly.

Debugging template code is something I do often when developing an ExpressionEngine site myself, or helping a customer debug one of my add-ons. Instead of getting a specific error message, a template tag may simply not be outputting the content they are expecting.

Occasionally I ask them to share the template code they’re using and I get a full template file with a few hundred lines of code. This to me indicates they don’t know where to start debugging the issue themselves. For example, my Publisher add-on is very complex, and one of it’s main functions is to display translated content in the {exp:channel:entries} tag. To do this it uses the channel_entry_query_results hook in ExpressionEngine. It’s very common to have multiple {exp:channel:entries} tags on a page, and each one of them calls the channel_entry_query_results hook. That means the code I need to debug is getting called multiple times.

This is especially an issue when I have to FTP to someone’s site and debug it myself. I can’t remotely step debug (well I can, but that requires server config changes and I can’t ask a customer to do that), so I usually resort to var_dump(); or writing to a local log file. If the issue resides in a single {exp:channel:entries} tag, but there are 2 other {exp:channel:entries} tags on the page, then the code I’m debugging gets called 2 additional times, both of which may be unrelated to the issue I’m debugging. Imagine playing Let’s Make a Deal and you’re presented with 3 doors, and you have to pick the correct door to win the prize, in this case the prize is the bug. Trust me, this isn’t a game a developer wants to play. Spending time choosing and venturing into the wrong doors is wasted time and is often frustrating. Those additional hook calls are just getting in the way and adding noise. As a developer I want to avoid the noise and isolate the issue as quickly as possible.

The solution: Simplify

Remove the two other doors from the equation. Remove everything from the template that is not directly related to the issue you’re trying to solve. This mean removing the header, footer, navigation, and possibly even all JavaScript and CSS code. Break it down to the bare necessities to replicate the bug.

What do you do if you don’t know which template code is the problem child? Start removing chunks of code until you can isolate which area, or template tag specifically is identified as the one causing the problem.

Here is a use case from a support ticket I recently had. The customer had 3 channels: Pages, Publications, and Documents. The Page entry was using a Bloqs field to display an entry from the Publications channel, which itself had a relationship field to another entry in the Documents channel. Then the documents channel had a custom file field which contained a PDF. They were also using Publisher to display all of this as translated content in two different languages. The bug in this case was that the file field in the Document entry would always display the default language value english.pdf. I first tried to debug the issue with the template code they provided:

{exp:channel:entries entry_id="3"}
    <h1>{title}</h1>
    {bloqs_field}
        {related_publication_bloq}
            <h2>{title}</h2>
            {related_document}
                <h3>{related_document:title}</h3>
                file: {related_document:file}
            {/related_document}
        {/related_publication_bloq}
    {/bloqs_field}
{/exp:channel:entries}

Note that the provided code is already very simple. The customer took the time, at my request, to setup a brand new ExpressionEngine environment with only the bare necessities to re-create the issue. He removed the noise. This was extremely helpful to me and is something I ask of customers quite often.

After a bit of step debugging (they sent me a full copy of the test site to run locally) I was still not sure what the cause of the problem was. I had spent nearly an hour trying to figure it out. To simplify the issue I broke the template tag above into 3 separate {exp:channel:entries} tags. After all each {related_ field is just calling another entry. So I broke the template out into the following, which is essentially doing exactly the same thing as the previous code snippet, but in 3 separate {exp:channel:entries} tag.

<!-- Page -->
{exp:channel:entries entry_id="3"}
    <h1>{title}</h1>
{/exp:channel:entries}

<!-- Publication -->
{exp:channel:entries entry_id="2"}
    <h2>{title}</h2>
    {related_document}
        <h3>{related_document:title}</h3>
        file: {related_document:file}
    {/related_document}
{/exp:channel:entries}

<!-- Document -->
{exp:channel:entries entry_id="1"}
    <h2>{title}</h2>
    file: {file}
{/exp:channel:entries}

With all three of the entries on the page at once I could see which was working and which was not, and it was immediately apparent that the {file} field was not printing the correct value. I then commented out the first two {exp:channel:entries} tags and step debugged the 3rd one. Since it was the only {exp:channel:entries} tag on the page with no {related_ fields I didn’t have to deal with extra debugging noise and got to the source of the problem in a couple minutes. Even if you’re not step debugging simplifying templates lets you hone in on problem areas, thus getting you to the solution faster.

Brian Litzinger's avatar
Brian Litzinger

BoldMinded has been a trusted name in ExpressionEngine add-on development for over 12 years.

Comments 0

Be the first to comment!