Repository navigation
fix(flags): fall back for incompatible local regex evaluation - #277
Conversation
posthog-php-fork_curl Compliance ReportDate: 2026-10-22T18:36:50.104513+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 34ms |
| Format Validation.Event Has Uuid | ✅ | 529ms |
| Format Validation.Event Has Lib Properties | ✅ | 531ms |
| Format Validation.Distinct Id Is String | ✅ | 529ms |
| Format Validation.Token Is Present | ✅ | 530ms |
| Format Validation.Custom Properties Preserved | ✅ | 530ms |
| Format Validation.Event Has Timestamp | ✅ | 531ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 530ms |
| Retry Behavior.Retries On 503 | ❌ | 5533ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2534ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2532ms |
| Retry Behavior.Respects Retry After Header | ❌ | 5535ms |
| Retry Behavior.Implements Backoff | ❌ | 15537ms |
| Retry Behavior.Retries On 500 | ❌ | 5044ms |
| Retry Behavior.Retries On 502 | ❌ | 5536ms |
| Retry Behavior.Retries On 504 | ❌ | 5535ms |
| Retry Behavior.Max Retries Respected | ❌ | 15545ms |
| Deduplication.Generates Unique Uuids | ✅ | 539ms |
| Deduplication.Preserves Uuid On Retry | ❌ | 5532ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ❌ | 10543ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ❌ | 5540ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 537ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 532ms |
| Compression.Sends Gzip When Enabled | ✅ | 533ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 531ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 519ms |
| Batch Format.Multiple Events Batched Together | ✅ | 520ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2532ms |
| Error Handling.Does Not Retry On 413 | ✅ | 2533ms |
| Error Handling.Retries On 408 | ❌ | 5536ms |
Failures
retry_behavior.retries_on_503
Expected at least 3 requests, got 1
retry_behavior.respects_retry_after_header
Expected at least 2 requests, got 1
retry_behavior.implements_backoff
Expected at least 3 requests, got 1
retry_behavior.retries_on_500
Expected at least 2 requests, got 1
retry_behavior.retries_on_502
Expected at least 2 requests, got 1
retry_behavior.retries_on_504
Expected at least 2 requests, got 1
retry_behavior.max_retries_respected
Expected 4 requests, got 1
deduplication.preserves_uuid_on_retry
Need at least 2 requests to check retry
deduplication.preserves_uuid_and_timestamp_on_retry
Expected at least 3 requests, got 1
deduplication.preserves_uuid_and_timestamp_on_batch_retry
Expected at least 2 requests, got 1
error_handling.retries_on_408
Expected at least 2 requests, got 1
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 | ✅ | 522ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 522ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 521ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 521ms |
| Request Payload.Groups Round Trip | ✅ | 522ms |
| Request Payload.Groups Default To Empty Object | ✅ | 522ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 522ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 521ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 521ms |
| 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 | ✅ | 525ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 523ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 625ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 626ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 537ms |
posthog-php-socket Compliance ReportDate: 2026-10-22T18:37:10.061617+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 27ms |
| Format Validation.Event Has Uuid | ✅ | 522ms |
| Format Validation.Event Has Lib Properties | ✅ | 521ms |
| Format Validation.Distinct Id Is String | ✅ | 523ms |
| Format Validation.Token Is Present | ✅ | 522ms |
| Format Validation.Custom Properties Preserved | ✅ | 522ms |
| Format Validation.Event Has Timestamp | ✅ | 522ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 522ms |
| Retry Behavior.Retries On 503 | ✅ | 5829ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2524ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2527ms |
| Retry Behavior.Respects Retry After Header | ❌ | 5630ms |
| Retry Behavior.Implements Backoff | ✅ | 16240ms |
| Retry Behavior.Retries On 500 | ✅ | 5134ms |
| Retry Behavior.Retries On 502 | ✅ | 5630ms |
| Retry Behavior.Retries On 504 | ✅ | 5630ms |
| Retry Behavior.Max Retries Respected | ✅ | 16244ms |
| Deduplication.Generates Unique Uuids | ✅ | 530ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5629ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 10837ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 5634ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 529ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 523ms |
| Compression.Sends Gzip When Enabled | ✅ | 522ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 522ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 518ms |
| Batch Format.Multiple Events Batched Together | ✅ | 512ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2524ms |
| Error Handling.Does Not Retry On 413 | ✅ | 2523ms |
| Error Handling.Retries On 408 | ❌ | 5531ms |
Failures
retry_behavior.respects_retry_after_header
Retry delay too short: 101ms < 2500ms
error_handling.retries_on_408
Expected at least 2 requests, got 1
Feature_Flags Tests
✅ 17/17 tests passed
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 523ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 521ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 522ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 521ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 521ms |
| Request Payload.Groups Round Trip | ✅ | 521ms |
| Request Payload.Groups Default To Empty Object | ✅ | 521ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 523ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 521ms |
| 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 | ✅ | 508ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 526ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 524ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 624ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 624ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 524ms |
posthog-php-lib_curl Compliance ReportDate: 2026-10-22T18:37:13.224379+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 24ms |
| Format Validation.Event Has Uuid | ✅ | 520ms |
| Format Validation.Event Has Lib Properties | ✅ | 521ms |
| Format Validation.Distinct Id Is String | ✅ | 521ms |
| Format Validation.Token Is Present | ✅ | 521ms |
| Format Validation.Custom Properties Preserved | ✅ | 521ms |
| Format Validation.Event Has Timestamp | ✅ | 521ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 521ms |
| Retry Behavior.Retries On 503 | ✅ | 5830ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2523ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2523ms |
| Retry Behavior.Respects Retry After Header | ❌ | 6330ms |
| Retry Behavior.Implements Backoff | ✅ | 16243ms |
| Retry Behavior.Retries On 500 | ✅ | 5630ms |
| Retry Behavior.Retries On 502 | ✅ | 5629ms |
| Retry Behavior.Retries On 504 | ✅ | 5629ms |
| Retry Behavior.Max Retries Respected | ✅ | 17043ms |
| Deduplication.Generates Unique Uuids | ✅ | 227ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5629ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 10835ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 5632ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 525ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 522ms |
| Compression.Sends Gzip When Enabled | ✅ | 521ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 521ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 517ms |
| Batch Format.Multiple Events Batched Together | ✅ | 511ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2523ms |
| Error Handling.Does Not Retry On 413 | ✅ | 2523ms |
| Error Handling.Retries On 408 | ✅ | 5628ms |
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 | ✅ | 520ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 519ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 520ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 519ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 519ms |
| Request Payload.Groups Round Trip | ✅ | 519ms |
| Request Payload.Groups Default To Empty Object | ✅ | 520ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 519ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 519ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 520ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 515ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 507ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 522ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 521ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 622ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 621ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 521ms |
💡 Motivation and Context
Local feature flag regex evaluation can return a different Boolean than the flags service and skip remote fallback. For example, PCRE matches
^foo$against"foo\n", but the service does not. PCRE also rejects(?<=a+)beven though the service accepts it. Unicode word classes differ too:^\w$does not match U+200C locally, but does on the service.This adapts the agreed shared-syntax/local-evaluation approach from posthog-js#5354 and its cross-SDK discussion to PHP's PCRE engine. It does not assume that the JS PR is merged or released.
Changes
/uD.\wor\Wescapes, keep ASCII, Unicode letters and decimal digits local. Conservatively defer other input categories, including join controls, marks, non-decimal numerals and symbols. Escaped literal word markers and Unicode patterns without these shorthands are unaffected.not_regexor cohort negation.This deliberately reduces local coverage for advanced syntax and uncertain word-class inputs, even where native matching sometimes agrees. It adds no engine, dependency or public option and makes no universal regex, Unicode or resource-budget equivalence claim.
💚 How did you test it?
main, focused PHPUnit passed: 701 tests and 1,794 assertions. Full PHPUnit passed: 2,131 tests and 8,923 assertions.composer api:checkand strict Composer validation passed. Scoped PHPCS has zero errors; existing warnings and suite deprecations remain.Local validation used PHP 8.5.10 / PCRE 10.48. A prior unchanged loopback timeout test flaked under concurrent activity, then passed targeted and sequential full retries without changes. The fresh post-main full run also passed. No coverage driver or additional PHP-version matrix was available locally. The literal whole-directory lint command previously exhausted memory while scanning installed vendor files, so repository source/tests were linted directly. CI results are not yet claimed.
📝 Checklist
If releasing new changes
pnpm changeto generate a change intent file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
@marandaneto directed the compatibility fix and publication. Pi agents used local file and shell tools, PHP/Composer/PHPUnit, Git, GitHub CLI and the isolated autoreview helper. No public agent-session link is available. Human review is required.
The implementation keeps proven shared behavior local and uses existing fallback for uncertain PCRE/service differences. Test-audit and behavior-neutral deslop checks were completed. Final isolated autoreview passed on the committed branch against
origin/main, with no actionable findings. No dependency replacement or public API expansion was used.