Skip to content

feat: add opt-in Laravel autocapture - #284

Merged
marandaneto merged 40 commits into
mainfrom
feat/laravel-autocapture
Nov 2, 2026
Merged

marandaneto merged 40 commits into
mainfrom
feat/laravel-autocapture

Conversation

@marandaneto

@marandaneto marandaneto commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

💡 Motivation and Context

Stacked on the Laravel runtime PR #283. This adds optional server-side collection without changing the PHP SDK's capture pipeline.

The package can collect request summaries, a separate server pageview mode, auth lifecycle events, explicitly mapped domain events, allowlisted model changes after commit, and job/command/scheduler outcomes. Every category defaults off. Model and domain data is explicitly selected. HTTP metadata uses route templates, not concrete parameters or query strings. Automatic events preserve identity, session and groups without copying arbitrary context properties.

The optional Blade conditional reuses native flag snapshots and exposure behavior. Collector events use ordinary laravel_* names, not UI $autocapture. The canonical autocapture spec is client-only. The public server schemas and limits are documented in AUTOCAPTURE.md.

Background schedules record launch, not cross-process completion or missed-schedule monitoring. Model collection is not a durable outbox. Browser pageviews must not be enabled alongside equivalent server pageviews.

💚 How did you test it?

  • 17 collector tests cover safe defaults, credential/URL filtering, identity/session correlation, publication of config, per-category exclusions, sampling, exact event/model allowlists, nested transactions and rollbacks, duplicate terminal events, failure preservation and flag exposure deduplication.
  • Integrated runtime plus collectors: 81 tests, 515 assertions on Laravel 13.
  • Full-stack Laravel 12 and 13 tests also pass through the temporary-source test helper in the final tooling PR.
  • PSR-12, syntax checks and git diff --check pass.
  • The URL privacy test constructs synthetic credentials at runtime to avoid a secret scanner treating a literal credential-bearing test URL as an unknown live credential. The credential-removal assertion remains in place. No real credential was used.
  • Initial isolated review reports no actionable findings. It caught explicit fail() jobs being labeled successful; a real sync-job regression failed before the fix and now passes.

📝 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

  • Recorded a Laravel-only initial release intent in .laravel-changes/.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Implemented with Pi, PHP/Composer, Git and GitHub CLI. Collectors were developed in an isolated worktree and integrated with the runtime lifecycle fixes using signed merge commits. Tests use framework events, SQLite transactions and mocked SDK traffic, not a live PostHog project. This is original integration code. Human review is required before merge.

Reviewed commit: 0e3243ee46def0be86f153169bc24d31876936d4. Reviewed base: 3b2ad5a8904fe7baa5d3e849dbce41bb3509d00a. Initial isolated review has no actionable findings.

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: 8ffd0ad16ce8368a8687b7f2411e661b24f28ac6.

Model events use connection-correct transaction records, aggregate 1,000-event/1 MiB encoded admission limits, bounded batch callbacks, and immutable snapshots. Rollback, PDO-reset/disconnect, reentrant commit callbacks and Laravel 12 same-PDO commit-time retries are covered. Scheduler outcomes omit overlap-lock acquisition skips using weak, once-per-task execution markers supported since Laravel 12.0.

Runtime plus collectors: 81 tests / 515 assertions. Final clean-source Laravel 12/13 stack (PHP 8.5 locally): 106 package tests / 664 assertions and 29 example tests / 163 assertions per version. New real schedule:run cases and observable transaction/callback admission checks failed before their fixes. Independent review caught the commit-time retry capacity edge; its Laravel 12 regression failed before and passed after correction.

All three collector review concerns addressed. No public budget configuration or new skipped outcome was added; delivery remains best-effort, not a durable outbox.

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 0e3243e: 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.

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-fork_curl Compliance Report

Date: 2026-11-02T13:42:37.147001+00:00
Duration: 116492ms

⚠️ 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 ✅ 529ms
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 ✅ 530ms
Format Validation.Event Has Timestamp ✅ 531ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 531ms
Retry Behavior.Retries On 503 ✅ 5857ms
Retry Behavior.Does Not Retry On 400 ✅ 2532ms
Retry Behavior.Does Not Retry On 401 ✅ 2533ms
Retry Behavior.Respects Retry After Header ❌ 6346ms
Retry Behavior.Implements Backoff ✅ 16278ms
Retry Behavior.Retries On 500 ✅ 5648ms
Retry Behavior.Retries On 502 ✅ 5646ms
Retry Behavior.Retries On 504 ✅ 5647ms
Retry Behavior.Max Retries Respected ✅ 16278ms
Deduplication.Generates Unique Uuids ✅ 538ms
Deduplication.Preserves Uuid On Retry ✅ 5644ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10862ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5650ms
Deduplication.No Duplicate Events In Batch ✅ 535ms
Deduplication.Different Events Have Different Uuids ✅ 533ms
Compression.Sends Gzip When Enabled ✅ 532ms
Batch Format.Uses Proper Batch Structure ✅ 532ms
Batch Format.Flush With No Events Sends Nothing ✅ 519ms
Batch Format.Multiple Events Batched Together ✅ 520ms
Error Handling.Does Not Retry On 403 ✅ 2533ms
Error Handling.Does Not Retry On 413 ✅ 2533ms
Error Handling.Retries On 408 ✅ 5646ms

Failures

retry_behavior.respects_retry_after_header

Retry delay too short: 811ms < 2500ms

Feature_Flags Tests

✅ 17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 522ms
Request Payload.Flags Request Uses V2 Query Param ✅ 523ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 522ms
Request Payload.Flags Request Omits Authorization Header ✅ 522ms
Request Payload.Token In Flags Body Matches Init ✅ 522ms
Request Payload.Groups Round Trip ✅ 523ms
Request Payload.Groups Default To Empty Object ✅ 523ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 522ms
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 ✅ 517ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 516ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 526ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 523ms
Retry Behavior.Retries Flags On 502 ✅ 626ms
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-11-02T13:43:05.763496+00:00
Duration: 116311ms

⚠️ 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 ✅ 22ms
Format Validation.Event Has Uuid ✅ 521ms
Format Validation.Event Has Lib Properties ✅ 527ms
Format Validation.Distinct Id Is String ✅ 524ms
Format Validation.Token Is Present ✅ 518ms
Format Validation.Custom Properties Preserved ✅ 519ms
Format Validation.Event Has Timestamp ✅ 519ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 519ms
Retry Behavior.Retries On 503 ✅ 5832ms
Retry Behavior.Does Not Retry On 400 ✅ 2521ms
Retry Behavior.Does Not Retry On 401 ✅ 2520ms
Retry Behavior.Respects Retry After Header ❌ 6328ms
Retry Behavior.Implements Backoff ✅ 16238ms
Retry Behavior.Retries On 500 ✅ 5628ms
Retry Behavior.Retries On 502 ✅ 5627ms
Retry Behavior.Retries On 504 ✅ 5628ms
Retry Behavior.Max Retries Respected ✅ 17038ms
Deduplication.Generates Unique Uuids ✅ 229ms
Deduplication.Preserves Uuid On Retry ✅ 5627ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10836ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5630ms
Deduplication.No Duplicate Events In Batch ✅ 524ms
Deduplication.Different Events Have Different Uuids ✅ 521ms
Compression.Sends Gzip When Enabled ✅ 520ms
Batch Format.Uses Proper Batch Structure ✅ 519ms
Batch Format.Flush With No Events Sends Nothing ✅ 516ms
Batch Format.Multiple Events Batched Together ✅ 510ms
Error Handling.Does Not Retry On 403 ✅ 2522ms
Error Handling.Does Not Retry On 413 ✅ 2521ms
Error Handling.Retries On 408 ✅ 5641ms

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 ✅ 521ms
Request Payload.Flags Request Uses V2 Query Param ✅ 518ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 519ms
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 ✅ 518ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 517ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 518ms
Request Lifecycle.No Flags Request On Init Alone ✅ 514ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 507ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 521ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 519ms
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-socket Compliance Report

Date: 2026-11-02T13:42:48.830937+00:00
Duration: 116227ms

⚠️ 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 ✅ 30ms
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 ✅ 526ms
Format Validation.Custom Properties Preserved ✅ 525ms
Format Validation.Event Has Timestamp ✅ 525ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 527ms
Retry Behavior.Retries On 503 ✅ 5834ms
Retry Behavior.Does Not Retry On 400 ✅ 2526ms
Retry Behavior.Does Not Retry On 401 ✅ 2531ms
Retry Behavior.Respects Retry After Header ❌ 6334ms
Retry Behavior.Implements Backoff ✅ 16250ms
Retry Behavior.Retries On 500 ✅ 5633ms
Retry Behavior.Retries On 502 ✅ 5634ms
Retry Behavior.Retries On 504 ✅ 5634ms
Retry Behavior.Max Retries Respected ✅ 16250ms
Deduplication.Generates Unique Uuids ✅ 533ms
Deduplication.Preserves Uuid On Retry ✅ 5634ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10840ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5636ms
Deduplication.No Duplicate Events In Batch ✅ 532ms
Deduplication.Different Events Have Different Uuids ✅ 527ms
Compression.Sends Gzip When Enabled ✅ 527ms
Batch Format.Uses Proper Batch Structure ✅ 526ms
Batch Format.Flush With No Events Sends Nothing ✅ 522ms
Batch Format.Multiple Events Batched Together ✅ 513ms
Error Handling.Does Not Retry On 403 ✅ 2527ms
Error Handling.Does Not Retry On 413 ✅ 2529ms
Error Handling.Retries On 408 ✅ 5635ms

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 ✅ 527ms
Request Payload.Flags Request Uses V2 Query Param ✅ 524ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 524ms
Request Payload.Flags Request Omits Authorization Header ✅ 524ms
Request Payload.Token In Flags Body Matches Init ✅ 523ms
Request Payload.Groups Round Trip ✅ 522ms
Request Payload.Groups Default To Empty Object ✅ 525ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 522ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 524ms
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 ✅ 509ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 527ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 525ms
Retry Behavior.Retries Flags On 502 ✅ 626ms
Retry Behavior.Retries Flags On 504 ✅ 626ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 524ms

Base automatically changed from feat/laravel-package-runtime to main November 2, 2026 10:38
@marandaneto
marandaneto marked this pull request as ready for review November 2, 2026 11:30
@marandaneto
marandaneto requested a review from a team as a code owner November 2, 2026 11:30

@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 5c89a6e: 1 Should fix. This pull request is large, so the review ran in 4 parts with less depth than usual.

@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 a1083be: no findings. This pull request is large, so the review ran in 4 parts with less depth than usual.

@ioannisj ioannisj 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.

🤖 An AI agent wrote this on behalf of @ioannisj.

No blockers from my side. Three smaller things inline: the collectors still do their work when PostHog is disabled, collector failures get swallowed with no debug signal, and model events get lost in app tests that wrap each test in a transaction.

@marandaneto
marandaneto merged commit d8285a1 into main Nov 2, 2026
27 checks passed
@marandaneto
marandaneto deleted the feat/laravel-autocapture branch November 2, 2026 13:49
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