Blog posts under the debugging tag https://webdevstudios.com/tags/debugging/ WordPress Design and Development Agency Mon, 15 Apr 2024 16:05:41 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.2 https://webdevstudios.com/wp-content/uploads/2022/07/cropped-wds-icon.white-on-dark-60x60.png Blog posts under the debugging tag https://webdevstudios.com/tags/debugging/ 32 32 58379230 Debugging WordPress Using Xdebug, Local, and VS Code https://webdevstudios.com/2022/10/06/debugging-wordpress/ https://webdevstudios.com/2022/10/06/debugging-wordpress/#comments Thu, 06 Oct 2022 16:00:51 +0000 https://webdevstudios.com/?p=25376 This post covers debugging WordPress using Xdebug, Local, and VS Code. This approach to debugging is far superior to logging variables to the error log or to the screen. Getting things set up correctly can be tricky. That’s where this tutorial helps. Xdebug Xdebug is a powerful tool for debugging PHP applications. It conveniently comes Read More Debugging WordPress Using Xdebug, Local, and VS Code

The post Debugging WordPress Using Xdebug, Local, and VS Code appeared first on WebDevStudios.

]]>
This post covers debugging WordPress using Xdebug, Local, and VS Code. This approach to debugging is far superior to logging variables to the error log or to the screen.

Getting things set up correctly can be tricky. That’s where this tutorial helps.

Xdebug in action

Xdebug

Xdebug is a powerful tool for debugging PHP applications. It conveniently comes bundled with Local.

We’re using VS Code for our code editor in this tutorial, as well as some other tools I’ve listed below. Everything included here is the current release and version numbers are noted just in case it might be helpful since things change.

Prerequisites

  • Local v6.4.2+6012: A local WordPress development environment, formally known as Local by Flywheel
      • PHP v7.4.1/v7.3.5/v8.0: Selectable via the Local UI. Choose v7.4.1 or v 7.3.5 for now because PHP 8 has a different Xdebug setup we’ll cover at the end of the article.
  • VS Code v1.70.2: Code editor
  • PHP Debug by Xdebug for VS code v1.27.0: VS Code extension which allows VS Code to talk to Xdebug
  • Xdebug Helper browser extension: Used for explicitly enabling or disabling debugging sessions

I use Windows because that’s what makes me happy, but this guide applies to other environments as well. I just wanted to note that, since the paths I’ll be referencing are Windows-y.

Create a Site in Local

Once all of the prerequisites have been installed and activated, you’ll need to create a new WP site in Local if you don’t have one already. When PHP Versions 7.3.x or 7.4.x are selected in Local, Xdebug Version 2 is used.

When PHP Version 8 is selected, Xdebug Version 3 is used. We’re going to use PHP Version 7.4.1 to start off with and will cover PHP Version 8 at the end of the article.

I’ve named my site xdebugvscode.test. Here are its settings:

Local site settings

Set Up php.ini.hbs Directives

Now that the site has been created, let’s double-check the Xdebug configuration directives in the site’s php.ini.hbs file. This file is located under your site’s conf/php directory.

For me, the path is D:devlocal-sitesxdebugvscodeconfphpphp.ini.hbs. Scroll to the bottom of the file under the [xdebug] section, and verify that you have the directives below set accordingly. If you need to make any changes, be sure to restart your Local site.

; Enable remote debugging.
xdebug.remote_enable=1

; If enabled, the xdebug.remote_host setting is ignored and
; Xdebug will try to connect to the client that made the HTTP request.
; It checks the $_SERVER['HTTP_X_FORWARDED_FOR'] and $_SERVER['REMOTE_ADDR'] variables to find out remote IP
xdebug.remote_connect_back=Off

; Set the port that Xdebug connects to.
xdebug.remote_port="9000"

; Disable the profiler.
xdebug.profiler_enable=0

; Set the profiler's output directory for when we're profiling.
xdebug.profiler_output_dir = "D:/dev/xdebug-profiler-output"

A Note About the php.ini.hbs File When Switching PHP Versions

If you ever change the PHP version for your Local site, Local will save the settings for the previous install of PHP under the {local-site-dir}/conf/php/php-{version-number}directory.

For example, I started out with PHP Version 7.4.1 installed, then switched to PHP Version 7.3.5. Local made a copy of my PHP Version 7.4.1 settings under D:devlocal-sitesxdebugvscodeconfphp-7.4.1.

If you change PHP versions and made any modifications to Local’s php.ini.hbs file, you’ll need to make the changes again, if this is the first time the PHP version has been activated. After that, if you swap between PHP versions, your setting changes will be preserved.

Ensure Xdebug Is Running

To make sure that Xdebug is running, create a file named pi.php in the root of your site and add the following code to it:

<?php
// Output information about PHP's configuration.
phpinfo();

location of the ph.php file

Now load up https://yoursite.test/pi.php and make sure that Xdebug is running. Find the Xdebug section; it will look something like this:
php_info() output
If you’re not seeing this, then Xdebug is not running. Double-check the php.ini.hbs file to ensure that Xdebug is enabled.

Activate Xdebug Helper Browser Extension

Once you’ve installed and activated the Xdebug Helper extension for your preferred browser, make sure to enable debugging by clicking on the icon and selecting Debug. The little bug icon will turn green letting you know that the browser extension will set the appropriate flag to turn on debugging.

Enable the Xdebug helper extension

Open Local Site in VS Code and Save a Workspace

Once the site has been created, go to File > Open Folder in VS Code, then navigate to the site’s directory. Now, save the workspace by going to File > Save Workspace As… and then give the workspace a name.

Install and Activate PHP Debug Extension for VS Code

The PHP Debug extension for VS Code helps to bring everything together. Locate the extension in VS Code’s Extension Browser, and activate it. Make sure to select the one published by the Xdebug team, since there are a few extensions with the same name.

PHP debug extension for VS Code

 

Create a launch.json File for Your Workspace

Next, we will create a launch.json file for our workspace. Click on VS Code’s debugging icon to open the debugging panel, then click on the create a launch.json file link.

Create launch.json step 1

Now select the name of your workspace which you created earlier.

Create launch.json step 2

VS Code will populate the launch.json file with defaults, but we’re going to replace them.

Create launch.json step 3

Go ahead and delete the entire contents of the default launch.json, replace the contents with the settings below, then save the file.

The launch.jsonwill be stored at {local-site-name}/.vscode/launch.json. So, mine is saved at D:devlocal-sitesxdebugvscode.vscodelaunch.json.

 

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for Xdebug",
      "type": "php",
      "request": "launch",
      "port": 9000,
      "log": false,
      "maxConnections": 1, // @see  https://github.com/xdebug/vscode-php-debug/issues/604
      "xdebugSettings": {
        "resolved_breakpoints": "0", // @see https://github.com/xdebug/vscode-php-debug/issues/629 and https://stackoverflow.com/a/69925257/3059883
        "max_data": 512,
        "show_hidden": 1,
        "max_children": 128
      }
    }
  ]
}

When using Local’s PHP Version 7.3.x or 7.4.x, the version of Xdebug in use will be 2.9.0, which had a bug that caused some weirdness with breakpoints. I’ve added some links to the config file above which go into some more detail if you’re interested.

Check out the PHP Debug extension’s Github page for more information about supported settings.

Add a Breakpoint to Your Code

For the purposes of this debugging WordPress demo, I’m going to add a breakpoint to line 48 of  Twenty Twenty-Two’s functions.php file.

To add a breakpoint, hover over the gutter (just to the left of a line number) and a little orange dot will appear. Click it to add a breakpoint.

Click it again to remove it. You can add multiple breakpoints, and the debugger will stop on each one. To keep things simple here, I’ll just add the one.

Adding a breakpoint

Once your breakpoint has been added, you will see it listed in the lower left-hand corner of  VS Code’s debugger panel. It’s also possible to set breakpoints on Notices, Warnings, Errors, etc., but we’ll uncheck all of those for this example.

Debugger breakpoints

Start Debugging WordPress

We’re finally ready to start debugging WordPress! Open VS Code’s debugger panel and click the green triangle to start listening for Xdebug. Use the F5 shortcut.
Start debugging
Once VS Code is listening for Xdebug, the Debugger Controls window will be displayed. The bottom of the VS Code window will turn orange.

Listening for debugger

Now Xdebug is waiting to start the session. To kick things off, just load a page that will hit the breakpoint.

I’m just going to load the homepage of the test site I created. Once I hit refresh on the homepage of the test site, VS Code will be highlighted in the taskbar and the debugger will stop executing code on the breakpoint that I had previously set.

Once a debugging session has started, additional controls will be active on the Debugger Control Panel, and variables will be listed under the VARIABLES section of the Debugging Panel.

Inside a debugging session

We can now use the Debugger Controls to navigate through the codebase:

Debugger Controls

  • Pause/Run (F6): Resumes code execution. Debugger will stop on the next breakpoint if set.
  • Step Over (F10): Steps over a block of code.
  • Step Into (F11): Steps into a block of code.
  • Step Out (Shift + F11): Steps out of a block of code that was previously stepped into.
  • Restart (Ctrl + Shift + F5): Restarts the debugging session.
  • Stop (Shift+F5): Halts execution of the script.

The GIF at the top of this article demonstrates some basic usage of these controls.

Exploring Variables

We can also explore the values of local and global variables using the VS Code’s VARIABLES section within the Debugging Panel.

Exploring variables

PHP Version 8 + Xdebug Version 3 Set Up

When a Local site is configured to use PHP Version 8.X, the bundled version of Xdebug will be Version 3, and there are many changes from Versions 2 to 3.

These changes are covered thoroughly in this guide. Notably, Xdebug Version 3 defaults to port 9003 instead of 9000. I’m going to switch my Xdebug Version 3 setup to use port 9000 for consistency, so I don’t have to change anything in my launch.json file.

Here are the Xdebug directives in my PHP Version 8 site’s php.ini.hbs file. Note that I’ve commented out the line xdebug.start_with_request.

xdebug.mode=debug
xdebug.client_port="9000"
; xdebug.start_with_request=yes
xdebug.discover_client_host=yes

Now that we’ve switched our site to PHP Version 8 if we check the output of our pi.php file that we created earlier, we can see that Xdebug Version 3 is now loaded, where Version 2.9.0 was being used under our PHP Versions 7.3 and 7.4 sites.

Xdebug version 3

One of the cool features that comes bundled with Xdebug Version 3 is the addition of the xdebug_info() function. This is similar to the php_info() function, but it provides more detailed diagnostics for about the Xdebug setup.

To use it, simply add a file to your site’s web root. For example, xdebug-info.php , then add the following contents:

<?php
xdebug_info();

Load that file up in your browser, and you’ll see something like this:

xdebug_info output - debugger not listening

In the screenshot above, the diagnostics log is indicating a problem connecting to the host on port 9000. The reason for this is that I have not enabled VS Code to listen for debugging.

So, I tap F5 to enable debugging, and now when I reload my xdebug-info.php, the Diagnostics Log is clean, and I’m ready to debug. Thanks to fellow WebDevStudios Backend Engineer, Biplav Subedi, for this tip!

xdebug_info output - debugger is listening

Troubleshooting

One common issue that comes up when you’re debugging WordPress is that the debugger does not stop on the breakpoint that you’ve set. This is typically caused by a port conflict—some other process is already using the port that you’ve configured for debugging.

Make sure that no other application is using the port that was configured in the php.ini.hbs and launch.json file. If an application is already using the port you’ve specified, make sure to change your port to something that’s not in use.

To check for a port conflict, end your debugging session in VS Code then run the following command:

Windows CMD: netstat -an | find "9000"

Linux/Mac: sudo netstat -nap | grep ":9000"

You should not see any output after entering these commands. If you do, that means that a process is already using the port you’ve configured for Xdebug (9000) in our example. So you should either configure a different port in your php.ini.hbs and launch.json files, or locate the process that is tying up the port, and stop it/configure it to use a different port.

Bonus: Quick and Dirty Debugging

Sometimes, instead of using Xdebug, I prefer to simply log variables to WP’s error.log file using PHP’s error_log().

I find this handy for real quick stuff, though it’s really not the best way to debug WordPress when things get serious. For this setup, first, add the following statements to your site’s wp-config.php file:

define( 'WP_DEBUG', true ); // Debugging mode activated.
define( 'WP_DEBUG_LOG', true ); // Logs to wp-content/debug.log
define( 'WP_DEBUG_DISPLAY', false ); // Don't add errors to the output.
define( 'SCRIPT_DEBUG', true ); // Load un-minified scripts.

Now, errors will be logged to your site’s wp-content/debug.log file. I typically keep this file open in a side panel of VS Code.

error logging to debug.log

I usually use this one-liner to output values to the debug.log file:  error_log( '$variable_name ' . var_export( $variable_name, true ) );

To make things easier, I trigger this one-liner using a VS Code User Snippet with the trigger logv. Here’s a copy of the debugging-related snippets I use. These are configured in VS Code under File > Preferences >  Configure User Snippets > PHP.

{
// Place your snippets for php here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// "Print to console": {
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"Log var_export()": {
"prefix": "logv",
"body": ["error_log( '$1 ' . var_export( $2, true ) );"],
"description": "Do a var_export( $variable ) to PHP error log."
},
"Log print_r()": {
"prefix": "logp",
"body": ["error_log( print_r( $0, true ) );"],
"description": "Do a print_r( $variable ) to PHP error log."
},
"Exit with var_dump()": {
"prefix": "xvd",
"body": ["exit( var_dump( $0 ) );"],
"description": "Exit with var_dump()"
},
"Exit with print_r()": {
"prefix": "xpr",
"body": ["exit( print_r( $0 ) );"],
"description": "Exit with print_r()"
},
"Detailed Error Log": {
"prefix": "logd",
"body": [
"error_log( print_r( (object)",
"t[",
"tt'line' => __LINE__,",
"tt'file' => __FILE__,",
"tt'dump' => [",
"ttt$0,",
"tt],",
"t], true ) );"
],
"description": "Detailed error logging"
},
"Log gettype()": {
"prefix": "logt",
"body": ["error_log( gettype( $0 ) );"],
"description": "Do a gettype( $variable ) to PHP error log."
},
"Debug Filters": {
"prefix": "logf",
"body": ["add_action( 'all', function() { error_log( var_export( current_filter(), true ) ); } );"],
"description": "Logs current filter."
}
}

Conclusion

That’s about it for this post on debugging WordPress using Xdebug, Local, and VS Code. Using Xdebug, you’ll be able to more efficiently and effectively debug code. If you’ve never used it before, I’m sure you will find that Xdebug is a helpful tool in your development arsenal.

The post Debugging WordPress Using Xdebug, Local, and VS Code appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2022/10/06/debugging-wordpress/feed/ 6 25376
Debugging WordPress with Local by Flywheel, (neo)vim, and xdebug https://webdevstudios.com/2019/04/16/debugging-wordpress-with-local-by-flywheel-neovim-and-xdebug/ https://webdevstudios.com/2019/04/16/debugging-wordpress-with-local-by-flywheel-neovim-and-xdebug/#respond Tue, 16 Apr 2019 16:00:53 +0000 https://webdevstudios.com/?p=20143 Introduction Local by Flywheel is a popular tool for getting quick and reliable WordPress installations running on your computer. One of its lesser-known features is the inclusion of xdebug support. xdebug is a PHP extension that allows for real-time debugging of PHP code. Today, we’re going to go over how to get both of these Read More Debugging WordPress with Local by Flywheel, (neo)vim, and xdebug

The post Debugging WordPress with Local by Flywheel, (neo)vim, and xdebug appeared first on WebDevStudios.

]]>
Introduction

Local by Flywheel is a popular tool for getting quick and reliable WordPress installations running on your computer. One of its lesser-known features is the inclusion of xdebug support. xdebug is a PHP extension that allows for real-time debugging of PHP code. Today, we’re going to go over how to get both of these working together within vim to provide a powerful interface that allows for some successful debugging of WordPress.

Prerequisites

Firstly, this tutorial is intended for users of vim or neovim. If you aren’t familiar with how to use vi-like editors, but want to learn, I would recommend running vimtutor in your command line and learning how to move and work in vim before continuing with this post.

Next, you’ll need Local by Flywheel. You can download it here. If you haven’t set up a site before with Local, it is pretty straightforward. You can find a guide for getting started on Flywheel’s blog.

Setup

vim

For vim or neovim, henceforth referred to simply as “vim” (with distinctions for neovim if necessary), you will need the Vdebug plugin. Installation instructions are included on the plugin’s page. I use vim-plug to manage plugins; so my .vimrc looks like this:

""" Vim-Plug
call plug#begin()
Plug 'vim-vdebug/vdebug'
call plug#end()

After adding Vdebug, we’ll need to add some configuration. Before we continue, let’s get Local configured for xdebug.

Local and xdebug

Local ships with the xdebug extension available but not enabled. Each site that you want to debug will need its configuration altered to work with debugging. First, find your Local site site’s directory. In Local, you can view this on the Overview tab, under “Site Path.”

Open your site’s directory, and find the file conf/php/<your site's PHP version>/php.ini. Open this file with any text editor and find the section titled [Xdebug]. This contains the relevant configuration we’re looking for. After modifying this section, your section should look similar to:

xdebug.remote_enable=1
xdebug.remote_connect_back=On
xdebug.remote_port="9000"
xdebug.profiler_enable=1
xdebug.remote_autostart=true
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_host=<your local IP>

Note that the remote_host argument should be your computer’s local IP. This will allow the Local machine to talk to your editor.

Save your modifications, and restart your site in Local.

Configuring vim

Open your vim configuration file. Typically, for vim, this will be ~/.vimrc, in NeoVim, your configuration is usually in ~/.config/nvim/init.vim. Add a new section to your vimrc that looks like this:

let g:vdebug_options= {
    "port" : 9000,
    "server" : '',
    "timeout" : 20,
    "on_close" : 'detach',
    "break_on_open" : 0,
    "ide_key" : '',
    "path_maps" : {
        '/app/public/': '<path to your project public folder>'
    },
    "debug_window_level" : 0,
    "debug_file_level" : 0,
    "debug_file" : "",
    "watch_window_style" : 'compact',
    "marker_default"     : '⬦',
    "marker_closed_tree" : '▸',
    "marker_open_tree" : '▾'
}

Note that you need to fill in the path_map item with your local project’s public folder. If your project is located in /Users/you/Local/myproject/, your public folder would be in /Users/you/Local/myproject/app/public/. Also note that the left-hand side points to /app/public/. This is where Local’s server stores your public files. The right-hand side points to the location of the files as you see them in your filesystem.

Now, save your vim configuration and restart vim (you can probably source the configuration, but Vdebug can be a little weird sometimes). Open a file in your project. Let’s try wp-config.php to start. Find a line with code before wp-settings.php is included and press <F10> on your keyboard.

The line should change color. In my editor, it shows a green line where I set the breakpoint:

Now, press <F5> and you should see a message in the status line saying that “Vdebug will wait for a connection in the background.” Visit any page in your site, and you should see Vdebug connect and pause on the line you set as a breakpoint:

Troubleshooting

Here are some tips if you’re having trouble with Vdebug:

  • If the debugger is attaching and then stopping, verify the path_maps configuration.
  • If the debugger is not attaching, verify your Xdebug configuration in your php.ini.

Working with the Debugger

Author note: the following code takes place in the twentynineteen theme. The premise of this exercise is ridiculous, and meant to be an example of how to use the debugging functionality.

Vdebug includes a number of default key mappings, as well as some commands to help evaluate your code. Open your theme’s functions.php and add the following:

add_filter( 'the_content', function( $content ) {
    if ( is_admin() ) {
        return $content;
    }

    return str_rot13( $content );
} );

Now, travel to a single post on your site’s frontend, and view a post:

Whoops, seems something has gone wrong! Let’s suppose this code is buried somewhere deep in our included files; it could be a pain to find the source of this issue. With xdebug, we can trace the code back to our bad filter—the one that we “totally don’t know about…”

First, open up template-parts/content/content-single.php, and find the line calling the_content:

23         the_content(
24             sprintf(
25                 wp_kses(
26                     /* translators: %s: Name of current post. Only visible to screen readers */
27                     __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'twentynineteen' ),
28                     array(
29                         'span' => array(
30                             'class' => array(),
31                         ),
32                     )
33                 ),
34                 get_the_title()
35             )
36         );

Since we know our issue is in the content being output, move your cursor on the line with the call to the_content and press <F10> on your keyboard. The line should highlight to indicate that you have set a breakpoint.

Before continuing, it’s a good time to review some of Vdebug‘s key mappings from the Quick Guide section of the README:

<F5>: start/run (to next breakpoint/end of script)
<F2>: step over
<F3>: step into
<F4>: step out
<F6>: stop debugging (kills script)
<F7>: detach script from debugger
<F9>: run to cursor
<F10>: toggle line breakpoint
<F11>: show context variables (e.g. after "eval")
<F12>: evaluate variable under cursor
:Breakpoint <type> <args>: set a breakpoint of any type (see :help VdebugBreakpoints)
:VdebugEval <code>: evaluate some code and display the result
<Leader>e: evaluate the expression under visual highlight and display the result

We will primarily be focused on <F5>, <F3>, <F4>, <F9>, and <F10>. Remember, if you get lost, you can always press <F6> to quit the debugger and then start again.

Next, to start the debugger, press <F5>. You should see the following message in your status line: “Vdebug will wait for a connection in the background.” Reload the page in your browser and you should be presented with the debugging window.

The debugger should halt on our breakpoint. From here, we’ll want to “step into” the function calls to decipher what’s going on using <F3>. When we first step in, you’ll notice we’re not inside the_content, but __. What’s going on? PHP executes methods from the innermost function call to the outermost. Since our breakpoint is actually on a set of nested function calls, we’ll need to press <F4> to step out” of each function until we get to the_content:

Once we’re in the content, we can see a call to apply_filters. Knowing how WordPress works with Actions and Filters, it’s a safe bet that our content is being modified in a filter. Move your cursor to the line calling apply_filters, and press <F9> to allow the program to run until we get to that line.

Next, press <F3> to step into apply_filters. I’ll tell you, this function is a little confusing. We just want to worry about the line that actually calls the filter methods:

    $filtered = $wp_filter[ $tag ]->apply_filters( $value, $args );

Put your cursor on that line and press <F9> again, and then “step in” with <F3>. As you enter the apply_filters method, bear in mind that the crux of WordPress hooks are callbacks. With that in mind, it’s safe to say the following lines are where the magic happens:

282                 // Avoid the array_slice if possible.
283                 if ( $the_['accepted_args'] == 0 ) {
284                     $value = call_user_func_array( $the_['function'], array() );
285                 } elseif ( $the_['accepted_args'] >= $num_args ) {
286                     $value = call_user_func_array( $the_['function'], $args );
287                 } else {
288                     $value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int)$the_['accepted_args'] ) );
289                 }

Lines 284, 286, and 288 all use call_user_func_array in some form or fashion. Place your cursor on each line and press <F10> to add a breakpoint, and then hit <F5> to continue execution. The debugger will begin breaking on one of those three lines.

The Watch Window

Vdebug displays a window where you can watch the variables that are available in various scopes. By default, the “Locals” scope is shown.

This is a window just like any other vim window. Move your cursor to the Locals window, and scroll down until you find the $the_ array (hint: you can search with normal vim commands); expand it by using the <Enter> key. Next, you should look at the value of $the_["function"]. Notice that this will either be an array or a string. Expand the array or view the string with each run of <F5>, looking for anything that may stick out as odd.

Did you find it? There’s a Closure. Since we can’t see what it does here, we’ll need to “step in” again with <F3>. The debugger now drops us into the offending method—the filter we added earlier calling str_rot13.

Wrapping Up

With this ridiculous example, I hope you are able to use xdebug in your own projects to effectively debug. Understanding the WordPress codebase is crucial to being an effective developer, and getting down and dirty with a debugger is a great way to learn how truly complex a system can be.

Tips

  • Vdebug can be fickle – you may need to hit <F6> a couple of times to close the debugger completely.
  • Use :help Vdebug and read the documentation to learn about things like <leader>e.
  • Use project-specific .vimrc files to set up your Vdebug paths:

In the following examples, use .vimrc for vim and init.vim for neovim:

" In your ~/.vimrc or ~/.config/nvim/init.vim
set exrc " see :help exrc

" At the BOTTOM of your RC file:
set secure

This will allow you to set up a configuration for each project. vim with search for either .vimrc, _vimrc, or .exrc in your current directory and load it if found. For neovim, the file names are .nvimrc, _nvimrc, and .exrc. I tend to work from the wp-content folder to make use of tags for my plugins and themes. Depending on where you start your editing, place your RC file in the appropriate location and add your vdebug_options configuration in there. Here’s mine in ~/src/WDS/local/lab/app/public/wp-content/.nvimrc:

" Local 'Lab' {{{
let g:vdebug_options= {
    "port" : 9000,
    "server" : '',
    "timeout" : 20,
    "on_close" : 'detach',
    "break_on_open" : 0,
    "ide_key" : '',
    "path_maps" : {
        '/app/public/': '/Users/phatsk/src/WDS/local/lab/app/public/'
    },
    "debug_window_level" : 0,
    "debug_file_level" : 0,
    "debug_file" : "",
    "watch_window_style" : 'compact',
    "marker_default"     : '⬦',
    "marker_closed_tree" : '▸',
    "marker_open_tree" : '▾'
}
" }}}

Now, I don’t have to worry about resetting things in my init.vim whenever I switch projects!

Actual Conclusion

You made it! In this post you (hopefully) learned:

  • How to enable xdebug in your Local installations
  • Install the Vdebug plugin for neo/vim
  • Effectively use Vdebug to track down bugs in code.

Happy bug squashing!

The post Debugging WordPress with Local by Flywheel, (neo)vim, and xdebug appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2019/04/16/debugging-wordpress-with-local-by-flywheel-neovim-and-xdebug/feed/ 0 20143
Debugging WordPress Core: Actions and Filters https://webdevstudios.com/2019/01/22/debugging-wordpress-core-actions-filters/ https://webdevstudios.com/2019/01/22/debugging-wordpress-core-actions-filters/#comments Tue, 22 Jan 2019 17:00:39 +0000 https://webdevstudios.com/?p=19386 Most WordPress developers are familiar with the concept of actions and filters. At the very heart of WordPress, these hooks allow developers to extend the functionality of WordPress in numerous ways. Whether you want to run a process when a post is saved, add a new section to the Edit User page, or modify the Read More Debugging WordPress Core: Actions and Filters

The post Debugging WordPress Core: Actions and Filters appeared first on WebDevStudios.

]]>
Most WordPress developers are familiar with the concept of actions and filters. At the very heart of WordPress, these hooks allow developers to extend the functionality of WordPress in numerous ways. Whether you want to run a process when a post is saved, add a new section to the Edit User page, or modify the SQL used when querying the database, WordPress has hooks for (almost) everything.

One thing I’ve noticed a lot, as a frequent user of the WordPress StackExchange, is that many developers don’t know where to start when trying to figure out which actions or hooks might be available to them. In this blog post, I want to help walk through the process of tracking down various hooks with examples of when you might want to use them and how to implement them.

Actions and Filters: What’s the Difference?

This is a very basic mistake I see often when helping people figure out their WordPress issues. Say for example someone wants to modify the post before editing, but they can’t figure out why their code isn’t doing anything. Well, let’s take a look at a basic example that makes a post’s content all-uppercase:

add_action( 'content_edit_pre', function( $post_content ) {
	return strtoupper( $post_content );
} );

Pretty straightforward, right?

GIF animation of a panicked man in front of a blazing fire.

On the surface, this looks like a proper filter but it will never work. The reason is that WordPress makes a distinction between actions and filters. Actions are assigned via add_action, while filters are assigned via add_filter. The corresponding methods to call these are do_action and apply_filters, respectively. Under the hood, there’s actually not much difference. In fact, add_action calls add_filter. Here’s the full source code from WordPress:

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
    return add_filter($tag, $function_to_add, $priority, $accepted_args);
}

Kind of crazy, right?

It’s because add_filter and add_action do roughly the same thing at the core level, with one minor exception: the returned value of apply_filters can be used to modify existing data structures, while do_action returns literally nothing (void in PHP).

So, our above example will never return any value to modify the content, as do_action simply doesn’t do that. While these differences may make you want to ask, “Why even have different methods?” the distinction is very important.

Actions are for when you want something to happen as a result of something else happening, while filters are used to modify data at run time. Our above example will work with the exact same code, with one minor modification:

add_filter( 'content_edit_pre', function( $post_content ) {
	return strtoupper( $post_content );
} );

[Edit: thanks @Anton Serednii for the correction!]
In WordPress-speak, we use the term “hook” to refer to actions and filters interchangeably, as they are roughly the same thing. When talking about a specific hook, we use either action or term. (Example: “You want to use the admin_enqueue_scripts action to add scripts to WP Admin,” or, “You can use the body_class filter to add additional CSS classes to a page’s <body> tag.”)

Finding the Right Hook

WordPress has a lot of hooks to use. I mean, a lot. A rough count of the WordPress codebase puts the number of hooks called at around 2,744. That’s a lot of hooks!

A GIF animation of Dustin Hoffman as Captain Hook.

 

So, how do you find the right one? Well, you can refer to the action and filter references linked above (and also check out the Plugin API landing page), but those references cover everything and, as we just discussed. That’s a lot of things.

Furthermore, some of the hooks are still undocumented to this day in the Codex. In some cases, the best way is to identify the method that you want to hook into and then check it out in the source code. Additionally, you might learn some interesting things about the hooks once you dive into where they are called from.

Example 1: The save_post Action

The save_post action is triggered by wp_insert_post and wp_publish_post. Both methods are defined in wp-includes/post.php. If we dig into the source code, we’ll first find this definition of save_post:

/**
 * Fires once a post has been saved.
 *
 * @since 1.5.0
 *
 * @param int $post_ID Post ID.
 * @param WP_Post $post Post object.
 * @param bool $update Whether this is an existing post being updated or not.
 */
do_action( 'save_post', $post_ID, $post, $update );

What’s interesting here is that directly above it we can also see this:

/**
 * Fires once a post has been saved.
 *
 * The dynamic portion of the hook name, `$post->post_type`, refers to
 * the post type slug.
 *
 * @since 3.7.0
 *
 * @param int $post_ID Post ID.
 * @param WP_Post $post Post object.
 * @param bool $update Whether this is an existing post being updated or not.
 */
do_action( "save_post_{$post->post_type}", $post_ID, $post, $update );

That’s neat! We can actually target save_post for our own specific post type. This saves a few lines of doing a check like this:

if ( 'my_custom_post_type' !== $post->post_type ) {
    return;
}

Another important note when it comes to hooks is not to hook something that might bite you later. For example, save_post is a great action to use when you want to do something after a post saves. For instance, you might want to record a new post entry for your publish_log custom post type that tracks when posts are published. Let’s take a look at an example:

function maybe_create_publish_log( $post_id, $post ) {
    if ( 'publish' !== $post->post_status ) {
        return;
    }

    wp_insert_post( [
        'post_type' => 'publish_log',
        'post_author' => $post->post_author,
        'post_status' => 'publish',
        'post_content' => "New post {$post->post_title} published on {$post->post_date}.",
        // etc...
    ] );
}

add_action( 'save_post', 'maybe_create_publish_log', 10, 2 );

At first glance, this seems fine. But remember earlier when we learned that save_post is called by wp_insert_post? With this code, you might find yourself creating tons of  publish_log posts because your hook to save_post is being called over and over by wp_insert_post. So, how do you get around this?

The answer is the aptly named remove_action (if you guessed that its sister function is remove_filter and that remove_action simply calls remove_filter, you get a cookie). Let’s take a look at our code now:

function maybe_create_publish_log( $post_id, $post ) {
    if ( 'publish' !== $post->post_status ) {
        return;
    }

    remove_action( 'save_post', 'maybe_create_publish_log' );
    wp_insert_post( [
       'post_type' => 'publish_log',
       'post_author' => $post->post_author,
       'post_status' => 'publish',
       'post_content' => "New post {$post->post_title} published on {$post->post_date}.",
        // etc...
    ] );
    add_action( 'save_post', 'maybe_create_publish_log', 10, 2 );
}

add_action( 'save_post', 'maybe_create_publish_log', 10, 2 );

This is one of the benefits to diving into the core files: if you look up where your action is called from, you will know if you’re about to get into a publishing loop, or avoid other possible “gotchas” when developing your plugins and themes.

Example 2: Modifying the Edit User Screen

Recently at WebDevStudios, we’ve had a couple of clients that needed specific sections added to the user profile page. You may be looking to integrate a service, such as Medium, to your user accounts, or you may want to give administrators the ability to modify fields specific to a plugin. But where do you start?

If you look in the action reference, well, you might be looking for a long time. You’d eventually find what you’re looking for, but there is an easier way.

First, on a WordPress install, you’ll notice you’re at this page when editing a User: /wp-admin/user-edit.php?user_id=3. If you’re editing your own user, you’ll find that you’re at /wp-admin/profile.phpYou might be thinking, “Oh geez, I have to dig into two files to find the action I want?!”

But fear not, because upon opening profile.php you’ll see it’s actually rather simple:

<?php
/**
 * User Profile Administration Screen.
 *
 * @package WordPress
 * @subpackage Administration
 */

/**
 * This is a profile page.
 *
 * @since 2.5.0
 * @var bool
 */
define('IS_PROFILE_PAGE', true);

/** Load User Editing Page */
require_once( dirname( __FILE__ ) . '/user-edit.php' );

Well, that saves us some time. Now, if you dig into user-edit.php, you’re going to be looking for calls to do_action. Let’s do a quick check with ag – the silver searcher:

  ag do_action wp-admin/user-edit.php

Which gives us the following output:

132: do_action( 'personal_options_update', $user_id );
141: do_action( 'edit_user_profile_update', $user_id );
230: do_action( 'user_edit_form_tag' );
285: do_action( 'admin_color_scheme_picker', $user_id );
347:do_action( 'personal_options', $profileuser );
362: do_action( 'profile_personal_options', $profileuser );
658: do_action( 'show_user_profile', $profileuser );
667: do_action( 'edit_user_profile', $profileuser );

Now, let’s dig into the actual file itself. Odds are we want to be looking at edit_user_profile specifically:

if ( IS_PROFILE_PAGE ) {
	/**
	 * Fires after the 'About Yourself' settings table on the 'Your Profile' editing screen.
	 *
	 * The action only fires if the current user is editing their own profile.
	 *
	 * @since 2.0.0
	 *
	 * @param WP_User $profileuser The current WP_User object.
	 */
	do_action( 'show_user_profile', $profileuser );
} else {
	/**
	 * Fires after the 'About the User' settings table on the 'Edit User' screen.
	 *
	 * @since 2.0.0
	 *
	 * @param WP_User $profileuser The current WP_User object.
	 */
	do_action( 'edit_user_profile', $profileuser );
}

You’ll notice that I copied out the surrounding if conditional. As well, you might notice something familiar on the first line: the if ( IS_PROFILE_PAGE ) check lets you hook in just on your own user page, or only on other users’ pages, or both by a combination of both actions.

With these actions, we can render a custom set of fields to modify the user edit page and bring custom functionality right into the core of WordPress. While the Codex’s action list is quite extensive and sometimes difficult to navigate, it is a wonderful resource for finding additional information on most actions and filters once you know what to look for. Have a peek at the page for edit_user_profile, for example.

Example 3: Modifying Query SQL with Filters

This is a slightly more advanced example, but an important one. WordPress does a lot of things under the hood to construct the MySQL queries that ultimately find your posts, tags, etc. Let’s start with an example WP Query from get_posts:

$args = [
    'post_type'      => 'post',
    'posts_per_page' => 5,
    'orderby'        => 'post_date',
    'order'          => 'ASC',
    's'              => 'Test',
];

$posts = get_posts( $args );

The resulting SQL looks something like this:

SELECT
    wp_posts.ID
FROM
    wp_posts
WHERE 1=1
AND (((wp_posts.post_title LIKE '%Test%') OR (wp_posts.post_excerpt LIKE '%Test%') OR (wp_posts.post_content LIKE '%Test%')))
AND (wp_posts.post_password = '')
AND wp_posts.post_type = 'post'
AND ((wp_posts.post_status = 'publish'))
ORDER BY wp_posts.post_date ASC
LIMIT 0, 5

This query, although simple, is filtered several times before it is passed to the database for record retrieval. A quick aside—the “%” signs may be encoded as long hashes when you view the query, as a placeholder to help with how $wpdb parses placeholders.

Next, we’ll step through some of the more useful filters used. The following code can be found in wp-includes/class-wp-query.php. 

posts_where

This is one of the earliest filters on the query, and filters the WHERE clause of the query. For the above example, this filter’s parameters are the WHERE clause as the first parameter, and a reference to the query object as the second. The WHERE clause looks like this:

    [0] =>  AND (((wp_posts.post_title LIKE '{67feda5925d0533a58f19e45c96ff1c761ddaa2263949b4ae0023a90b3f25b1f}Test{67feda5925d0533a58f19e45c96ff1c761ddaa2263949b4ae0023a90b3f25b1f}') OR (wp_posts.post_excerpt LIKE '{67feda5925d0533a58f19e45c96ff1c761ddaa2263949b4ae0023a90b3f25b1f}Test{67feda5925d0533a58f19e45c96ff1c761ddaa2263949b4ae0023a90b3f25b1f}') OR (wp_posts.post_content LIKE '{67feda5925d0533a58f19e45c96ff1c761ddaa2263949b4ae0023a90b3f25b1f}Test{67feda5925d0533a58f19e45c96ff1c761ddaa2263949b4ae0023a90b3f25b1f}')))  AND (wp_posts.post_password = '') 

Two notes about the above: first, you can see the expanded version of the placeholder for the “%” symbol. Second, the query clause starts with an AND. You may have noticed in the original query that this clause starts with WHERE 1=1. This is a simple trick to allow adding more conditions to the WHERE clause. If we wanted to add an extra field to check against when someone searches, say from another table, we could do something like this:

add_filter( 'posts_where', function( $where, $query ) {
    if ( 'post' !== $query->get( 'post_type' ) {
        return $where;
    }

    if ( ! $query->get( 's' ) ) {
        return $where;
    }

    $search = $query->get( 's' );
    $where .= " AND mycustomtable.ad_keywords LIKE '%{$search}%'";
    return $where;
}, 10, 2 );

This would allow us to search across our custom table for rows with a matching search in the ad_keywords column. If you’re familiar with SQL, you’re probably wondering how we’re querying the mycustomtable table without a JOIN. Well, right below the call to filter posts_where is our next guest…

posts_join

The posts_join filter allows us to write JOIN clauses to other tables we might need in our query. Currently, our query doesn’t join to any other tables since we aren’t doing a taxonomy query or meta query. However, if we want to JOIN our custom table, it’s pretty straight forward.

add_filter( 'posts_join', function( $joins, $query ) {
    if ( 'post' !== $query->get( 'post_type' ) {
        return $joins;
    }

    if ( ! $query->get( 's' ) ) {
        return $joins;
    }

    $joins .= 'JOIN mycustomtable ON( wp_posts.ID = mycustomtable.post_id )';
    return $joins;
}, 10, 2 );
Other Filters

While the above two filters make up the bulk of useful filters for most cases, the following may also be relevant depending on your use-case:

  • posts_where_paged and posts_join_paged – Similar to the above two filters, but used expressly when pagination is concerned
  • posts_orerby – Direct access to the ORDER BY clause, responsible for the order in which your posts appear when queried
  • post_limits – The query’s LIMIT clause
  • posts_fields – Determines which fields are returned from the database (note that WordPress does a lot of this for you when it gets the actual post objects, typically you wouldn’t need to modify this outside of very specific use-cases)
  • posts_clauses – This is kind of a catchall for various parts of the query, including WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT, selected fields, and LIMIT.

Conclusion

With the above, I hope you have a better understanding of how and where WordPress filters certain things, and I hope you go off and explore the codebase the next time you say, “Geez, I really wish I could modify that!” WordPress has plenty of actions for developers to tap into once you know where to look. Happy coding!

The post Debugging WordPress Core: Actions and Filters appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2019/01/22/debugging-wordpress-core-actions-filters/feed/ 3 19386
The PHPStorm Debugger is Pretty Awesome https://webdevstudios.com/2016/06/21/phpstorm-debugger-awesome/ https://webdevstudios.com/2016/06/21/phpstorm-debugger-awesome/#comments Tue, 21 Jun 2016 17:17:34 +0000 https://webdevstudios.com/?p=13113 In a previous post, I wrote about how awesome PHPStorm is in the context of being a front-end developer. In this post, we are going to look at getting the PHPStorm debugger setup to work with Xdebug and then test it to make sure it’s working! In my opinion, one of the most overlooked and Read More The PHPStorm Debugger is Pretty Awesome

The post The PHPStorm Debugger is Pretty Awesome appeared first on WebDevStudios.

]]>
In a previous post, I wrote about how awesome PHPStorm is in the context of being a front-end developer. In this post, we are going to look at getting the PHPStorm debugger setup to work with Xdebug and then test it to make sure it’s working!

In my opinion, one of the most overlooked and underused features of PHPStorm is its debugger. I’ve only just recently started to learn about it and use it. One of the biggest reasons it is underused, even by developers who are already using PHPStorm, is because setting it up can be a confusing process if you’ve never done it before. Despite how it may appear, it’s very simple to setup, and once you do it a time or two, it’s something you can do in just a few short minutes.

The PHPStorm debugger will make you never want to use a var_dump() ever again!

I currently use Vagrant for my development environment which comes prepackaged with Xdebug. If you’re using something else like MAMP or XAMP, you’ll need to make sure you have Xdebug installed and activated.

Configure PHPStorm and Xdebug

In order for the PHPStorm debugger to work, it needs to be configured to work with Xdebug. Don’t be alarmed by the number of steps it takes to configure; I just wanted to be as detailed as possible.

1. In PHPStorm, go to Run > Edit Configurations. This brings up the Run/Debug Configurations window.
2. Click the + (top left) and select PHP Remote Debug from the drop-down to add a new config.

At this point, you should have a window that looks like this:
PHPStorm configuration
3. Click the ... next to Servers: so we can configure a server.
4. In the Servers dialog, click the + to add a new server configuration.
5. Enter in a Name and the Host address.
PHPStorm and Xdebug step 5
6. This step is for Vagrant users only. If you are not using Vagrant, please skip to step 7.

Click Use path mappings to configure file location settings. The File/Directory should already be preconfigured. Under Absolute path on server, you will need to enter in the path to your projects htdocs folder on your vagrant server. It should look something like this:
PHPStorm - Xdebug Path Mappings
7. Click Ok to save and close out the Servers dialog.
8. You should now be back at the Run/Debug Configurations dialog, but there should be a selection in the Servers: section with the server that was just configured.
9. Add in a Ide key(session id) of VVVDEBUG.
10. Check the checkbox for Single instance only.

The fully configured Run/Debug Configurations dialog should look like this:
xdebug-10
11. Click Ok to close and save everything out!

To recap:

  • Go to Run > Edit Configurations
  • Configure the server and the path mappings if you are using Vagrant
  • Give the configuration a name and set the Ide key to VVVDEBUG

See, it isn’t that bad after all!

The Big Test

Now that we have PHPStorm configured to work with Xdebug, it’s time to test it out and see if it is actually working. The debugger works like most others in that you’ll set a breakpoint in your code for investigation, and then reload the page. When the page reloads, PHPStorm will take over when the breakpoint is reached.

For this test, I’m going to add the following function to my functions.php file. It’s just a simple function that will merge two arrays using the nifty wp_parse_args() function.

Here’s what it looks like:

function test_debugger( $args ) {

    $defaults = array(
        'phpstorm' => 'Rocks!',
        'debugger' => false
    );

    $args = wp_parse_args( $args, $defaults );

    return $args;
}

// Call test_debugger
test_debugger( array(
    'var_dump' => 'no more',
    'debugger' => true
));

In my code, I’m going to set a breakpoint by clicking in the gutter at the exact spot where I want the debugger to kick in.
set-breakpoint

After the breakpoint is set, PHPStorm needs to be put into Debug Mode. This can be done by using Control + D or by going to Run > 'BPDefault' where ‘BPDefault’ is the name of the configuration created earlier.

After debugging is turned on, all that’s left is to reload the page and keep an eye on PHPStorm to work its magic!

Now, for the gif of all gifs:

Click on the image to view larger size.
Click on the image to view larger size.

There is a lot going on in this gif, so let me point out a few key points.

  • By pressing ^ (Control) + D the bottom Debug pane slides up. Then, I reload the page and PHPStorm kicks in where the breakpoint is set.
  • Notice the Variables column. It contains EVERY variable in scope at that time.
  • Then, I use the Step Into button to go into the test_debugger function.
  • As I’m stepping through, you can see the values of variables directly to the right of them.
  • Did you also catch that we stepped into the wp_parse_args()? Pretty cool stuff!
  • Finally, we can see in the Variables column that $args is set to the arrays that were merged.

Wrapping Up

A lot has been covered in this post and there is so much more that could be written about the different features the debugger provides. However, I think its best we save that for another post!

I’d also like to thank Kellen for sharing his knowledge with me on this subject.

If you find yourself getting stuck or not being able to get the debugger configured, please feel free to leave a question or comment below!

The post The PHPStorm Debugger is Pretty Awesome appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/06/21/phpstorm-debugger-awesome/feed/ 4 13113
Debugging JavaScript Events: Why Does It Do That When I Click This? https://webdevstudios.com/2016/01/26/debugging-javascript-events/ https://webdevstudios.com/2016/01/26/debugging-javascript-events/#comments Tue, 26 Jan 2016 16:00:19 +0000 http://webdevstudios.com/?p=12345 Sometimes things happen on a page that you can’t figure out. You click on a link and nothing happens; you submit a form and a modal pops up. Something is happening in JavaScript, but you can’t figure out where. Enter, once again, the Chrome Developer Tools. Let’s say we have a link on the page Read More Debugging JavaScript Events: Why Does It Do That When I Click This?

The post Debugging JavaScript Events: Why Does It Do That When I Click This? appeared first on WebDevStudios.

]]>
Sometimes things happen on a page that you can’t figure out. You click on a link and nothing happens; you submit a form and a modal pops up. Something is happening in JavaScript, but you can’t figure out where.

Enter, once again, the Chrome Developer Tools.

Let’s say we have a link on the page that when clicked, instead of taking us to the URL in its href attribute, throws error messages all over the page randomly. A quick search all of your codebase returns no clear clues towards what script could be causing it. There are either too many references to the link to filter through, or nothing that seems to point directly to this link. We need to check what events are bound to this DOM node.

To do this, open up Chrome on the page, right click on the element, and click “Inspect Element.” This opens up the familiar Chrome Developer Tools. Hopefully you are already familiar with the basics here, inspecting elements and their styles, and basic JavaScript debugging (if you aren’t do yourself a favor and do a bit of looking around and look up some tutorials!). What interests us now is the “Event Listeners” tab on the right in the elements screen (which should be the screen that opens when you use “Inspect Element”).

Screenshot 2015-12-28 16.15.01

Under this panel, you should see a bunch of expandable event names and under each links to the functions that are bound to that event on the currently selected element. For our example, we should check out the click event to see what scripts we have bound to that event. On the left, we can see the DOM node the event is specifically bound to (events can be bound to a parent object listening for actions on child objects), and on the right, we can see the script and the location in the script of the function that is bound to that action.

Screenshot 2015-12-28 16.17.22

Clicking on that file name will bring us to the Sources page of the Developer Tools with that file shown. From here you can read through each function bound to the event and find which seems most likely to be the one triggering the behavior you are trying to figure out. You can add breakpoints and pause the code mid-execution to check exactly what is happening that may be causing the issue.

If the code is minimized make sure to click the “Pretty Print” button in the bottom left of the source view to expand out the code for easier reading, turning this:

Screenshot 2015-12-28 16.23.22

Into this:

Screenshot 2015-12-28 16.25.15

Much better, right?

Do you have any questions? Other tips you’d like to add to the mix? Throw them in the comments below!

The post Debugging JavaScript Events: Why Does It Do That When I Click This? appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/01/26/debugging-javascript-events/feed/ 1 12345
You Broke It! Better Error Handling So Your Errors Don’t Suck! https://webdevstudios.com/2014/08/11/you-broke-it-better-error-handling-so-your-errors-dont-suck/ https://webdevstudios.com/2014/08/11/you-broke-it-better-error-handling-so-your-errors-dont-suck/#respond Mon, 11 Aug 2014 14:00:29 +0000 http://webdevstudios.com/?p=8369 There you are. Face illuminated by the blue glow of the monitor. You’ve just finished passing the third of three sets of WP_Query arguments to a function that’s supposed to run a loop with the passed args. Except…arg is what you’re feeling right now because your loop…isn’t looping. It isn’t doing anything, actually. No function my_awesome_query expected 1 parameter, none given. Read More You Broke It! Better Error Handling So Your Errors Don’t Suck!

The post You Broke It! Better Error Handling So Your Errors Don’t Suck! appeared first on WebDevStudios.

]]>
There you are. Face illuminated by the blue glow of the monitor. You’ve just finished passing the third of three sets of WP_Query arguments to a function that’s supposed to run a loop with the passed args. Except…arg is what you’re feeling right now because your loop…isn’t looping. It isn’t doing anything, actually. No function my_awesome_query expected 1 parameter, none given. No fatal error on line 11123. No unexpected if (T_IF), expecting }. Nothing.

You’ll get past this. You always do. It’s probably something stupid. In fact, there it is — you’ve added some extra argument that doesn’t exist in these sets of posts you’re trying to display. You delete it and voila, it works.

This scenario should be pretty familiar. No matter what level of developer you are, at some point or another, you’ve stared at your code and said WHY ISN’T THIS WORKING??? Sometimes, a PHP fatal error saves you. But that assumes you have WP_DEBUG switched on (you do, right?). Debugging code is one of the hardest parts of writing code, and it gets exponentially harder as your plugin or theme gets bigger and does more things. Taking care to fail gracefully can not only save your bacon, it can help other people using your code understand what’s going on; remember, WordPress is open source software…someday, for some reason or another, someone may well be looking at your code and trying to decipher it. Maybe it will be you in five years, after you’ve completely forgotten about the project. Either way, leaving breadcrumbs and carefully thinking about and writing descriptive error messages is part of being a software craftsman, and it’s incredibly easy to just overlook and forget about.

But, you say, that’s what PHP errors are for. They even give me line numbers so I can trace the problem. Okay, hotshot, what about those times when even var_dumping an h1 tag that says HELLO WORLD doesn’t return anything on the page? In those cases, it can be extremely difficult to figure out what’s causing the problem, and providing a better fail option is usually trivial.

Here’s an example we should all be familiar with:

if ( have_posts() ) : while ( have_posts() ) : the_post();

If you’ve done any theme (or even plugin) development, you should know this by heart. It’s the WordPress loop. It checks for the existence of posts for a given archive page (usually the index.php or an archive template of some kind, but this can be used to display single posts or pages, too), and for each post on that page, it runs the_post() — which allows us to use template tags in that template like the_title or the_permalink. The problem is, a lot of times I come across themes that do this at the end of the loop:

endwhile; endif;

This closes out the loop, sure. But it doesn’t give you — or a user — any information about what they might be looking at. Or not looking at, as the case may be. If no posts are found, the loop just ends. It doesn’t give you any information about it. A simple alternative could be something like this:

endwhile;

else : 

     _e( 'No posts found', 'wds' );

endif;

…or even this:

endwhile;

else : 

     $labels = get_post_type_labels( get_post_type() );
     $not_found = $labels->not_found;

     echo $not_found;

endif;

…which will display the post type “no posts found” message for the current post type (read more about get_post_type_labels).

Here’s another example. You have something like this in your plugin:

function do_the_things() {
     if ( true == some_condition() ) {
          // do something
     }
}

If your condition isn’t met, your function fails and you get no information. No PHP error, it just doesn’t work. It doesn’t take that much more effort to add an else clause:

function do_the_things() {
     if ( true == some_condition() ) {
          // do something
     } else {
          _e( 'Something went wrong. <tt>some_condition()</tt> returned false.', 'wds' );
     }
}

You can do the same kind of thing with a PHP switch:

function do_the_fruits() {

     $fruit = which_fruit();

     switch( $fruit ) {
    
          case 'apples' :

               // do something with apples
               break;

          case 'oranges' :

               // do something with oranges
               break;

          case 'bananas' :

               // do something with bananas
               break;


          default :

               _e( 'Error in <tt>do_the_fruit</tt>: Could not recognize that fruit.', 'wds' );
               break;
          
     }
     

}

If your function takes a passed parameter, a good coding practice is to give it a default value. If that default value isn’t a valid option, you can return a friendlier error message telling the user (in this case, most likely a developer) what went wrong:

function which_fruit( $fruit = '' ) {
     if ( '' != $fruit ) {
          return $fruit;
     } else {
          _e( 'You did not pass a valid fruit to <tt>which_fruit</tt>.', 'wds' );
     }
}

In all these examples, hopefully you’ve noticed that I’m actually saying in what function the problem is happening. Just saying “I didn’t recognize that fruit” isn’t as helpful as saying “there was a problem in do_the_fruit.” Adding the function name to the message that displays makes your life a lot easier and makes it so you don’t need to go digging for answers when stuff breaks.

There’s another way to display error messages and this is built into WordPress itself. It’s the wp_die function. Now, I’m going to preface this by saying that you should use wp_die with care — wp_die kills the execution of the page, so only use it when you are sure you want a hard error message. However, if it’s between that and generating a white page of death (from a PHP fatal error), or if you are doing stuff in the WordPress admin, then it might be something worth looking into. wp_die takes 3 parameters, the message itself, the page title in the browser, and an array of additional arguments like text direction, server response code and whether to display a back button. An example might be something like this:

wp_die( '<h1>' . __( 'Uh oh! Something broke!', 'wds' ) . '</h1>', __( 'You broke it!', 'wds' ), array( 'back_link' =&gt; true ) );

Add that to your admin function and, depending on where the error is occurring in your code, you might get something like this:

…or this:

You_broke_it_

Now, obviously that’s not the best or most descriptive error message, but that shows you how important descriptive error messages are. If something breaks, just saying it’s broken or not returning any information that something broke at all isn’t helpful for debugging. Specifically saying this is what broke — this specific function — is much more helpful and is a better user experience overall than a white screen or a PHP error.

Considering what happens when a function breaks gives you a better understanding of how each piece of code fits together. As a developer, it’s easy to get into a zone and write function after function — each subsequent function hooking into the last — without stopping to consider what happens if one of those functions gives you some an unexpected response or data. It might seem like it takes longer to consider every possible scenario in your code, but having to backtrack and trawl through lines of code can take even longer than that.  Next time you write an if statement in your code, consider what happens if your conditions aren’t met. Doing so may make you rethink the functionality entirely or, at the very least, help you troubleshoot problems that will inevitably come up later.

 

The post You Broke It! Better Error Handling So Your Errors Don’t Suck! appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2014/08/11/you-broke-it-better-error-handling-so-your-errors-dont-suck/feed/ 0 8369