ScyllaDB PHP Driver: the story so far — Dusan Malusev
Skip to content
On this page The DataStax PHP driver for Cassandra would not compile on PHP 8. That was the whole problem.
At the time I was working at Nano Interactive. The entire backend was PHP. We were running Apache Cassandra as a primary data store — session data, event pipelines, a few hot lookup tables — and the driver that talked to it was the official DataStax extension. It worked fine on PHP 7. Then PHP 8 came out, we needed to upgrade, and the extension refused to build. Compile errors in the Zend internals layer. make spitting out pages of incompatible API warnings.
I went looking for a fix. There was none. DataStax had effectively abandoned the project — no PHP 8 tag, no branch, no response on the open issues. The last meaningful release was years old. The options were: stay on PHP 7 indefinitely, rewrite our Cassandra layer to use something else, or fix the driver ourselves.
why not just switch
Switching looked easy on paper. In practice the codebase had Cassandra-specific types everywhere — Cassandra\Uuid, Cassandra\Timestamp, Cassandra\Bigint — not because of architectural brilliance but because the extension had its own type system and those types had leaked into application code over years. Replacing the driver meant touching hundreds of files. And we had no guarantee that a different client library would give us the same behavioral semantics — retry policies, paging state, consistency levels.
There was also no other option in the PHP ecosystem. No community-maintained pure-PHP client existed that came close in performance — the extension is a thin wrapper over the ScyllaDB C/C++ driver, which means shard-aware routing and native binary protocol handling at C++ speed. A pure-PHP implementation would have been an order of magnitude slower. The realistic alternatives were: stay on PHP 7 until it became a security liability, or abandon PHP entirely and rewrite the service in another language.
Staying on PHP 7 was not a permanent answer. Security patches. Framework compatibility. PHP 7 EOL was already behind us. Rewriting the service was months of work with no guarantee the result would be better. So I forked the driver and started reading Zend source.
learning the zend engine the hard way
PHP extensions are C code that hooks into the Zend Engine through a set of macros and function tables. In principle that is not complicated. In practice the codebase I inherited had been written to support PHP 5, 6, and 7 simultaneously through a layer of compatibility macros with names like PHP5TO7_ZEND_OBJECT, PHP5TO7_ZEND_HASH_FOREACH_STR_KEY_VAL, and PHP5TO7_ZVAL_MAYBE_DESTROY. There were dozens of them, some wrapping trivial one-liners, some hiding real semantic differences between PHP versions.
The Zend Engine's object model changed significantly between PHP 7 and PHP 8. zend_parse_parameters deprecated half its format specifiers. get_properties and get_gc handlers changed signatures. And the macros — originally written to smooth over the 5→7 transition — did not account for 8 at all.
I fixed the compile errors. PHP 8.0 worked. Then 8.1. Then 8.2, which introduced new deprecations of its own — __toString prototype mismatches, zend_hash_sort API changes. Each minor version was another round of "which macro is lying to me now."
This was also when I realized the original driver had been written in C, and that C was making the maintenance problem worse. Not because C is wrong for PHP extensions — it is perfectly valid — but because the compatibility macro layer had produced code that was almost impossible to read. You needed three mental translation steps to understand what any given function actually did at runtime.
the decision i still regret
In March 2023 I renamed every .c file to .cpp. All of them. In one pull request.
The rationale made sense at the time: C++ gives you RAII, references that cannot be null, std::string instead of manual char * arithmetic, better type inference. The Zend API is a C API but you can call it from C++ with extern "C". ScyllaDB's own C/C++ driver — which we wrap — is C++ already. The pieces would fit together.
What I underestimated was how much of the existing code relied on C idioms that do not translate cleanly to C++: implicit void * casts everywhere, VLAs, designated initializers used in ways the C++ standard does not permit. The conversion produced warnings. Some warnings were bugs. Tracking down which was which took months.
And the toolchain story got harder. Finding people who were comfortable contributing to a PHP extension was already difficult. Finding people comfortable with a PHP extension written in C++ that wraps a C++ driver and uses CMake — that narrowed the pool considerably.
I would have been better off keeping C and just deleting the compatibility macro layer one file at a time. The abstraction was the problem, not the language.
cmake and the build system swamp
The original...