Skip to content

feat: add a runnable Laravel SDK example - #286

Draft
marandaneto wants to merge 15 commits into
feat/laravel-pennantfrom
feat/laravel-example
Draft

marandaneto wants to merge 15 commits into
feat/laravel-pennantfrom
feat/laravel-example

Conversation

@marandaneto

@marandaneto marandaneto commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

💡 Motivation and Context

This follows the Pennant integration PR and adds a runnable Laravel 12/13 application under examples/laravel using both local Composer packages.

The dashboard demonstrates explicit/ambient capture, identify/alias/groups, manual and framework errors, flag variants/payloads/snapshots/unresolved reasons, Pennant reads, auth/header precedence, queue context and overrides, model/domain events, and explicit flush. Artisan and scheduled commands exercise the console paths. Environment toggles enable each optional collector independently.

The baseline needs no npm, external database, queue server or PostHog credentials. It uses SQLite and defaults to disabled telemetry with a blank key. Composer path repositories are development-only. No production package metadata is changed here.

This application is for local development only. Its synthetic login intentionally has no password. It must not be exposed publicly. Browser setup is documented separately, with secret-key and duplicate-pageview warnings.

💚 How did you test it?

  • 29 tests, 163 assertions on both Laravel 12 and 13. There are no skipped collector tests.
  • Real SQLite commit/rollback, actual Artisan/scheduler execution, fake capture assertions, and mock-server flag/event serialization and 503 failure cases.
  • Composer installation, config cache/clear and a served HTTP dashboard smoke test pass. The dashboard has 12 forms, includes Pennant and loads no browser script.
  • The full-stack helper tests a clean committed-source copy, not the developer's installed vendor directories.
  • Initial isolated review reports no actionable findings. An earlier claim that after_commit=true broke the queue tests was rejected: Laravel's testing transaction manager deliberately allows callbacks outside application transactions. Added coverage verifies real application commit/rollback behavior without disabling that setting.

📝 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. This is an example application, not a published package change.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Implemented with Pi, PHP/Composer, Git and GitHub CLI in an isolated worktree. The application exercises public package APIs and uses synthetic data only. CLI tests explicitly enable Symfony event rerouting because Laravel suppresses it during unit tests. No live PostHog project was used. Human review is required before merge.

Reviewed commit: 0ce49ab1ed24c6f08f29ab73f551dc1379e0060c. Reviewed base: 0377f640e79547ded44cc36b2e38cbbb26c68685. Initial isolated review has no actionable findings.

The example CI follow-up uses repository-standard camel-case test names and PHP formatting. Blade templates are excluded from the PHP-only linter, and the conventional Laravel front controller is exempt only from the declarations/side-effects rule. All example tests still execute.

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.

Review feedback follow-up

Current head: 212980ce70e36e7395709b701c88e4824fb6a5d7.

The Pennant form is disabled and server-guarded in POSTHOG_LOCAL_ONLY mode with explicit skipped feedback. A real SDK/mock transport regression verifies zero remote flag requests, while the remote-enabled control still returns boolean and variant values. Latest upstream fixes and internal metadata hooks are integrated.

29 example tests / 163 assertions on Laravel 12 and 13; 106 package tests / 664 assertions in the same clean-source matrix. The local-only request-count regression failed before the guard and passes afterward. Independent scoped review is clean.

The single example review concern is addressed. Local-only means no remote flag fallback; definition fetching and separately enabled analytics collectors are not redefined as offline mode.

Core human-review integration

Signed merges integrate #282 commit 858c204: non-throwing instance group validation (static facade validation unchanged), explicit typed exception source independent of mechanism/handled, provenance protection and scope ownership docs. The core suite passes 2,532 tests / 11,366 assertions. Final committed-source Laravel 12/13 matrices remain green at 106 package tests / 664 assertions and 29 example tests / 163 assertions each. The core delta received a clean isolated review; no automatic Laravel hook-source inference was added.

Before the first Laravel release: server SDK registration

posthog-laravel is not yet registered on PostHog server master for PHP usage attribution, Python/Rust feature-flag library classification/parser, or SDK health frontend/backend registries. A separate backend registration/alias PR must land before the Laravel package ships, following the approach in PostHog/posthog#93911. Event ingestion itself is not rejected just because this library identifier is unclassified. SDK health/version handling must respect Laravel's independent package version, not compare 0.1.x against PHP core 4.x. No backend PR, repository or release was created by this SDK follow-up; this remains an explicit pre-release prerequisite alongside core ^4.20 publication and distribution/Packagist setup.

@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 9e9b8af: 1 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.

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-fork_curl Compliance Report

Date: 2026-10-29T16:10:51.130575+00:00
Duration: 116152ms

⚠️ 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 ✅ 41ms
Format Validation.Event Has Uuid ✅ 533ms
Format Validation.Event Has Lib Properties ✅ 534ms
Format Validation.Distinct Id Is String ✅ 534ms
Format Validation.Token Is Present ✅ 535ms
Format Validation.Custom Properties Preserved ✅ 537ms
Format Validation.Event Has Timestamp ✅ 534ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 534ms
Retry Behavior.Retries On 503 ✅ 5858ms
Retry Behavior.Does Not Retry On 400 ✅ 2537ms
Retry Behavior.Does Not Retry On 401 ✅ 2536ms
Retry Behavior.Respects Retry After Header ❌ 6350ms
Retry Behavior.Implements Backoff ✅ 16278ms
Retry Behavior.Retries On 500 ✅ 5158ms
Retry Behavior.Retries On 502 ✅ 5648ms
Retry Behavior.Retries On 504 ✅ 5653ms
Retry Behavior.Max Retries Respected ✅ 16284ms
Deduplication.Generates Unique Uuids ✅ 545ms
Deduplication.Preserves Uuid On Retry ✅ 5651ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10871ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5651ms
Deduplication.No Duplicate Events In Batch ✅ 542ms
Deduplication.Different Events Have Different Uuids ✅ 537ms
Compression.Sends Gzip When Enabled ✅ 536ms
Batch Format.Uses Proper Batch Structure ✅ 533ms
Batch Format.Flush With No Events Sends Nothing ✅ 520ms
Batch Format.Multiple Events Batched Together ✅ 522ms
Error Handling.Does Not Retry On 403 ✅ 2536ms
Error Handling.Does Not Retry On 413 ✅ 2534ms
Error Handling.Retries On 408 ✅ 5648ms

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 ✅ 526ms
Request Payload.Flags Request Uses V2 Query Param ✅ 524ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 523ms
Request Payload.Flags Request Omits Authorization Header ✅ 525ms
Request Payload.Token In Flags Body Matches Init ✅ 524ms
Request Payload.Groups Round Trip ✅ 524ms
Request Payload.Groups Default To Empty Object ✅ 523ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 524ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 525ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 524ms
Request Lifecycle.No Flags Request On Init Alone ✅ 519ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 520ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 527ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 526ms
Retry Behavior.Retries Flags On 502 ✅ 627ms
Retry Behavior.Retries Flags On 504 ✅ 625ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 536ms

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-socket Compliance Report

Date: 2026-10-29T16:10:59.758675+00:00
Duration: 115777ms

⚠️ 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 ✅ 23ms
Format Validation.Event Has Uuid ✅ 518ms
Format Validation.Event Has Lib Properties ✅ 520ms
Format Validation.Distinct Id Is String ✅ 520ms
Format Validation.Token Is Present ✅ 521ms
Format Validation.Custom Properties Preserved ✅ 520ms
Format Validation.Event Has Timestamp ✅ 520ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 520ms
Retry Behavior.Retries On 503 ✅ 5829ms
Retry Behavior.Does Not Retry On 400 ✅ 2523ms
Retry Behavior.Does Not Retry On 401 ✅ 2522ms
Retry Behavior.Respects Retry After Header ❌ 6326ms
Retry Behavior.Implements Backoff ✅ 16244ms
Retry Behavior.Retries On 500 ✅ 5625ms
Retry Behavior.Retries On 502 ✅ 5627ms
Retry Behavior.Retries On 504 ✅ 5628ms
Retry Behavior.Max Retries Respected ✅ 16240ms
Deduplication.Generates Unique Uuids ✅ 524ms
Deduplication.Preserves Uuid On Retry ✅ 5625ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10835ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5629ms
Deduplication.No Duplicate Events In Batch ✅ 522ms
Deduplication.Different Events Have Different Uuids ✅ 520ms
Compression.Sends Gzip When Enabled ✅ 519ms
Batch Format.Uses Proper Batch Structure ✅ 520ms
Batch Format.Flush With No Events Sends Nothing ✅ 516ms
Batch Format.Multiple Events Batched Together ✅ 509ms
Error Handling.Does Not Retry On 403 ✅ 2521ms
Error Handling.Does Not Retry On 413 ✅ 2521ms
Error Handling.Retries On 408 ✅ 5626ms

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 ✅ 519ms
Request Payload.Flags Request Uses V2 Query Param ✅ 518ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 518ms
Request Payload.Flags Request Omits Authorization Header ✅ 517ms
Request Payload.Token In Flags Body Matches Init ✅ 518ms
Request Payload.Groups Round Trip ✅ 517ms
Request Payload.Groups Default To Empty Object ✅ 518ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 517ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 517ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 517ms
Request Lifecycle.No Flags Request On Init Alone ✅ 513ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 508ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 521ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 518ms
Retry Behavior.Retries Flags On 502 ✅ 620ms
Retry Behavior.Retries Flags On 504 ✅ 620ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 519ms

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-lib_curl Compliance Report

Date: 2026-10-29T16:11:00.580528+00:00
Duration: 116205ms

⚠️ 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 ✅ 21ms
Format Validation.Event Has Uuid ✅ 517ms
Format Validation.Event Has Lib Properties ✅ 516ms
Format Validation.Distinct Id Is String ✅ 519ms
Format Validation.Token Is Present ✅ 518ms
Format Validation.Custom Properties Preserved ✅ 519ms
Format Validation.Event Has Timestamp ✅ 517ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 518ms
Retry Behavior.Retries On 503 ✅ 5826ms
Retry Behavior.Does Not Retry On 400 ✅ 2521ms
Retry Behavior.Does Not Retry On 401 ✅ 2521ms
Retry Behavior.Respects Retry After Header ❌ 6329ms
Retry Behavior.Implements Backoff ✅ 16239ms
Retry Behavior.Retries On 500 ✅ 5627ms
Retry Behavior.Retries On 502 ✅ 5625ms
Retry Behavior.Retries On 504 ✅ 5627ms
Retry Behavior.Max Retries Respected ✅ 17043ms
Deduplication.Generates Unique Uuids ✅ 226ms
Deduplication.Preserves Uuid On Retry ✅ 5624ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10832ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5630ms
Deduplication.No Duplicate Events In Batch ✅ 524ms
Deduplication.Different Events Have Different Uuids ✅ 519ms
Compression.Sends Gzip When Enabled ✅ 517ms
Batch Format.Uses Proper Batch Structure ✅ 517ms
Batch Format.Flush With No Events Sends Nothing ✅ 513ms
Batch Format.Multiple Events Batched Together ✅ 510ms
Error Handling.Does Not Retry On 403 ✅ 2519ms
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: 804ms < 2500ms

Feature_Flags Tests

✅ 17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 519ms
Request Payload.Flags Request Uses V2 Query Param ✅ 515ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 515ms
Request Payload.Flags Request Omits Authorization Header ✅ 514ms
Request Payload.Token In Flags Body Matches Init ✅ 515ms
Request Payload.Groups Round Trip ✅ 515ms
Request Payload.Groups Default To Empty Object ✅ 515ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 514ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 514ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 515ms
Request Lifecycle.No Flags Request On Init Alone ✅ 510ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 507ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 517ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 514ms
Retry Behavior.Retries Flags On 502 ✅ 617ms
Retry Behavior.Retries Flags On 504 ✅ 615ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 517ms

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