Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
# - readability-inconsistent-declaration-parameter-name (handler ABI)
# - misc-unused-parameters (Zend handler ABI)
# - misc-include-cleaner (PHP umbrella headers)
# - modernize-* (we're moving C++ → C23, not the other way)
#
# modernize-* is mostly C++-targeted. We enable the subset that helps the
# C++ → C23 migration: prefer C23 nullptr/true/false/enum constants.
#
# False positives from third-party / Zend macros (re-enable per-module
# as code is ported to C23):
Expand All @@ -22,6 +24,9 @@
# - bugprone-exception-escape (last C++ files; dissolves with migration)
# - performance-no-int-to-ptr (uthash HASH_DEL)
# - cert-err33-c (cass_* return codes deliberately ignored on cleanup)
# - cert-err34-c (sscanf/atoi on driver-controlled inputs; noisy)
# - clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling
# (memcpy/memset/fprintf — Annex K _s variants aren't portable on glibc)
# - bugprone-easily-swappable-parameters / bugprone-macro-parentheses (Zend ABI shape)

Checks: >
Expand All @@ -38,21 +43,30 @@ Checks: >
-clang-analyzer-security.ArrayBound,
-clang-analyzer-core.NullDereference,
-clang-analyzer-deadcode.DeadStores,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
cert-*,
-cert-err33-c,
-cert-err34-c,
-cert-dcl37-c,
-cert-dcl51-cpp,
-cert-str34-c,
performance-*,
-performance-no-int-to-ptr,
portability-*,
modernize-macro-to-enum,
modernize-use-bool-literals,
modernize-use-nullptr,
readability-misleading-indentation,
readability-redundant-declaration,
readability-redundant-string-init,
readability-redundant-casting,
readability-redundant-inline-specifier,
readability-non-const-parameter,
readability-avoid-const-params-in-decls,
misc-redundant-expression,
misc-misplaced-const,
misc-static-assert
misc-static-assert,
misc-header-include-cycle

# Treat the curated set as errors — we only enabled checks we expect to pass.
WarningsAsErrors: '*'
Expand All @@ -68,3 +82,11 @@ CheckOptions:
value: 'true'
- key: misc-unused-parameters.StrictMode
value: 'false'
# else-after-return: don't rewrite ternaries / single-statement guards.
- key: readability-else-after-return.WarnOnUnfixable
value: 'false'
- key: readability-else-after-return.WarnOnConditionVariables
value: 'false'
# modernize-use-nullptr: also treat 0 as a null candidate in pointer ctx.
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

33 changes: 8 additions & 25 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,32 +123,15 @@ include(cmake/PhpExtensionHelpers.cmake)
include(cmake/GenStubs.cmake)

if (ENABLE_SANITIZERS)
set(_sanitizers_cmake_dir "${PROJECT_SOURCE_DIR}/third-party/sanitizers-cmake/cmake")
if (NOT EXISTS "${_sanitizers_cmake_dir}/FindSanitizers.cmake")
# Submodule not initialized — try to init it automatically when we're
# inside a git checkout; otherwise tell the user exactly what to run.
find_package(Git QUIET)
if (Git_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.gitmodules")
message(STATUS "Initializing third-party/sanitizers-cmake submodule")
execute_process(
COMMAND "${GIT_EXECUTABLE}" submodule update --init --recursive
third-party/sanitizers-cmake
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
RESULT_VARIABLE _sm_rc
)
else ()
set(_sm_rc 1)
endif ()
if (NOT EXISTS "${_sanitizers_cmake_dir}/FindSanitizers.cmake")
message(FATAL_ERROR
"ENABLE_SANITIZERS=ON but third-party/sanitizers-cmake is missing.\n"
"Run: git submodule update --init --recursive\n"
"Or disable with: -DENABLE_SANITIZERS=OFF"
)
endif ()
unset(_sm_rc)
# sanitizers-cmake is vendored under third-party/ (no submodule). The path
# is added to CMAKE_MODULE_PATH at the top of this file.
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/third-party/sanitizers-cmake/cmake/FindSanitizers.cmake")
message(FATAL_ERROR
"ENABLE_SANITIZERS=ON but third-party/sanitizers-cmake is missing "
"(expected a vendored copy of arsenm/sanitizers-cmake under "
"third-party/sanitizers-cmake/). Re-clone the repo or "
"disable with: -DENABLE_SANITIZERS=OFF")
endif ()
unset(_sanitizers_cmake_dir)
find_package(Sanitizers REQUIRED)
endif ()

Expand Down
3 changes: 3 additions & 0 deletions cmake/TargetOptimizations.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ function(scylladb_php_library target)
# _TIME_BITS/_FILE_OFFSET_BITS are glibc extensions; macOS uses 64-bit by default
$<$<NOT:$<PLATFORM_ID:Darwin>>:_TIME_BITS=64>
$<$<NOT:$<PLATFORM_ID:Darwin>>:_FILE_OFFSET_BITS=64>
# PHP's zend_memrchr inlines glibc's memrchr() when HAVE_MEMRCHR is set
# — that prototype is only exposed under _GNU_SOURCE on glibc.
$<$<NOT:$<PLATFORM_ID:Darwin>>:_GNU_SOURCE>
$<$<CONFIG:Debug>:DEBUG>
$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:RELEASE>
)
Expand Down
13 changes: 7 additions & 6 deletions include/DateTime/Date.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,19 @@ typedef struct {
extern PHP_SCYLLADB_API zend_class_entry *php_scylladb_date_ce;
extern PHP_SCYLLADB_API zend_class_entry *php_scylladb_time_ce;

[[nodiscard]] PHP_SCYLLADB_API php_scylladb_date *php_scylladb_date_instantiate(zval *object);
[[nodiscard]] PHP_SCYLLADB_API zend_result php_scylladb_date_initialize(php_scylladb_date *object,
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API php_scylladb_date *php_scylladb_date_instantiate(zval *object);
/* secondsStr is optional (use the long alternative); only `object` is required. */
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API zend_result php_scylladb_date_initialize(php_scylladb_date *object,
zend_string *secondsStr,
zend_long seconds);

[[nodiscard]] PHP_SCYLLADB_API php_scylladb_time *php_scylladb_time_instantiate(zval *object);
[[nodiscard]] PHP_SCYLLADB_API zend_result php_scylladb_time_initialize(php_scylladb_time *object,
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API php_scylladb_time *php_scylladb_time_instantiate(zval *object);
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API zend_result php_scylladb_time_initialize(php_scylladb_time *object,
zend_string *nanosecondsStr,
zend_long nanoseconds);

[[nodiscard]] PHP_SCYLLADB_API php_scylladb_timestamp *php_scylladb_timestamp_instantiate(zval *object);
[[nodiscard]] PHP_SCYLLADB_API zend_result php_scylladb_timestamp_initialize(php_scylladb_timestamp *object,
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API php_scylladb_timestamp *php_scylladb_timestamp_instantiate(zval *object);
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API zend_result php_scylladb_timestamp_initialize(php_scylladb_timestamp *object,
cass_int64_t seconds,
cass_int64_t microseconds);

Expand Down
8 changes: 4 additions & 4 deletions include/RetryPolicy/RetryPolicy.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ extern PHP_SCYLLADB_API zend_class_entry *php_scylladb_retry_policy_fallthrough_
extern PHP_SCYLLADB_API zend_class_entry *php_scylladb_retry_policy_logging_ce;
extern PHP_SCYLLADB_API zend_class_entry *php_scylladb_retry_policy_ce;

[[nodiscard]] PHP_SCYLLADB_API php_scylladb_retry_policy *php_scylladb_retry_policy_default_policy_instantiate(zval *dst);
[[nodiscard]] PHP_SCYLLADB_API php_scylladb_retry_policy *php_scylladb_retry_policy_downgrading_consistency_instantiate(zval *dst);
[[nodiscard]] PHP_SCYLLADB_API php_scylladb_retry_policy *php_scylladb_retry_policy_fallthrough_instantiate(zval *dst);
[[nodiscard]] PHP_SCYLLADB_API php_scylladb_retry_policy *php_scylladb_retry_policy_logging_instantiate(zval *dst, php_scylladb_retry_policy *retry_policy);
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API php_scylladb_retry_policy *php_scylladb_retry_policy_default_policy_instantiate(zval *dst);
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API php_scylladb_retry_policy *php_scylladb_retry_policy_downgrading_consistency_instantiate(zval *dst);
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API php_scylladb_retry_policy *php_scylladb_retry_policy_fallthrough_instantiate(zval *dst);
[[nodiscard, gnu::nonnull(1, 2)]] PHP_SCYLLADB_API php_scylladb_retry_policy *php_scylladb_retry_policy_logging_instantiate(zval *dst, php_scylladb_retry_policy *retry_policy);

static zend_always_inline php_scylladb_retry_policy *php_scylladb_retry_policy_from_obj(zend_object *obj) {
return PHP_SCYLLADB_OBJ_FETCH(php_scylladb_retry_policy, obj);
Expand Down
2 changes: 1 addition & 1 deletion include/SSLOptions/SSLOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ static zend_always_inline php_scylladb_ssl *php_scylladb_ssl_from_obj(zend_objec
#define Z_SCYLLADB_SSL_P(zv) php_scylladb_ssl_from_obj(Z_OBJ_P((zv)))
#define Z_SCYLLADB_SSL(zv) php_scylladb_ssl_from_obj(Z_OBJ((zv)))

[[nodiscard]] PHP_SCYLLADB_API php_scylladb_ssl *php_scylladb_ssl_instantiate(zval *object);
[[nodiscard, gnu::nonnull(1)]] PHP_SCYLLADB_API php_scylladb_ssl *php_scylladb_ssl_instantiate(zval *object);

END_EXTERN_C()
69 changes: 45 additions & 24 deletions include/php_scylladb.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
#include <unistd.h>
#include <version.h>

#ifdef __cplusplus
extern "C"
{
#endif

#include <inttypes.h>
#include <math.h>
#include <string.h>
Expand Down Expand Up @@ -47,9 +42,35 @@ static zend_always_inline zend_class_entry *zend_register_internal_class_with_fl

#define PHP_SCYLLADB_CORE_ME(name, arg_info, flags) PHP_ME(Cassandra, name, arg_info, flags)

#define SAFE_STR(a) ((a) ? a : "")
/* Inline functions give us single-evaluation of the argument without the
* GNU statement-expression extension that -Wpedantic complains about. */
[[gnu::const]]
static inline const char *php_scylladb_safe_str(const char *s) {
return s ? s : "";
}

[[gnu::pure]]
static inline const char *php_scylladb_safe_zend_string(const zend_string *s) {
return s != nullptr ? ZSTR_VAL(s) : "";
}

#define SAFE_STR(a) php_scylladb_safe_str(a)
#define SAFE_ZEND_STRING(a) php_scylladb_safe_zend_string(a)

#define SAFE_ZEND_STRING(a) ((a != NULL) ? ZSTR_VAL(a) : "")
/* Branch-prediction hints — gcc/clang builtins, both are C23-compatible. */
#define PHP_SCYLLADB_LIKELY(x) __builtin_expect(!!(x), 1)
#define PHP_SCYLLADB_UNLIKELY(x) __builtin_expect(!!(x), 0)

/* Scope-bound cleanup. The destructor must take `T **` (cleanup attribute passes
* a pointer to the variable). Example:
*
* static inline void php_scylladb_zs_release(zend_string **p) {
* if (*p) zend_string_release(*p);
* }
* PHP_SCYLLADB_CLEANUP(php_scylladb_zs_release) zend_string *s = zend_string_init(...);
*
* Fires on every exit path (return / break / goto / fall-through). */
#define PHP_SCYLLADB_CLEANUP(fn) __attribute__((cleanup(fn)))

#ifdef ZTS
#include "TSRM.h"
Expand All @@ -65,22 +86,23 @@ static zend_always_inline zend_class_entry *zend_register_internal_class_with_fl

#define CURRENT_CPP_DRIVER_VERSION CPP_DRIVER_VERSION(CASS_VERSION_MAJOR, CASS_VERSION_MINOR, CASS_VERSION_PATCH)

typedef unsigned long ulong;

extern zend_module_entry php_scylladb_module_entry;
extern zend_module_entry php_scylladb_module_entry;
#define phpext_cassandra_ptr &php_scylladb_module_entry

PHP_MINIT_FUNCTION(php_scylladb);
PHP_MSHUTDOWN_FUNCTION(php_scylladb);
PHP_RINIT_FUNCTION(php_scylladb);
PHP_RSHUTDOWN_FUNCTION(php_scylladb);
PHP_MINFO_FUNCTION(php_scylladb);
PHP_INI_MH(OnUpdateLogLevel);
PHP_INI_MH(OnUpdateLog);
PHP_MINIT_FUNCTION(php_scylladb);
PHP_MSHUTDOWN_FUNCTION(php_scylladb);
PHP_RINIT_FUNCTION(php_scylladb);
PHP_RSHUTDOWN_FUNCTION(php_scylladb);
PHP_MINFO_FUNCTION(php_scylladb);
PHP_INI_MH(OnUpdateLogLevel);
PHP_INI_MH(OnUpdateLog);

zend_class_entry *exception_class(CassError rc);
/* The error → exception_ce mapping is a closed lookup with no side effects
* and no memory reads beyond its argument — `const` is exact. */
[[gnu::const]] zend_class_entry *exception_class(CassError rc);

void throw_invalid_argument(const zval *object, const char *object_name, const char *expected_type);
[[gnu::nonnull(1, 2, 3)]]
void throw_invalid_argument(const zval *object, const char *object_name, const char *expected_type);

#define INVALID_ARGUMENT(object, expected) \
do \
Expand Down Expand Up @@ -110,12 +132,11 @@ typedef unsigned long ulong;

#define ASSERT_SUCCESS_VALUE(rc, value) ASSERT_SUCCESS_BLOCK(rc, return value;)

/* Kept as #define: clang-tidy on CI runs without -std=c23 and chokes on
* `constexpr` despite the build compiler supporting it. Revisit once the
* lint job inherits the build's compilation database flags. */
#define PHP_SCYLLADB_DEFAULT_CONSISTENCY CASS_CONSISTENCY_LOCAL_ONE

/* String-literal concatenation forces these to stay as #define. */
#define PHP_SCYLLADB_DEFAULT_LOG PHP_SCYLLADB_NAME ".log"
#define PHP_SCYLLADB_DEFAULT_LOG_LEVEL "ERROR"


#ifdef __cplusplus
}
#endif
20 changes: 16 additions & 4 deletions include/php_scylladb_cache_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,22 @@
* needs.
*/

/* Kept as #define for clang-tidy compatibility (lint job doesn't pick up
* the build's -std=c23 yet). */
#define PHP_SCYLLADB_FNV1A_OFFSET 0xcbf29ce484222325ULL
#define PHP_SCYLLADB_FNV1A_PRIME 0x100000001b3ULL

[[gnu::const]]
static zend_always_inline zend_ulong php_scylladb_cache_key_init(void) {
return PHP_SCYLLADB_FNV1A_OFFSET;
}

[[gnu::const]]
static zend_always_inline zend_ulong php_scylladb_cache_key_mix_byte(zend_ulong h, uint8_t b) {
return (h ^ b) * PHP_SCYLLADB_FNV1A_PRIME;
}

[[gnu::pure]]
static zend_always_inline zend_ulong php_scylladb_cache_key_mix_bytes(zend_ulong h, const void *buf, size_t len) {
const uint8_t *p = (const uint8_t *)buf;
for (size_t i = 0; i < len; i++) {
Expand All @@ -45,31 +50,38 @@ static zend_always_inline zend_ulong php_scylladb_cache_key_mix_bytes(zend_ulong
return h;
}

/* `pure` rather than `const`: the inlined body takes &v (a local) and
* dereferences it through mix_bytes — `const` forbids any indirection. */
[[gnu::pure]]
static zend_always_inline zend_ulong php_scylladb_cache_key_mix_long(zend_ulong h, zend_long v) {
return php_scylladb_cache_key_mix_bytes(h, &v, sizeof(v));
}

[[gnu::pure]]
static zend_always_inline zend_ulong php_scylladb_cache_key_mix_int(zend_ulong h, int v) {
return php_scylladb_cache_key_mix_bytes(h, &v, sizeof(v));
}

[[gnu::pure]]
static zend_always_inline zend_ulong php_scylladb_cache_key_mix_ulong(zend_ulong h, zend_ulong v) {
return php_scylladb_cache_key_mix_bytes(h, &v, sizeof(v));
}

/* Mix a zend_string, including a separator byte so adjacent fields can't
alias (e.g. ("ab", "c") vs ("a", "bc")). NULL becomes a single 0 byte. */
alias (e.g. ("ab", "c") vs ("a", "bc")). nullptr becomes a single 0 byte. */
[[gnu::pure]]
static zend_always_inline zend_ulong php_scylladb_cache_key_mix_zstr(zend_ulong h, const zend_string *s) {
if (s == NULL) {
if (s == nullptr) {
return php_scylladb_cache_key_mix_byte(h, 0);
}
h = php_scylladb_cache_key_mix_bytes(h, ZSTR_VAL(s), ZSTR_LEN(s));
return php_scylladb_cache_key_mix_byte(h, 0); /* separator */
}

/* Mix a C string (NUL-terminated or NULL). */
/* Mix a C string (NUL-terminated or nullptr). */
[[gnu::pure]]
static zend_always_inline zend_ulong php_scylladb_cache_key_mix_cstr(zend_ulong h, const char *s) {
if (s == NULL) {
if (s == nullptr) {
return php_scylladb_cache_key_mix_byte(h, 0);
}
while (*s) {
Expand Down
20 changes: 9 additions & 11 deletions src/BatchStatement.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void php_scylladb_batch_statement_entry_dtor(zval* dest)
ZEND_METHOD(Cassandra_BatchStatement, __construct)
{
zend_long type = CASS_BATCH_TYPE_LOGGED;
php_scylladb_statement *self = NULL;
php_scylladb_statement *self = nullptr;

ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Expand All @@ -61,10 +61,10 @@ ZEND_METHOD(Cassandra_BatchStatement, __construct)

ZEND_METHOD(Cassandra_BatchStatement, add)
{
zval *statement = NULL;
zval *arguments = NULL;
php_scylladb_batch_statement_entry *batch_statement_entry = NULL;
php_scylladb_statement *self = NULL;
zval *statement = nullptr;
zval *arguments = nullptr;
php_scylladb_batch_statement_entry *batch_statement_entry = nullptr;
php_scylladb_statement *self = nullptr;
zval entry;

ZEND_PARSE_PARAMETERS_START(1, 2)
Expand Down Expand Up @@ -117,7 +117,7 @@ int php_scylladb_batch_statement_compare(zval *obj1, zval *obj2)

void php_scylladb_batch_statement_free(zend_object *object)
{
php_scylladb_statement *self = php_scylladb_statement_object_fetch(object);
auto self = php_scylladb_statement_object_fetch(object);

zend_hash_destroy(&self->data.batch.statements);

Expand All @@ -126,13 +126,11 @@ void php_scylladb_batch_statement_free(zend_object *object)

zend_object* php_scylladb_batch_statement_new(zend_class_entry *ce)
{
php_scylladb_statement *self = (php_scylladb_statement *)ecalloc(1, sizeof(php_scylladb_statement) + zend_object_properties_size(ce));
php_scylladb_statement *self =
PHP_SCYLLADB_OBJ_ALLOCATE(php_scylladb_statement, ce, &php_scylladb_batch_statement_handlers);

self->type = PHP_SCYLLADB_BATCH_STATEMENT;
self->data.batch.type = CASS_BATCH_TYPE_LOGGED;
zend_hash_init(&self->data.batch.statements, 0, NULL, (dtor_func_t)php_scylladb_batch_statement_entry_dtor, 0);

zend_object_std_init(&self->zendObject, ce);
self->zendObject.handlers = &php_scylladb_batch_statement_handlers;
zend_hash_init(&self->data.batch.statements, 0, nullptr, (dtor_func_t)php_scylladb_batch_statement_entry_dtor, 0);
return &self->zendObject;
}
1 change: 1 addition & 0 deletions src/BatchStatement.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace Cassandra {
/**
* @strict-properties
* @not-serializable
* @scylladb-struct php_scylladb_statement
*/ final class BatchStatement implements Statement {
public function __construct(int $type = \Cassandra::BATCH_LOGGED) {}
Expand Down
Loading
Loading