Skip to content

feat: add PHP framework integration primitives - #282

Open
marandaneto wants to merge 5 commits into
mainfrom
feat/php-framework-integration
Open

marandaneto wants to merge 5 commits into
mainfrom
feat/php-framework-integration

Conversation

@marandaneto

@marandaneto marandaneto commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

💡 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:

  • Add Client::groupIdentify() and delegate the static facade to it.
  • Add enterContext() and an idempotent closeable scope. Scopes preserve client and Fiber isolation, including cleanup from another execution path.
  • Allow framework exception capture to supply handled/mechanism metadata while preserving the original exception chain and stack. Existing manual capture stays handled by default.
  • Add @internal protected Client::libraryName(), libraryVersion() and releaseId() overrides for PostHog framework integrations (library metadata, and release IDs from cached application configuration). These are not constructor options, so customers keep POSTHOG_RELEASE_ID as 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?

  • Added framework tests first and confirmed the missing APIs and handled-state assertion failed before implementation.
  • Full PHP suite after review fixes: 2,530 tests, 11,252 assertions.
  • Focused framework and configured batch transport suites: 48 tests, 211 assertions.
  • PHP_CodeSniffer for all changed PHP files, API snapshot check and git diff --check pass.
  • Initial isolated autoreview of 34e07100d9ce06f2c56312a9d1072a59574ffed8 against origin/main reported no actionable findings.

Review follow-up 8773c43 addresses 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

  • All commits are signed.
  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran pnpm change to 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:

  1. feat: add PHP framework integration primitives #282: PHP framework support
  2. feat: add the Laravel runtime package #283: Laravel runtime
  3. feat: add opt-in Laravel autocapture #284: Optional server autocapture
  4. feat: integrate PostHog feature flags with Laravel Pennant #285: Read-only Pennant integration
  5. feat: add a runnable Laravel SDK example #286: Runnable Laravel example
  6. ci: release the Laravel package independently #287: Independent CI and publishing

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 ecbc8cc supplies 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.

@marandaneto marandaneto self-assigned this Oct 29, 2026

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread lib/Client.php Outdated
Comment thread lib/RequestContext.php Outdated
Comment thread lib/Client.php
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-fork_curl Compliance Report

Date: 2026-10-29T12:26:18.154751+00:00
Duration: 115989ms

⚠️ Some Tests Failed

46/47 tests passed, 1 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
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

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-lib_curl Compliance Report

Date: 2026-10-29T12:26:11.816474+00:00
Duration: 115682ms

⚠️ Some Tests Failed

46/47 tests passed, 1 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
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

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-socket Compliance Report

Date: 2026-10-29T12:26:09.049904+00:00
Duration: 116194ms

⚠️ Some Tests Failed

46/47 tests passed, 1 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
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.
@marandaneto
marandaneto marked this pull request as ready for review October 29, 2026 12:24
@marandaneto
marandaneto requested a review from a team as a code owner October 29, 2026 12:24

This branch has not been deployed

No deployments
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.

1 participant