Use PHP Intelephense for Developing Against 3rd Party WordPress Plugins and Themes

My best advice for anyone interested in best practices, team collaboration and releasing maintainable and sustainable software written in PHP is to use the PHP Intelephense language server with your text editor of choice. It is hands-down the best tool for working with PHP code; it enables code completion and displays documentation and hints for function parameters.

And while the full version of Intelephense costs money, it is a token amount for the increased quality and productivity you get out of it.

The issue is that Intelephense needs to know the framework, extensions and plugins that your app or plugin depends on by means of stubbing. It will index and learn about your own code as you work on it and it has built-in support for various PHP extensions and frameworks out of the box and by setting things up correctly, it can treat 3rd party code the same way using Composer.

I am going to assume you know how JSON files work, are using Visual Studio Code and have PHP Composer and NPM installed and know how to use those as well.

Installing Intelephense with VSCode

As Intelephense is a language server that runs outside if your editor, you are going to want to install it globally using NPM:

$ npm i intelephense -g

The official documentation for PHP Intelephense instructs you to prepare the VSCode extension installation by looking up @builtin php under the Extensions Tab. Disabling the PHP Language Features extension, but keeping PHP Language Basics enabled as it provides syntax highlighting and such.

I don’t do this myself, as feel like there are too many features missing from disabling the PHP Language Features extension. But your mileage may wary.

Then you can install the PHP Intelephense Extension. and reload your VSCode window to make sure everything is in order.

You may notice that Intelephense will not be aware of any WordPress Core functions by default. You will learn how to enable WordPress support in the next section.

Enabling WordPress Support in Intelephense

You can enable support for WordPress Core functions by adding WordPress to the list of stub files that are enabled in Intelephense. You do this by opening your VSCode Settings and searching for @ext:bmewburn.vscode-intelephense-client stubs.

This shows you a list of the PHP extensions and frameworks that are enabled in Intelephense. Scroll to the bottom of that list and you will find a button that says Add Item.

Click it and you will see a drop-down menu containing various PHP frameworks and extensions. Find WordPress in that list and click the OK button to add it.

This is sufficient for most use cases when it comes WordPress theme and plugin development.

What is lacking though is if your plugin or theme depends on code from another plugin or 3rd party codebase and that will be covered in the next section.

Working with 3rd Party Plugins

As someone who has been working on a plugin that depends on WooCommerce, having my editor shouting at me about the WooCommerce functions that I’m using not being defined and then not helping at all is really annoying.

I see those red squiggly lines below PHP function calls in online tutorials and YouTube videos all the time and it is time to solve that once and for all.

There are no built-in Intelephense stubs for the likes of common themes or popular plugins like Jetpack or WooCommerce, but there is a way around it by adding those as development dependencies in Composer, even if it does not have its down Composer file and isn’t an official Composer package.

I am going to go through how I do this with WooCommerce in particular, but the following instructions should apply to most other WordPress plugins.

Creating a Composer File

If you don’t have a composer.json file for your plugin, you can create one by opening a terminal window for your project and typing in composer init. Follow the instructions and make sure that you skip the dependency installation process for the time being.

Adding WooCommerce as a Development Dependency

When you’re done with the process, a composer.json file will appear in your project. Add the following section to the root of that file, making sure that the JSON code is valid:

"repositories": [
    {
        "type": "package",
        "package": {
            "name": "woocommerce/woocommerce",
            "version": "10.0.2",
            "dist": {
                "url": "https://github.com/woocommerce/woocommerce/releases/download/10.0.2/woocommerce.zip",
                "type": "zip"
            }
        }
    }
]

This adds the WooCommerce Github archive as a package repository that Composer can now install as a development dependency using the following command:

$ composer require --dev woocommerce/woocommerce:10.0.2

The WooCommerce source code is now located in the ./vendor/woocommerce/woocommerce/ directory. It may take a couple of seconds, but Intelephense is now going to read the public PHP classes and functions in the WooCommerce codebase.

You don’t need to autoload or require those to “fool the system” as Intelephense will assume that those are defined somewhere already and will pick them up automatically.

Shortcomings

The main shortcoming of this method is that it requires regular maintenance as a new version of WooCommerce is released regularly. If you have the time, you can probably run a weekly Github Action to handle it for you, but as for myself, I am going to keep things as-is.

Handling Development Dependencies for Published Plugins and Themes

It may not be obvious to everyone, but including development dependencies such as WooCommerce when you ship your plugin is not advisable and I’m not sure if the current WordPress.org plugins team would allow it.

In case you only use Composer to maintain your development dependencies and you define them as such in your Composer file, it’s sufficient to delete the ./vendor/ directory before zipping and shipping your plugin or theme. In fact, you should add it to your .gitignore file as well to make sure it doesn’t end up in your git directory.

Conclusion

Sometimes there’s a trick to getting things working and admittedly this is very hacky way to get Intelephense features working properly with WordPress and 3rd party plugins and themes.

But it works. And it works well enough.

My guess as to why we need to go this route is that the mainline PHP community, which nowadays is largely concentrated around Laravel and Drupal on one hand and the WordPress community on the other don’t really mingle that much. The use of advanced development tools such as Composer and modern PSRs is also discouraged in the WordPress community and there is a discrepancy between the coding standards used by the WordPress crowd and the rest of the PHP community.

But that is worthy of its own blog post.