Repository navigation
feat: add PHP framework integration primitives - #282
marandaneto wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 34e0710: 3 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.
posthog-php-fork_curl Compliance ReportDate: 2026-10-29T12:26:18.154751+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 35ms |
| Format Validation.Event Has Uuid | ✅ | 531ms |
| Format Validation.Event Has Lib Properties | ✅ | 531ms |
| Format Validation.Distinct Id Is String | ✅ | 531ms |
| Format Validation.Token Is Present | ✅ | 531ms |
| Format Validation.Custom Properties Preserved | ✅ | 531ms |
| Format Validation.Event Has Timestamp | ✅ | 533ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 531ms |
| Retry Behavior.Retries On 503 | ✅ | 5856ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2534ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2534ms |
| Retry Behavior.Respects Retry After Header | ❌ | 6347ms |
| Retry Behavior.Implements Backoff | ✅ | 16274ms |
| Retry Behavior.Retries On 500 | ✅ | 5646ms |
| Retry Behavior.Retries On 502 | ✅ | 5646ms |
| Retry Behavior.Retries On 504 | ✅ | 5648ms |
| Retry Behavior.Max Retries Respected | ✅ | 16267ms |
| Deduplication.Generates Unique Uuids | ✅ | 46ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5645ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 10861ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 5650ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 539ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 532ms |
| Compression.Sends Gzip When Enabled | ✅ | 533ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 530ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 520ms |
| Batch Format.Multiple Events Batched Together | ✅ | 519ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2533ms |
| Error Handling.Does Not Retry On 413 | ✅ | 2535ms |
| Error Handling.Retries On 408 | ✅ | 5645ms |
Failures
retry_behavior.respects_retry_after_header
Retry delay too short: 810ms < 2500ms
Feature_Flags Tests
✅ 17/17 tests passed
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 524ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 523ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 523ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 522ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 522ms |
| Request Payload.Groups Round Trip | ✅ | 522ms |
| Request Payload.Groups Default To Empty Object | ✅ | 523ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 523ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 522ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 522ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 518ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 516ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 527ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 523ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 625ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 623ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 534ms |
posthog-php-lib_curl Compliance ReportDate: 2026-10-29T12:26:11.816474+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 20ms |
| Format Validation.Event Has Uuid | ✅ | 516ms |
| Format Validation.Event Has Lib Properties | ✅ | 517ms |
| Format Validation.Distinct Id Is String | ✅ | 518ms |
| Format Validation.Token Is Present | ✅ | 517ms |
| Format Validation.Custom Properties Preserved | ✅ | 517ms |
| Format Validation.Event Has Timestamp | ✅ | 518ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 517ms |
| Retry Behavior.Retries On 503 | ✅ | 5824ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2522ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2521ms |
| Retry Behavior.Respects Retry After Header | ❌ | 6326ms |
| Retry Behavior.Implements Backoff | ✅ | 16237ms |
| Retry Behavior.Retries On 500 | ✅ | 5626ms |
| Retry Behavior.Retries On 502 | ✅ | 5626ms |
| Retry Behavior.Retries On 504 | ✅ | 5625ms |
| Retry Behavior.Max Retries Respected | ✅ | 17039ms |
| Deduplication.Generates Unique Uuids | ✅ | 222ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5624ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 10823ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 5135ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 523ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 519ms |
| Compression.Sends Gzip When Enabled | ✅ | 518ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 517ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 514ms |
| Batch Format.Multiple Events Batched Together | ✅ | 509ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2521ms |
| Error Handling.Does Not Retry On 413 | ✅ | 2520ms |
| Error Handling.Retries On 408 | ✅ | 5625ms |
Failures
retry_behavior.respects_retry_after_header
Retry delay too short: 803ms < 2500ms
Feature_Flags Tests
✅ 17/17 tests passed
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 518ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 515ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 517ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 515ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 517ms |
| Request Payload.Groups Round Trip | ✅ | 517ms |
| Request Payload.Groups Default To Empty Object | ✅ | 517ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 520ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 515ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 517ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 512ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 508ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 519ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 516ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 618ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 617ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 517ms |
posthog-php-socket Compliance ReportDate: 2026-10-29T12:26:09.049904+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 29ms |
| Format Validation.Event Has Uuid | ✅ | 523ms |
| Format Validation.Event Has Lib Properties | ✅ | 525ms |
| Format Validation.Distinct Id Is String | ✅ | 525ms |
| Format Validation.Token Is Present | ✅ | 525ms |
| Format Validation.Custom Properties Preserved | ✅ | 524ms |
| Format Validation.Event Has Timestamp | ✅ | 525ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 526ms |
| Retry Behavior.Retries On 503 | ✅ | 5834ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2528ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2529ms |
| Retry Behavior.Respects Retry After Header | ❌ | 6333ms |
| Retry Behavior.Implements Backoff | ✅ | 16247ms |
| Retry Behavior.Retries On 500 | ✅ | 5635ms |
| Retry Behavior.Retries On 502 | ✅ | 5632ms |
| Retry Behavior.Retries On 504 | ✅ | 5633ms |
| Retry Behavior.Max Retries Respected | ✅ | 16248ms |
| Deduplication.Generates Unique Uuids | ✅ | 533ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5633ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 10838ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 5639ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 531ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 526ms |
| Compression.Sends Gzip When Enabled | ✅ | 525ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 525ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 520ms |
| Batch Format.Multiple Events Batched Together | ✅ | 515ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2527ms |
| Error Handling.Does Not Retry On 413 | ✅ | 2528ms |
| Error Handling.Retries On 408 | ✅ | 5633ms |
Failures
retry_behavior.respects_retry_after_header
Retry delay too short: 803ms < 2500ms
Feature_Flags Tests
✅ 17/17 tests passed
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 524ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 523ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 523ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 523ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 523ms |
| Request Payload.Groups Round Trip | ✅ | 523ms |
| Request Payload.Groups Default To Empty Object | ✅ | 523ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 523ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 522ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 523ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 518ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 508ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 529ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 524ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 626ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 625ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 526ms |
Remove library_name, library_version and release_id from the public client options. Framework integrations override internal Client methods instead, so customers keep POSTHOG_RELEASE_ID as the only documented release id input.
💡 Motivation and Context
Framework integrations need to keep context active across before/after callbacks, report failed boundaries accurately, and use a client instance without changing the global default.
This is the first PR in a Laravel integration stack. It adds reusable PHP SDK support only:
Client::groupIdentify()and delegate the static facade to it.enterContext()and an idempotent closeable scope. Scopes preserve client and Fiber isolation, including cleanup from another execution path.@internalprotectedClient::libraryName(),libraryVersion()andreleaseId()overrides for PostHog framework integrations (library metadata, and release IDs from cached application configuration). These are not constructor options, so customers keepPOSTHOG_RELEASE_IDas the only documented release ID input. Event properties still take precedence.Public API changes are recorded in the API snapshot. Capture, group-identify, exception metadata and tracing-header specs apply. The optional framework boundary metadata extends the manual exception API without changing its default handled behavior.
💚 How did you test it?
git diff --checkpass.34e07100d9ce06f2c56312a9d1072a59574ffed8againstorigin/mainreported no actionable findings.Review follow-up
8773c43addresses all three bot findings with failing-before/passing-after regressions: configured batch User-Agents (including safe shell/header handling), abandoned Fiber cleanup without retention or object-ID identity reuse, and independently validated exception metadata overrides. The 16 adapter tests also pass. These fixes follow the HTTP client and exception-event-metadata contracts; unrelated Retry-After compliance report failures remain outside this framework PR.📝 Checklist
If releasing new changes
pnpm changeto generate a change intent file.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Implemented with Pi using local file tools, PHP/Composer, Git and GitHub CLI. A separate isolated Pi review checked the final core diff. The user requested an independently released Laravel package depending on the PHP SDK, with stacked PRs and an example application. Framework lifecycle and payload support live in the core so the package does not duplicate them. Human review is required before merge.
Stack
Merge bottom-up:
Laravel source lives in this repository but its Composer package is released separately. Publish core ^4.20 first. The distribution repository, protected release environment and Packagist setup remain administrator prerequisites. No release has been published.
Follow-up
ecbc8ccsupplies the internal context-bound snapshot host needed by #283's lazy-exposure feedback. Four regressions failed before the fix (later-session leakage and unscoped snapshots inheriting later context), then passed; the combined flag/framework suites pass 491 tests / 1,899 assertions. A fresh read-only review of this exact commit found no actionable issues. API shape is unchanged. PHP 8.2–8.5, coverage, style, API, and security CI checks pass.