refactor: C23 modernization wave 2#133
Merged
Merged
Conversation
Continues the C++ → C23 migration with a sweep of language features,
attribute hardening, gen_stub annotations, and lint policy.
Language features
- nullptr replaces NULL throughout hand-written code (auto-generated
*_arginfo.h files stay on NULL until upstream gen_stub catches up).
- `auto` (C23) for ~370 type-redundant locals where the macro/function
already encodes the type: `php_scylladb_X *self = PHP_SCYLLADB_GET_X(...)`
→ `auto self = PHP_SCYLLADB_GET_X(...)`.
- `[[maybe_unused]]` replaces `(void)param;` suppressions.
- `= {}` (C23 empty initializer) replaces `= {0}`.
- `static constexpr` for true compile-time constants (FNV table, default
consistency) where the value isn't consumed by the preprocessor.
- `typeof` + statement expression in SAFE_STR / SAFE_ZEND_STRING to avoid
double-evaluation of side-effecting arguments.
Attribute hardening
- [[nodiscard, gnu::nonnull(...)]] on public *_instantiate / *_initialize
APIs and other helpers with required pointer args.
- [[gnu::const]] / [[gnu::pure]] on the FNV cache-key helpers and on
exception_class() — they're pure mappings, this lets the optimizer
CSE repeated calls.
- PHP_SCYLLADB_CLEANUP(fn) wrapper over __attribute__((cleanup(...))) so
RAII-style scope cleanup is one line. Adopted at 10 sites in
DefaultSession.c's bind_argument paths (varint/decimal/collection/
map/collection/tuple/user-type intermediaries): removes manual rc-then-
free ladders, preserves exception safety.
- PHP_SCYLLADB_LIKELY / PHP_SCYLLADB_UNLIKELY branch-prediction macros.
Stub-level codegen improvements
- @cvalue annotations on Cassandra.stub.php's 24 enum-backed constants
(CONSISTENCY_*, VERIFY_*, BATCH_*, LOG_*, VERSION). The generated
arginfo now emits ZVAL_LONG(&val, CASS_CONSISTENCY_ANY) instead of a
magic 0x0, so values stay in lockstep with the linked driver.
- @not-serializable on all 29 concrete classes that hold a raw Cass*
pointer. Closes a UAF: default zend serialize would dehydrate via
get_properties (no native handle), then unserialize would create a
fresh instance whose pointer is uninitialized; the next method call
was a crash. Now PHP throws "Serialization not allowed" explicitly.
Bind dispatcher
- bind_argument_by_index / bind_argument_by_name: replaced the primitive
if-chain with a switch on Z_TYPE_P. Single read, compiler emits a jump
table. Object branch unchanged (instanceof can't be switched).
Build / lint
- .clang-tidy: enable modernize-use-nullptr / -use-bool-literals /
-macro-to-enum plus a few targeted readability checks.
- cmake/TargetOptimizations.cmake: bump C standard to c_std_23, keep
C++20 for residual .cpp files until they're ported.
- Dropped dead extern "C" guards in headers we now own; PHP-style
BEGIN_EXTERN_C / END_EXTERN_C kept where the convention applies.
Portability fix
- Removed `typedef unsigned long ulong;` from php_scylladb.h. `ulong` is
not POSIX (needs _BSD_SOURCE on glibc) and zend_hash_* already takes
zend_ulong; all 8 call sites migrated.
Hash iteration
- Tuple.c / UserTypeValue.c: switched to the key-only foreach variants
(ZEND_HASH_FOREACH_NUM_KEY / _STR_KEY) where the value pointer was
unused. Drops two (void)current; suppressions and dead locals.
Build verification
- Not run locally — toolchain needs libuv + cpp-driver + a compiled PHP
source tree + a running ScyllaDB. The cleanup-attribute paths and
gen_stub-emitted code are the highest-attention items; please run
cmake --build out/DebugPHP8.4NTS && composer install && pest before
merging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three CI breakages, three localized fixes: 1. clang-tidy chokes on `constexpr` despite -std=c23 — its frontend doesn't pick up the C23 flag from the compilation database in the way clang(1) does. Revert the FNV constants and DEFAULT_CONSISTENCY to `#define`. The gain was cosmetic; revisit once the lint job's std flag is sorted. 2. readability-else-after-return fires as warnings-as-error on pre-existing `} else if (...)` after `return` chains in TypeFactory and elsewhere. Disable the check — fixing the call sites is a separate cleanup, not in scope for this PR. 3. Cassandra_descriptor.c was missing <cassandra.h> + <version.h>, so the new @cvalue references (CASS_CONSISTENCY_*, PHP_SCYLLADB_VERSION) were undeclared in that TU. Switch the descriptor template to include "php_scylladb.h" — the umbrella already brings every identifier any @cvalue might reference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SAFE_STR / SAFE_ZEND_STRING used `({ typeof(a) _v = (a); _v ? _v : ""; })`
to dodge double-evaluation, but statement expressions are a GNU extension
and trip -Wpedantic on the CI compiler.
Switch to `static inline` helpers behind the same macro names:
[[gnu::const]] static inline const char *php_scylladb_safe_str(const char *);
[[gnu::pure]] static inline const char *php_scylladb_safe_zend_string(const zend_string *);
Function-call evaluation evaluates the argument exactly once, so the
original safety property is preserved without leaving ISO C. Call-site
syntax `SAFE_STR(x)` / `SAFE_ZEND_STRING(x)` is unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
actions/checkout@v4 rewrites submodule URLs to use its scoped
GITHUB_TOKEN as HTTPS Basic auth. For a public submodule in a
different org (arsenm/sanitizers-cmake), the token can't satisfy the
auth challenge and the clone falls through to a terminal prompt that
isn't available in CI:
fatal: could not read Username for 'https://github.com':
terminal prompts disabled
The submodule is 15 files (six small Find*.cmake modules + a wrapper
script + tests). The upstream hasn't seen activity in years. Vendoring
removes the cross-org auth dance entirely and makes the build work
without `--recursive`.
Changes:
- Drop .gitmodules entirely (sanitizers-cmake was the only entry).
- Detach the gitlink and add the files as tracked content under
third-party/sanitizers-cmake/.
- Simplify CMakeLists.txt's sanitizer block — no more
`git submodule update --init` fallback, just an existence check
with a clear error if the vendored tree is missing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Continues the C++ → C23 migration: adopts the language features the C23 standard added for safety (
nullptr,auto,constexpr,[[attributes]],= {}), hardens the public API with[[gnu::nonnull]]/[[gnu::pure]]/[[gnu::const]]/[[nodiscard]], and pushes more correctness into the gen_stub layer so the generated arginfo files stop being a magic-number duplicator.Highlights
Correctness (these fix latent bugs)
@not-serializableon 29 native-pointer-holding classes (Cluster/Session/Statement/Future/Rows/TimestampGen/SSL/RetryPolicy/Database meta). Without this, defaultserialize($cluster) → unserialize(...)produces an object with no native handle; the next method call dereferences uninitialized memory. Now PHP refuses the operation explicitly.typeofinSAFE_STR/SAFE_ZEND_STRING— the prior((a) ? a : "")evaluatedatwice; any side-effecting argument was UB.ulongportability — removed the project-localtypedef unsigned long ulong;and migrated 8 call sites tozend_ulong(the PHP-canonical type).ulongisn't POSIX and needs_BSD_SOURCEon glibc.Stub-driven codegen
@cvaluefor all enum-backed constants inCassandra.stub.php—CONSISTENCY_*,VERIFY_*,BATCH_*,LOG_*,VERSION. The generated arginfo emitsZVAL_LONG(&val, CASS_CONSISTENCY_ANY)instead ofZVAL_LONG(&val, 0x0); values stay synchronized with cassandra.h automatically.@not-serializablewired into 29 stubs (see above).Attribute hardening
[[nodiscard, gnu::nonnull(...)]]on public*_instantiate/*_initializeAPIs (DateTime, RetryPolicy, SSLOptions, plus utility headers).[[gnu::const]] exception_class(CassError)— pure mapping, CSE-able.[[gnu::pure]]/[[gnu::const]]on the FNV cache-key helpers.PHP_SCYLLADB_CLEANUP(fn)macro wrapping__attribute__((cleanup(...))). Adopted at 10 sites inbind_argument_by_index/bind_argument_by_name— removes theCassError rc; ...; free(data); CHECK_RESULT(rc);ladder and keeps exception safety.Language modernization
nullptrinstead ofNULLacross hand-written sources (~370 sites).auto(C23) where the type is spelled twice:auto self = PHP_SCYLLADB_GET_X(zv);(~370 sites across 49 files).[[maybe_unused]]on parameter declarations instead of(void)param;suppressions.= {}(C23) replacing= {0}at the 9 sites that had it.static constexprfor true compile-time constants where the value isn't consumed by the preprocessor (FNV table, default consistency).Dispatcher refactor
bind_argument_by_index/bind_argument_by_name:switch (Z_TYPE_P(value))replaces the if-chain over primitives. One read, one jump table. Object branch unchanged.Build / lint policy
.clang-tidyenablesmodernize-use-nullptr,modernize-use-bool-literals,modernize-macro-to-enum, plus a handful ofreadability-*checks now that the codebase is C23-ready.cmake/TargetOptimizations.cmake: C standard bumped toc_std_23; C++ stays at C++20 for residual.cppfiles until ported.#ifdef __cplusplus / extern "C"guards in headers we own (no C++ TUs exist insrc/). PHP-idiomaticBEGIN_EXTERN_C/END_EXTERN_Ckept where the convention applies.What's deliberately NOT in this PR
zval x;locals — they're immediately written byZVAL_NULL/array_init/ etc., which don't read prior state. Mass= {}would emit dead xors.*_arginfo.hfiles committed — they're gitignored and regenerated bytools/gen_stub/gen_arginfo.shat build time.@frameless-function/@compile-time-eval— apply only to global functions, of which we have none.Test plan
Build verification was not run locally; the toolchain needs libuv + cpp-driver + a compiled PHP source tree + a running ScyllaDB. CI will exercise the full matrix. Highest-attention items:
cmake --preset DebugPHP8.4NTS && cmake --build out/DebugPHP8.4NTS— compiles clean across the matrix../scripts/run-scylladb.sh && composer install && php ./vendor/bin/pest— the test suite covers thebind_argument_*paths that got the dispatcher refactor and the cleanup-attribute adoption.var_dump(unserialize(serialize($cluster)))should now throwSerialization of 'Cassandra\DefaultCluster' is not allowedinstead of producing a half-constructed object.[[gnu::nonnull]]annotations may surface new compiler warnings at internal call sites where anullptrflows in unconditionally — those are real bugs worth reviewing.🤖 Generated with Claude Code