{{last_updated_at}} by {{last_updated_by}}

Debugging CiviCRM cheatsheet

Command-line runs of PHPUnit:

From a buildkit civiroot, run a specific file's tests::

CIVICRM_UF=UnitTests phpunit5 tests/phpunit/CRM/Core/BAO/AddressTest.php

You can also limit to a single test by filtering on name:

CIVICRM_UF=UnitTests phpunit5 tests/phpunit/CRM/Core/BAO/AddressTest.php --filter testShared

With XDebug

Web Browser

  • Install the xdebug extension (e.g. sudo apt install php8.1-xdebug).
  • Configure xdebug by copying the values below to /etc/php/8.1/fpm/conf.d/xdebug.ini:
xdebug.client_host = ""
#xdebug.output_dir = "/home/jon/temp/xdebug"
  • Install a plugin for your browser, like "XDebug Helper for Firefox".
    • In the plugin's configuration, set the IDE key to VSCODE.
  • For buildkit sites, you'll need to cp the xdebug.ini into apache2/fpm/conf.d and cli/fpm/conf.8. Then restart apache and php

  • If one does not already exist, create a launch.json file within the .vscode folder (make a new folder if it does not yet exist) at the root of your codebase. What goes into the launch.json file depends on whether you are debugging a a site created with the civibuild command or one that was not built with that command. Here are examples of a civibuild launch.json and a non-civibuild launch.json. Note that the "max-depth" on the "Listen for XDebug" configuration can be changed to delve deeper into the values that are returned (i.e. a depth of 6 will return more levels of a nested array than a depth of 3).

To debug, you must turn on the debugger in VS Code, then enable debugging in the address bar for the requests in question.

Web Browser - remote debugging

To debug on a remote server, ensure you have local debugging working first.

Server-side setup

  • sudo apt install php{{php_version}}-xdebug (I should add this to group_vars/all.yml so it's everywhere).
  • Create a file `/etc/php/{{php_version}}/fpm/conf.d/xdebug.ini:
  • Restart PHP.

Client side setup

First time ever remote debugging setup
  • In your VS Code plugins screen, install the "Remote - SSH" plugin.
  • Using the new Remote Explorer sidebar icon in VS Code, select the server you'd like to connect to. Click the "Connect in current window" icon.
  • If you've already debugged on this site before, you should be able to expand the server and see a workspace file (see screenshot below). Click one of the connect icons.
  • If you haven't debugged on this site before, follow First Time instructions below. Otherwise, debug as normal (make sure your Firefox "debug" icon is turned on).
First Time
  • Click Open Folder and navigate to the site's gitroot (usually the same as webroot, but for D8+ it's the folder above web so you can also debug in vendor).
  • Select File menu » Save Workspace As and store a workspace file in your home directory.
  • In your VS Code plugins screen, you need to select Install in SSH for the PHP Debug and PHP Intelephense plugins. Make sure it's the "PHP Debug" by XDebug (should have 10M+ installs)
    • For Drupal sites, also Install in SSH the Drupal Syntax Highlighting extension or you can't debug .module files.
  • Go to the VS Code "debug" sidebar. Click Add Configuration and then select workspace, then PHP.
  • (Optional) in the Remote Explorer sidebar, under the server you're connected to, right-click the entry that doesn't say "Workspace" in it and select Remove from Recent List to only keep workspace entries.

civicrm-buildkit (mod_php)

The instructions above assume php-fpm. To also debug mod_php (e.g. civicrm-buildkit), do the following:

  • Use the same configuration file as under "Web Browser", but at /etc/php/7.4/apache2/conf.d/xdebug.ini.
  • Edit /etc/php/7.4/apache2/conf.d/xdebug.ini and change the client port from 9000 to 9001.

Command Line (phpunit)

  • You need to have XDebug otherwise configured for CLI. Use the same configuration file as under "Web Browser", but at /etc/php/7.4/cli/conf.d/xdebug.ini.

    • Also change xdebug.mode=debug,develop to avoid some unnecessary warning noise.
  • You need to start a debugging session in VS Code with "Listen for XDebug".

  • Depending on your VS Code setup, you may need to listen on a different port (I can use the same port for FPM but not mod_php).

Once you've got all that:

env CIVICRM_UF=UnitTests XDEBUG_SESSION=VSCODE phpunit7 /home/jon/local/civicrm-buildkit/build/dmaster/web/sites/all/modules/civicrm/tests/phpunit/CRM/Core/BAO/ActionScheduleTest.php --filter testMembershipJoinDateMinutesUnit

phpunit for extensions:

For core extensions:

CIVICRM_UF=UnitTests phpunit8 --bootstrap ext/afform/mock/tests/phpunit/bootstrap.php ext/afform/mock/tests/phpunit/api/v4/AfformContactUsageTest.php 

For non-core extensions, from the extension's root folder. Note the use of env XDEBUG_SESSION=VSCODE for step debugging, and --filter to limit the tests run.

env XDEBUG_SESSION=VSCODE CIVICRM_UF=UnitTests phpunit8 --bootstrap tests/phpunit/bootstrap.php tests/phpunit/api/v3/Cdntaxreceipts/GenerateTest.php --filter testGenerate
# If everything is in a default state, it could also be as easy as:
phpunit9 --filter testContributionCreate

Command-line runs of standalone scripts

env XDEBUG_SESSION=VSCODE php myscript.php

Also works for cv, probably drush and wp too:

env XDEBUG_SESSION=VSCODE cv api NotificationLog.retry system_log_id=147597 

Debugging REST API calls in Ansible/curl

Add an additional POST argument XDEBUG_SESSION=VSCODE. In curl, just add -d 'XDEBUG_SESSION=VSCODE' anywhere in your command.

For Ansible, this might look like:

  - name: Shut up about Civi extensions (warnings only, 7 days)
      url: "{{ primary_url }}/{{ endpoint }}"
      method: POST
        entity: StatusPreference
        action: create
        json: "{{ {'name': 'checkExtensionsUpdates', 'ignore_severity': 3, 'hush_until': seven_days_hence } | to_json }}"
        api_key: "{{ crm_api_key }}"
        key: "{{ crm_site_key }}"
      body_format: form-urlencoded
      return_content: yes

Debugging in PHP file_get_contents() (e.g. check_civicrm.php)

Add XDEBUG_SESSION=VSCODE as a GET argument, it works even on a POST request. Pairs well with a remote debugging session.

$url = "$prot://$host_address/$path/System/check?XDEBUG_SESSION=VSCODE";

Debugging JavaScript

While you can open up the Developer Tools on a browser (by right clicking on the window and then clicking Inspect) and debug the code directly there, it might be preferable to set up a debugging environment within an IDE, i.e. Visual Studio Code. To do so:

  1. Open up your launch.json file (Need to set one up? There are non-civibuild and Civibuild options)
  2. Within the ”configurations:” array, add a comma after the last curly brace and then paste in:
      "type": "msedge",
      "request": "attach",
      "name": "Attach to browser",
      "port": 9222
  • If you’d prefer to debug on chrome, use ”type”: “chrome”
    1. Within your terminal, run the following command:
/usr/bin/google-chrome --remote-debugging-port=9222 –user-data-dir=remote-debug-profile
  • /usr/bin/google-chrome is the path of the Google Chrome Binary on Linux. For other operating systems, check out this comment, but note that the exact name might be different (i.e. google-chrome1 vs google-chrome). You can always cd through the path it recommends to find the exact match for either Chrome or MSEdge.
  1. Within VS Code, go to the Run and Debug tab, and from the drop down menu at the top, select “Attach to browser” and start the debugger.

    • Note that the command given in the previous step is configured to open up a new browser, but these arguments (and more) can be customized. See the documentation
  2. The new browser window (or tab, depending on your settings) is now configured to be the debuggee-yes, that’s the technical term- so you can set breakpoints and debug within VS Code!

Running FunctionalJavascript tests

This is for Drupal 10+ and potentially my end-to-end testing on Civi FormBuilder.
The Mink setup instructions worked quite well.
I then also ran:

composer require drupal/webform
composer require drupal/webform_civicrm
composer require drupal/token
composer require semperit/minkcivicrmhelpers

Looking at CiviCARROT logs on the Webform-CiviCRM Github was helpful to figure those out.

For permissions reasons, you have to run as the www-data user (this is set up with mod_php, not php-fpm):

zabuntu: ~/local/wfc-testing/web/core » sudo -u www-data ../../vendor/bin/phpunit -c $HOME/local/wfc-testing/phpunit.xml ../modules/contrib/webform_civicrm/tests/src/FunctionalJavascript/ContributionPayLaterTest.php --filter testSubmitContribution
# with debugging:
sudo -u www-data env XDEBUG_SESSION=VSCODE ../../vendor/bin/phpunit -v -c $HOME/local/wfc-testing/phpunit.xml ../modules/contrib/webform_civicrm/tests/src/FunctionalJavascript/EventTest.php --filter testParticipantCount

This will only debug the test, it won't break on any lines the automated browser calls. To do that:

  • Make sure your /etc/php/x.x/apache2/conf.d/xdebug.ini is set to always start XDebug (the automated browser won't pass the trigger).
  • Duplicate your VS Code workspace (Ctrl-Shift-P, select "Workspaces: Duplicate as Workspace in New Window").
  • Create a basic XDebug config in the new window.

Now you can run an XDebug on port 9001 in your original window, and port 9003 on the secondary window.


  • If you get RuntimeException: The provided database connection in SIMPLETEST_DB contains CiviCRM tables, use a different database. then drop and recreate the test database. You can check it for sure in <projectroot>/web/core/phpunit.xml.dist but it should be wfc-testing.
  • Is chromedriver a current version, and did you start it? chromedriver --port=4444 (in whatever directory you downloaded chromedriver.)
  • phpunit -v will tell you why a test is "risky". Buuuuut it's probably because chromedriver isn't running.

Finding an entry point to debug

If a better one can't be found in an error log/message, use these steps to start debugging:

  1. ssh into the server
  2. change to root user
  3. cd /var/log/apache2
  4. nano other_vhosts_access.log
  5. search for your IP address ( you can type "What's my IP address?" into Google if you don't know it)
  6. find the relevant line(s) with your IP address and grab the civicrm path from the url
  7. open up mysql (cv sql) and run select * from civicrm_menu where path = '[civicrm path]';
  8. look at the access arguments, but we care most about the page_callback column which lists the civicrm class that is loaded at that path
  9. open up that class in the codebase and use the buildForm hook as the entry point for debugging

Updated by Jon Goldberg 5 days ago · 47 revisions