Skip to main content

Theming Drupal 8 in (Neo)Vim using Vdebug

Igor Lakic

In this article, we will explore how to best utilize Xdebug when theming Drupal 8 in (Neo)Vim. While both Kint or VarDumper continue to be amongst the most widely used tools in this area, they are also rather slow and somewhat unreliable for debugging Drupal 8. Luckily for us, there are faster and more efficient ways...

Intro

Text editors and IDEs are subjects of many lively discussions among developers. Nevertheless, every developer has its own favorite one which is to a large extent result of differing underlying work requirements, personal preferences, or in-house software limitations.  My personal choice lies with NeoVim, a highly configurable text editor that can be molded and customized to your liking.

In short, NeoVim can easily be turned into a full-fledged IDE, especially when you consider the plethora of available plugins. One of these plugins is Vdebug, a debugger that interfaces with XDebug for PHP and that can be configured to intelligently determine the root directory of a given project.

I'll concentrate on the NeoVim which I use in daily work, but the implementation is the same for both Vim and NeoVim.

What is Xdebug?

Xdebug is a powerful PHP development tool that is used in debugging projects, determining variables and arrays, evaluating variables, examining arrays, etc. And, unlike some other similar tools such as Kint and VarDumper, it will never end up consuming complete system memory and consequently "freeze" your whole system as is the case when examining deep arrays.

What is Vdebug?

Vdebug is a fast and powerful multi-language (PHP, Python, Perl, Ruby, etc.) debugger for (Neo)Vim. It interfaces with any debugger that faithfully uses the DBGP protocol, such as Xdebug for PHP. Some of the very useful features include:

  -  Set and toggle breakpoints.

  -  Step through your code line by line.

  -  Step over functions and methods.

  -  Step into and out of functions and methods.

  -  Run the application to the cursor.

  -  Evaluate variables under the cursor at run time.

  -  Navigate objects and arrays.

  -  Evaluate code and display the result.

Installing Vdebug

It is a good idea to first install one of the plugin managers for NeoVim. I'll be using "vim-plug" - a minimalist Vim plugin manager that is reliable and really easy to use.

To install Vdebug, insert the following line inside your "init.vim" ("vimrc" for Vim):

Plug 'vim-vdebug/vdebug'

Save changes, restart NeoVim, and run the following command:

:PlugInstall

At this point, the plugin is installed and you need to restart NeoVim once again to finish the process.

Xdebug configuration

Next, you will have to configure your Xdebug... If you're using Lando, you can just add the following to "config" section of your ".lando.yml" file :

xdebug: true

and then execute:

lando rebuild

In case you’re using Docksal (Xdebug is not enabled by default in Docksal as it causes a 20% website performance hit when enabled), all you need to do is to execute these two commands:

fin config set XDEBUG_ENABLED=1
fin restart

Now you can verify that Xdebug was enabled with:

fin exec php -v | grep -i xdebug

Starting with Docksal v1.6.0 (and assuming the default stack is used), installing the companion browser extension is no longer necessary. Once Xdebug is enabled, debugging sessions will start automatically.

If you're using Lando or some other solution, it would be a good idea to install "Xdebug helper" for Firefox or Chrome. Make sure that the 'idekey` matches what you’re using in Xdebug on the server (in this case, in Docker container).

DDEV users will have to enable xdebug in "config.yaml" with:

xdebug_enabled: true (it's disabled by default)

After changing, run again:

ddev start

Or if you want to enable it temporarily, execute these commands:

ddev exec enable_xdebug      (to enable it)
ddev exec disable_xdebug       (to disable it)

For more information on how to configure your Xdebug instance, check out the official Xdebug documentation.

Prepare Drupal 8 website for Xdebug

Once you're done with previous steps, you will have to configure Drupal 8 so that you can debug Twig (and php) files.

First, disable cache. This can be done with the help of "Drupal Console":

drupal site:mode  dev

or manually by adding these lines in the "development.services.yml":

# Local development services.
#
# To activate this feature, follow the instructions at the top of the
# 'example.settings.local.php' file, which sits next to this file.
services:
  cache.backend.null:
    class: Drupal\Core\Cache\NullBackendFactory
parameters:
  http.response.debug_cacheability_headers: true
  twig.config:
    debug: true
    auto_reload: true
    cache: false

It is also advisable to disable cache on your Drupal site completely by uncommenting these lines in your "settings.local.php":

$settings['cache']['bins']['render'] = 'cache.backend.null';
$settings['cache']['bins']['page'] = 'cache.backend.null';
$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';

After this, you won't need to clear your cache after every change in your php/twig files.

As one last step, you'll also have to install one of the following two modules: "Twig Xdebug" or "Devel" (which you probably already have installed on your Drupal instance).

We're almost there...

When using a Docker-based development environment, Docker container acts as a remote machine. So you need to map paths that will sync the local path (location of the project on your computer) with the remote one (location of the project in the Docker container) so that Vdebug can communicate with the Xdebug instance from the Docker container.

You can solve this an easy way by adding the following in your (Neo)Vim config file:

" === Vdebug setup ==="
" How much deep you can go into layers of variables
let g:vdebug_features = { 'max_children': 256 }
" Allows Vdebug to bind to all interfaces
let g:vdebug_options= {}
" When value is '0' it will jump straight to the file you're currenly debuging
" (no more stopping on 'index.php', autoloader line or something like that)
let g:vdebug_options['break_on_open'] = 0
" Use the compact window layout.
let g:vdebug_options['watch_window_style'] = 'compact'
" Sync the remote path ('/remote/path') with the local path ('/local/path')
let g:vdebug_options['path_maps'] = {
\   '/var/www': getcwd()
\   '/app': getcwd(),
\   }

The last 4 lines at the end are the most interesting for our configuration: First few lines of code are already explained in the code above so let me explain these 4 lines at the end. 

On the left side, you can see the path on your "remote" location (in Docker container) and on the right side, you can see the function which will automatically determine the precise location of the root directory of your project.

Now, every time you start working on a new project, simply opening its root directory in NeoVim will map it as a root directory in Vdebug as well. To be precise, the "getcwd()" function will take care of correctly mapping the root directory in Vdebug.

This change is definitely welcome, as not long ago, this modification had to be made in NeoVim’s configuration file individually for each new project, as local paths to different projects also differ. However, that wasn't the prettiest solution as it made NeoVim’s config file look extremely cluttered. You could however, make local NeoVim config file just for that project or configure local paths with the help of other NeoVim plugins, etc. but that also wasn't a tidy solution for many reasons.

So, various Docker-based development environments use various default root directories for projects: Docksal (by default) uses "/var/www", Lando uses "/app", and in DDEV that is "/var/www/http". (you can easily verify this by executing the command which will ssh you inside the Docker container).

Time to use Vdebug

Now, if you're going to use Devel module for debugging Twig files in Xdebug, you need to insert following code in your Twig template file:

{{ devel_breakpoint() }}

or if you decided to use "Twig Xdebug":

{{ breakpoint() }}

Next, start Vdebug and while still focused on Vdebug, press F5. Now once you refresh your website in a browser and go back to NeoVim, you will see a screen with Vdebug on which you can browse all your local, global variables, user-defined constants, etc. (you can see a short asciinema video I made about this topic here).

There are a lot of things you can do with it and one really handy one from time to time is the "VdebugEval" which will evaluate the code you enter after it, so for example, you can type something like:

:VdebugEval $context["elements"]["field_preface"]["#title"]

That way you can take a better look at a particular variable or array you're after and get a greater picture of it.

You definitely need to try it out and play with it a little, you'll not regret - I promise! ;)

Conclusion

This whole process might seem a little complicated, but in reality, it's easier to configure Vdebug to work with the Xdebug from a Docker container than to configure PHPStorm to do the same. Either way, you have to define paths on a local machine and remote server (which in this case is Docker container).

However, doing so is much faster and efficient when using  (Neo)Vim with Vdebug, as the "getcwd" function makes things partially automated (you only need to configure the location on the remote server - docker container) and you don't need to add all paths manually for every new project (if the project is based on the Docker-based development environment you already setup configuration for).

I hope you found this article helpful, both in terms of learning how to better utilize debugging tools in your workflow and in terms of using some great alternatives to classical IDEs. If you're still not convinced and want to continue using PHPStorm + Xdebug for Drupal theming purposes, awesome! I can recommend a great article + video from Danny Englander.

If you want to read another article on (Neo)Vim + Vdebug, more from the perspective of a back-end developer, I recommend an article from Bob Kepford.

Until next time, have a nice coding day! :)