Skip to content

feat(observer): allow zend_extension registration for low level function#678

Merged
ptondereau merged 1 commit intomasterfrom
feat/zend-extension
Apr 15, 2026
Merged

feat(observer): allow zend_extension registration for low level function#678
ptondereau merged 1 commit intomasterfrom
feat/zend-extension

Conversation

@ptondereau
Copy link
Copy Markdown
Member

@ptondereau ptondereau commented Feb 13, 2026

Description

Closes the API gap for building zend_extension-style profilers (APM, coverage tools, line-level timers) on top of ext-php-rs. Until now the crate only exposed the Observer API (function-call level). This PR adds dual registration so a single extension can also hook the engine's compile-time and per-statement callbacks.

What you get

module
    .zend_extension(|| MyProfiler::new())
    .hook_statements()
    .hook_fcalls()
    .finish()

The handler trait carries all hooks with default no-op implementations; the builder decides which ones the engine will actually call. Enabling a hook also flips the matching PHP compiler flag so the right opcodes get emitted.

Why the opt-in

hook_statements() and hook_fcalls() tell the PHP engine to emit extra opcodes in every compiled script (ZEND_EXT_STMT, ZEND_EXT_FCALL_BEGIN/END). Paying that tax by default would slow every PHP script, even when your profiler doesn't need the data. The builder makes the trade-off explicit. hook_op_array_compile() has no compile-time cost because its flag is already set by PHP's defaults; it only controls whether the dispatcher is registered.

ZTS

Flags are re-asserted in on_activate, which is per-request-per-thread, so worker threads created after MINIT still get them. Scripts pre-compiled by opcache before a thread's first activation may miss hooks; load the extension via zend_extension=... in php.ini if you need 100% coverage there.

Docs

Guide section at guide/src/advanced/observer.md#zend-extension-handler rewritten. Example at examples/zend_extension.rs updated. All trait methods renamed to on_* prefix for consistency with sibling observers.

Checklist

  • I have read the contribution guidelines and the code of conduct.
  • I have added tests that prove my code works as expected.
  • I have added documentation if applicable.
  • I have added a migration guide if applicable. (N/A: the earlier zend_extension_handler(factory) API shipped only on this unmerged branch, no release notes or published docs referenced it.)

Comment thread src/zend/zend_extension.rs
@coveralls
Copy link
Copy Markdown

coveralls commented Feb 13, 2026

Coverage Report for CI Build 24467964023

Coverage decreased (-0.2%) to 65.895%

Details

  • Coverage decreased (-0.2%) from the base build.
  • Patch coverage: 109 uncovered changes across 2 files (137 of 246 lines covered, 55.69%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
src/zend/zend_extension.rs 227 123 54.19%
src/zend/mod.rs 5 0 0.0%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 12995
Covered Lines: 8563
Line Coverage: 65.89%
Coverage Strength: 32.96 hits per line

💛 - Coveralls

@ptondereau ptondereau force-pushed the feat/zend-extension branch from 5026489 to 6c49878 Compare April 15, 2026 16:14
@github-actions
Copy link
Copy Markdown

🐰 Bencher Report

Branchfeat/zend-extension
TestbedPHP 8.4.19 (cli) (built: Mar 13 2026 01:28:58) (NTS)

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

🐰 View full continuous benchmarking report in Bencher

Add `ModuleBuilder::zend_extension(factory)` returning a `ZendExtensionBuilder`
with `hook_statements()`, `hook_fcalls()`, and `hook_op_array_compile()`
opt-ins. Only opted-in hooks are wired to PHP and only their matching
ZEND_COMPILE_* flags are set, so unused hooks cost nothing at compile time.
Compiler flags are also re-asserted in `on_activate` for ZTS worker threads.

Internal wiring now goes through `CompilerGlobals::get_mut()` and a pub(crate)
`zend::register_extension()` helper rather than raw ffi from the feature
module.
@ptondereau ptondereau force-pushed the feat/zend-extension branch from 6c49878 to 480e5d9 Compare April 15, 2026 17:13
@github-actions
Copy link
Copy Markdown

🐰 Bencher Report

Branchfeat/zend-extension
TestbedPHP 8.4.20 (cli) (built: Apr 11 2026 00:52:25) (NTS)

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

🐰 View full continuous benchmarking report in Bencher

@ptondereau ptondereau marked this pull request as ready for review April 15, 2026 17:24
@ptondereau ptondereau merged commit 0b8bf6a into master Apr 15, 2026
69 checks passed
@ptondereau ptondereau deleted the feat/zend-extension branch April 15, 2026 17:34
@Xenira Xenira mentioned this pull request Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants