Developing a PHP extension in C can be challenging compared to writing PHP code. You need to mind typing and make sure variables are initialized. There are a lot of macros and functions involved. And there is the dreaded segfault.
Working with a simple text editor can be frustrating. An IDE shows you where mistakes are made while typing. And the debugger can help to find the cause of segfaults and other issues.
If you're familiar with PHPStorm, the obvious IDE to use for extension development it CLion. Unfortunately, CLion is built around the CMake build tool, while PHP using automake.
Converting PHP to a CMake project is far from trivial. Luckily, we can make CLion behave relatively well with automake;
- Code completion and analysis
- Build and clean (as normal)
- Run tests with automatic (re)build
- Debugging
Skelton extension
The Improved PHP Library skeleton extension contains the necessary logic for editing in CLion as well as building on both *nix and Windows.
CMakeList.txt
We don't want to build the project using CMake, but we still need an add_library
command for CLion to acknowledge the source files. Using ___
as the name is an indication it should be ignored (you could also use _ignore_
).
Instead, we add a custom target name configure
which will run phpize
and ./configure
for our extension.
The PHP source files need to be included. By executing php-config
we can get the PHP_SOURCE
directory that includes the header files.
It's common to do #include "php.h"
rather than #include "main/php.h"
. Therefore we also include the main
subdirectory. The same for Zend
and TSRM
.
cmake_minimum_required(VERSION 3.8)
project(skeleton C)
add_compile_definitions(HAVE_SKELETON)
set(SOURCE_FILES php_skeleton skeleton.c)
execute_process (
COMMAND php-config --include-dir
OUTPUT_VARIABLE PHP_SOURCE
)
string(REGEX REPLACE "\n$" "" PHP_SOURCE "${PHP_SOURCE}")
message("Using source directory: ${PHP_SOURCE}")
include_directories(${PHP_SOURCE})
include_directories(${PHP_SOURCE}/main)
include_directories(${PHP_SOURCE}/Zend)
include_directories(${PHP_SOURCE}/TSRM)
include_directories(${PROJECT_SOURCE_DIR})
add_custom_target(configure
COMMAND phpize && ./configure
DEPENDS ${SOURCE_FILES}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
add_library(___ EXCLUDE_FROM_ALL ${SOURCE_FILES})
PHP versions
When building an extension, it's recommended to build and test it against multiple PHP versions. To install and use multiple PHP versions on your system you can use a tool like phpbrew.
CMake profiles
In the project settings in CLion, we create multiple CMake profiles; one per PHP version.
Create a new profile and edit the environment settings. We'll modify $PATH
so the correct php
, phpize
and php-config
executables are used.
Copy the existing PATH
entry and paste it as custom env var. Prepend the value with the path to the bin
dir of the PHP version you want to use.
PHP CLI symlink
CLion will only run configure with the selected PHP version. To run tests and debug we need to specify the path of the php cli executable. Instead of having to modify this every time, we can have autoconf create a symlink in the build
subdir of this project.
AC_CONFIG_COMMANDS_POST([
ln -s "$PHP_EXECUTABLE" build/php
])
Build configure
Select 'configure' in build configurations with one of the PHP versions and build Ctrl+F9
.
make
CLion has an automake plugin. We won't use this to build the project. The plugin runs make
as command and it doesn't work with the build functionality of CLion.
Custom build target
In the project settings go to the "Custom Build Targets" tab and add a 'make' build target.
For Build
click on the '...' to create an external command to the make
executable. Make sure the working directory is set to the project dir.
Duplicate the external command for Clean
, adding clean
as argument.
Custom build configurations
make
Edit the build configurations to add a new one, choosing "Custom Build Configuration". Name the configuration "make" and select make
as Target
.
As executable select php
located in the build
directory of the project (after you've run configure). Set program arguments to include your extension. It's recommended to use -n
not to ignore the default php.ini and thus not load any other extension.
-n -d "extension=modules/skeleton.so" "tests/smoke.php"
We need to change which file is run manually for debugging.
In the "Before launch" section add Build
, so make
is called before running php
when there are changed source files.
make test
PHP extension tests are written as phpt file and run with make test
. Writing tests is highly recommended and required for submitting the extension to PECL.
Duplicate the make
build configuration and name it "make test". Change the executable to make
(instead of php
) and set the program arguments to test
.
The working directory needs to be set to the project directory.
Add NO_INTERACTION=1
to the environment variables to prevent the "Do you want to submit the test results?" question after running the tests.
Clean toolbar button
It may help to add Clean
(Ctrl-Shift-F9
) to the toolbar.
Optional Makefile targets
The following is optional but will help out during development.
make info
Add a "Makefile" build configuration named "make info". Select the Makefile
of the project and choose info
as target.
make clean-tests
When a test fails .log
, .diff
, etc files are created to aid in solving the issue. These files are automatically when the test passes.
To explicitly remove the files, we need to add a custom target to the makefile by creating a Makefile.frag
file and adding
clean-tests:
rm -f tests/*.diff tests/*.exp tests/*.log tests/*.out tests/*.php tests/*.sh
After, add a "Makefile" build configuration named "make clean-tests". Select the Makefile
of the project and choose clean-tests
as target.
make mrproper
make clean
removes the build files, but still leaves all automake artifacts. To remove all generated files add a mrproper
target to the makefile via Makefile.frag
;
mrproper: clean
rm -rf autom4te.cache build modules vendor
rm -f acinclude.m4 aclocal.m4 config.guess config.h config.h.in config.log config.nice config.status config.sub \
configure configure.ac install-sh libtool ltmain.sh Makefile Makefile.fragments Makefile.global \
Makefile.objects missing mkinstalldirs run-tests.php
Top comments (1)
Thanks to this article I can now do debugging runs in Clion. I am certain that I could never have done it without this article. Thank you for the great article!