Using CLion with php-src
Tags: php, clion, jetbrains
Published: 2021-05-10 17:08:02

I’ve cloned and built [php-src](https://github.com/php/php-src) numerous times over the years, on a variety of platforms, but I’ve never worked with it in an IDE. A long-time [Vim](https://www.vim.org) user, I’ve only begun using [PhpStorm](https://www.jetbrains.com/phpstorm/) over the last year and a half, after seeing how [IntelliJ](https://www.jetbrains.com/idea/) helped me easily navigate an open source Java project. So, in setting up php-src for [release management](https://dev.to/ramsey/i-m-a-release-manager-for-php-8-1-24i2) tasks, I wanted to give [CLion](https://www.jetbrains.com/clion/) a try.

## Getting php-src ready for CLion

When opening existing source code or cloning a repo with CLion, it assumes the project already supports [CMake](https://cmake.org) tooling for builds. For someone new to C and using a project that doesn’t use CMake, this can be confusing and frustrating. Since php-src isn’t a CMake project, CLion will display warnings at the top of every open file, saying that a “CMake project is not loaded,” giving you the option to select or generate a `CMakeLists.txt` file. You can try to do this for php-src, but there’s a better way.

CLion also supports [JSON compilation databases](https://www.jetbrains.com/help/clion/compilation-database.html). To generate one and have CLion recognize php-src as a [compilation database project](https://www.jetbrains.com/help/clion/compilation-database.html), you’ll need to do the following *before* opening php-src in CLion. If you’ve already opened it in CLion, that’s okay. Delete the `.idea/` folder that CLion creates, and then do the following. (I could not figure out a way to make CLion recognize the compilation database if I had already opened the project in CLion. It seems the compilation database must exist the first time you open the project in order for CLion to recognize it.)

You’ll need the `compiledb` Python tool to generate the compilation database. It wraps the `make` command, so when you run it with `make`, it captures all the sources and creates a `compile_commands.json` file. Then, when you open the project in CLion for the first time, it will recognize your project as a compilation database project.

```shell
pip install compiledb

git clone https://github.com/php/php-src.git ~/repos/php/php-src
cd ~/repos/php/php-src
./buildconf
./configure \
    --prefix=$HOME/builds/php/8.1.0-dev \
    --disable-all \
    --disable-cgi \
    --enable-debug \
    --enable-zts

compiledb make
```

As you can see here, I use `compiledb` to call `make`. In this way, `compiledb` can learn about all the sources for your build.

You may also pass `-jN` to `make`, where `N` is the number of CPU cores your system has available. This will greatly speed up compilation.

You can run `nproc` to find out how many cores you have.  On my system, I have 12 cores, so I run the command like this:

```shell
compiledb make -j12
```

In my example, I pass `--prefix` to `configure`, but this isn’t necessary since I’m not going to run `make install`. Once `make` has finished building PHP, you can find the newly-built PHP CLI at `sapi/cli/php`.

```console
$ ./sapi/cli/php -v
PHP 8.1.0-dev (cli) (built: May  6 2021 18:27:37) (ZTS DEBUG)
Copyright (c) The PHP Group
Zend Engine v4.1.0-dev, Copyright (c) Zend Technologies

$ ./sapi/cli/php -m
[PHP Modules]
Core
date
hash
json
pcre
Reflection
SPL
standard

[Zend Modules]
```

## Setting up CLion to build and run PHP

Now that php-src has a compilation database (which needs to be updated each time you change your build with `configure` or if you add new sources that get included in `make`), you can open it in CLion.

Launch CLion to get to the welcome screen and choose to open a project from existing sources.

![CLion welcome screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/453rmnfowe292buy9ys4.png)

Browse to your php-src folder and, rather than selecting the folder to open, select the `compile_commands.json` file that 	`compiledb` generated.

![File selection dialog highlighting compile_commands.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pc3pflycdgmxdllyhryp.png)

When CLion prompts you, choose to open it as a project.

!["Open as Project" dialog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1qt7hutpz8ncm1ihc1ou.png)

CLion will now open the project and inform you that it successfully imported the compilation database project.

![Compilation database project successfully imported](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fmbg70s83uq8weqcemv5.png)

With CLion open, you’ll now [create custom build targets](https://www.jetbrains.com/help/clion/custom-build-targets.html) for `make` and `make clean`.

Open CLion Preferences and go to *Build, Execution, Deployment* -> *Custom Build Targets*. Create a new build target, named something like “Build PHP.” Then, next to the empty Build and Clean fields, click the *more* button (with three dots). This will open an External Tools window. Use the +-button here to create two external tools.

I named the first one “Build with Make.” The program is `make` and I added `-j12` to the arguments for a faster build (see earlier). You might also consider using `compiledb` as the program, with `make -j12` as the arguments.

!["Build with Make" external tool](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dl485qaqihjzbm256urx.png)

I named the second tool “Clean with Make” and used `make` as the program and `clean` as the argument.

!["Clean with Make" external tool](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/niwv1ckzq8kd24zg5f1f.png)

Now, you can click OK to close these windows and the External Tools window, and select the proper tools to use with your Build PHP target.

!["Build PHP" build target with external tools selected](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r1pyy581zuvguvw82jik.png)

Finally, you’ll want to [create a custom run/debug configuration](https://www.jetbrains.com/help/clion/custom-build-targets.html#custom-rundebug).  To do this, go to *Run* -> *Edit Configurations…*.

Use the +-button to create a new configuration and choose **Custom Build Application** from the menu that appears. I named my configuration “Build PHP” and I chose my “Build PHP” target (created earlier) from the Target dropdown list. For the executable, I entered the path to the PHP CLI built by this project (remember, it’s at `sapi/cli/php`). Last, I checked “Redirect input from” and entered the JetBrains variable `$FilePrompt$`. In this way, whenever you chose to run PHP from within CLion, it will prompt you with a file dialog to execute through the PHP interpreter. This lets you select a PHP script to run and test/debug whatever feature you’re working on.

!["Build PHP" run/debug configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xii747wxpxgsznbkebtx.png)

Now, click the hammer icon (🔨) or select *Build* -> *Build Project* to build PHP, or select *Build* -> *Clean* to run the `make clean` command. 

![Output after running "Build with Make"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jb32s6ksfbrpxyg9p58q.png)

If you click the play icon (▶️) or choose *Run* -> *Run ‘Build PHP’*, it will prompt you to select a file and run that file as a PHP script. In the following, I’ve selected one of the `.phpt` tests to execute this way.

![Using the file selection dialog to select a PHPT file to run](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/weji3r4ftbmemrse4x56.png)

![Output after running the PHPT file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gq5shyb2g9tyfw50mik5.png)

## Step debugging through php-src

One of the primary reasons I wanted to use an IDE to work with php-src is for the debugging tools. With everything set up as described earlier, I’m able to run a PHP script and break on breakpoints in the C source code.

As an example, I created a PHP script named `date-debug.php` and added the following code to it.

```php
<?php
echo date('Y-m-d H:i:s', time());
```

Then, I opened `ext/date/php_date.c` and placed a breakpoint in the `php_date()` function. You can see the breakpoint in the following screenshot. I placed it right after the `ZEND_PARSE_PARAMETERS` block.

When you click the debug icon (🪲) or choose *Run* -> *Debug ‘Build PHP’*, CLion will prompt you to select a file, just like it does when when you click the play icon, as described earlier. When I choose the `date-debug.php` file, it begins executing it and then stops at the breakpoint in the C code, which happens to be the code called when using the PHP `date()` function. As you can see, I’m now able to examine the memory and variables in this function, including the [zvals](https://www.phpinternalsbook.com/php7/zvals.html).

![Inspecting variables at a breakpoint in the C source code for PHP](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/97o3y6iglzvkdg3pdqwv.png)