April 2, 2020

539 words 3 mins read

Debugging Drupal in Lando using (Neo)vim and Vdebug

Debugging Drupal in Lando using (Neo)vim and Vdebug

With the introduction of OOP on Drupal 8, it has become a bit difficult for me to track how Drupal processes each request. Most of the time, you’ll be dealing with different objects, track down which class were instantiated and methods that were called, and inspect the variables.


So what are the tools that I am using? I’ve been a long time user of vim and recently just switched to Neovim. For development environment, I mostly jump between different methods for building Drupal but recently have been using Lando.


For the configuration, simply setting xdebug to true so that Lando would enable xdebug in the environment:

name: drupal
recipe: drupal8

    webroot: web
    xdebug: true
      php: config/php.ini

For config/php.ini, I simply followed the one from setting up vscode:


; Xdebug
xdebug.max_nesting_level = 256
xdebug.show_exception_trace = 0
xdebug.collect_params = 0
; Extra custom Xdebug setting for debug to work in VSCode.
xdebug.remote_enable = 1
xdebug.remote_autostart = 1
xdebug.remote_host = ${LANDO_HOST_IP}
; xdebug.remote_connect_back = 1
xdebug.remote_log = /tmp/xdebug.log


For (Neo)vim, Vdebug seems to be the only option for using Xdebug in PHP. A minimal configuration can be:

call plug#begin(stdpath('data') . '/plugged')
Plug 'vim-vdebug/vdebug'
call plug#end()

if !exists('g:vdebug_options')
  let g:vdebug_options = {}

let g:vdebug_options.path_maps = {
    \  '/app' : getcwd(),
\ }

let g:vdebug_options.break_on_open = 0
let g:vdebug_options.watch_window_style = 'compact'

if !exists('g:vdebug_features')
  let g:vdebug_features = {}

let g:vdebug_features.max_children = 128

vdebug_options.path_maps sets the paths so that it works with Lando. Due to the getcwd() value, you’ll need to make sure that before starting (Neo)vim, you’re current working directory would be your workspace i.e. whatever is mounted on ‘/app’ inside Lando.

vdebug_features.max_children allows inspecting variables from Drupal most of the time.

For other options, they can be found on :h Vdebug.


Consider the scenario where you are viewing a node and wanted to debug the method \Drupal\node\Entity\Node::getType(). To start debugging, open the file core/modules/node/src/Entity/Node.php and set a breakpoint using F10, start the debugger using F5, and finally visit a single node.

I’m not entirely sure of the actual definitions for step debugging although in my own words, I can probably explain them like these:

  • Step over: continue to the next line in the source.
  • Step into: if the line calls a certain function/method, then go over to that function/method where you can then continue debugging.
  • Step out: skip the remaining lines and proceed to the next step.


From the configuration above, it mostly just relies on the default bindings (:h VdebugKeys):

let g:vdebug_keymap = {
\    "run" : "<F5>",
\    "run_to_cursor" : "<F9>",
\    "step_over" : "<F2>",
\    "step_into" : "<F3>",
\    "step_out" : "<F4>",
\    "close" : "<F6>",
\    "detach" : "<F7>",
\    "set_breakpoint" : "<F10>",
\    "get_context" : "<F11>",
\    "eval_under_cursor" : "<F12>",
\    "eval_visual" : "<Leader>e",

User interface

Vdebug UI

The explanation for each window can be found at :h VdebugUI. I find the “Watch” window very helpful since I can inspect the different variables with their context. Pressing Enter on a variable would expand/collapse their trees.

So far, I think these are the basic things to debugging with (Neo)vim using Vdebug. Vdebug has a lot of other features such as evaluating although I haven’t gone much further yet.