Skip to content

chore(qs-13): php-sdk discriminator follow-ups — LogUtils annotation, generated-dir guard, rule_type no-throw sweep - #42

Merged
abbaseya merged 5 commits into
mainfrom
chore/php-sdk-discriminator-followups-qs13
Jun 9, 2026
Merged

chore(qs-13): php-sdk discriminator follow-ups — LogUtils annotation, generated-dir guard, rule_type no-throw sweep#42
abbaseya merged 5 commits into
mainfrom
chore/php-sdk-discriminator-followups-qs13

Conversation

@abbaseya

@abbaseya abbaseya commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Companion php-sdk follow-ups (a)/(b)/(c) for qs-13 — the Option-A root-cause fix for the RuleElement narrowed-discriminator-enum crash (qs-12). This PR is php-sdk scope only: no OpenAPI spec edits and no backend changes. The backend portion (the fix-php-discriminants.js dist-php post-process, package.json/openapitools.json wiring, the build-time assertion, and workflow gating) lands separately on backend PR #6340.

(a) packages/Utils/src/LogUtils.php — comment only

Annotated toLoggable() as belt-and-braces. It is still load-bearing on current main: the discriminator bases (RuleElement::rule_type, RuleElementNoUrl::rule_type) are still narrowed to single-value enums, so bypassing ObjectSerializer here is what prevents the Invalid value for enum crash when logging real-world rule_type values. It becomes redundant for the crash only once the backend regenerates those bases to string (PR #6340). It is retained as belt-and-braces thereafter. No logic changed.

(b) .github/workflows/qa.yml — generated-dir guard

New generated-guard job that fails if any .php file under packages/Types/lib/Generated/ has lost its OpenAPI generator marker (i.e. was hand-edited or wrongly added). Mirrors the android-sdk precedent (.github/workflows/ci.yml), with the PHP-specific differences:

  • The PHP marker "This class is auto generated by OpenAPI Generator" sits inside the file docblock (files start with <?php), so the guard scans the whole file (not head -1 like android).
  • It whitelists all generated files — Model/ and the non-Model/ infra (Api/, ObjectSerializer.php, Configuration.php, HeaderSelector.php, ApiException.php).

Verified locally: passes against the current 267-file Generated/ tree; fails when a marker-less file is introduced.

(c) tests/CrossSdk/RuleParityTest.phprule_type no-throw sweep (re-scoped)

Added a DataProvider-driven sweep over url / cookie / generic_text_key_value (non-js_condition) asserting that logging such a rule via RuleManager::isRuleMatched() — which routes its log context through LogUtils::toLoggable() — emits neither Invalid value for enum nor log serialization error. Modeled on the green RuleManagerLogSerializationTest logging path, parameterized via a single data provider to avoid the SonarQube new_duplicated_lines_density gate (no copy-pasted test bodies).

Deferred (intentionally NOT in this PR): the D1 matching assertion

The spec's "with D1 taken, ALSO assert RuleManager end-to-end matching on these types ($rule['rule_type'] preserves the real value)" assertion is deferred — it cannot pass on current main:

  • RuleElement.php:271 still runs $this->container['rule_type'] = static::$openAPIModelName; (= "RuleElement"), overwriting the input value (D1).
  • RuleManager.php:235 builds new RuleElement($ruleItem), and the custom-interface path (:268–285) dispatches on $rule['rule_type'] — which the overwrite corrupts.
  • So an end-to-end matching assertion on url/cookie via the custom interface would fail CI on current main.

D1 (removal of that constructor overwrite) is a backend-generated-types change that is not present on php-sdk main. Per AC2, (c) must merge to main before the backend regenerates the types; at that point D1 is absent. The matching assertion lands with/after the backend's auto-generated php-sdk types PR — it is explicitly not part of this PR.

Tests run

  • vendor/bin/phpunit --testsuite cross-sdkOK (403 tests, 534 assertions) (incl. the 3 new sweep cases)
  • vendor/bin/phpunit --filter RuleManagerLogSerializationTest (qs-12 regression, AC2 must-stay-green) → OK (1 test, 9 assertions)
  • vendor/bin/phpstan analyse on touched files → No errors
  • vendor/bin/php-cs-fixer fix --dry-run on touched files → clean
  • Generated-guard shell logic → verified pass (current tree) / fail (marker-less file)

Refs

  • Quick-spec: qs-13 (_bmad-output/implementation-artifacts/2026-03-13-convert-php-sdk/qs-13-php-serving-types-discriminator-postprocess.md)
  • Backend companion: PR #6340 (api-docs/auttomate-php-config-types)
  • Predecessor: qs-12 (log-path fence)

🤖 Generated with Claude Code

…generated-dir guard, rule_type no-throw sweep

Companion php-sdk follow-ups (a)/(b)/(c) for qs-13 (Option-A root-cause fix
for the RuleElement narrowed-discriminator-enum crash; backend portion lands
on PR #6340). php-sdk scope only — no OpenAPI spec or backend changes here.

(a) LogUtils::toLoggable — annotate as belt-and-braces. STILL load-bearing on
    current main (the discriminator bases are still narrowed to single-value
    enums); becomes redundant for the crash only once the backend regenerates
    those bases to `string`. No logic changed.

(b) qa.yml — add a Generated Types Guard job that fails if any .php file under
    packages/Types/lib/Generated/ has lost its OpenAPI generator marker. The
    PHP marker ("This class is auto generated by OpenAPI Generator") sits in the
    file docblock (files start with <?php), so the grep scans the whole file
    (unlike android's head -1) and whitelists ALL generated files incl. Api/,
    ObjectSerializer.php, Configuration.php, HeaderSelector.php, ApiException.php.
    Verified: passes against the current 267-file Generated/ tree; fails on a
    marker-less hand-edit.

(c) RuleParityTest — add a @dataProvider-driven sweep over url / cookie /
    generic_text_key_value asserting that logging such a rule via
    RuleManager::isRuleMatched() (which routes through LogUtils::toLoggable)
    emits neither "Invalid value for enum" nor "log serialization error".
    Parameterized to avoid the SonarQube duplication gate.

DEFERRED (not shipped): the D1-dependent end-to-end RuleManager *matching*
assertion. On current main RuleElement.php:271 still overwrites rule_type with
the model name, corrupting the custom-interface dispatch in RuleManager — so a
matching assertion would fail CI. D1 (removal of that overwrite) is a backend-
generated-types change absent from php-sdk main; the matching assertion lands
with/after the backend auto-generated php-sdk types PR.

Tests: vendor/bin/phpunit --testsuite cross-sdk -> 403 passed; qs-12 regression
RuleManagerLogSerializationTest green; phpstan + php-cs-fixer clean on all
touched files.

Refs: qs-13, backend PR #6340

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@abbaseya abbaseya self-assigned this Jun 9, 2026
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@abbaseya
abbaseya requested a review from DmytroConvert June 9, 2026 00:25
abbaseya and others added 3 commits June 9, 2026 05:27
…s-sdk parity

Port the three existence comparison methods from js-sdk comparisons.ts to
php-sdk Comparisons.php, matching JS strict-equality semantics (null/empty
string => not exists; 0 exists). not_exists keeps the underscore so the wire
match_type 'not_exists' resolves via RuleManager get_class_methods() dispatch;
doesNotExist delegates to not_exists (faithful equivalent of JS's static alias).

Extend the cross-SDK parity surface: add exists/not_exists vector groups
(present/null/empty-string/negation/strict-zero) flowing through the existing
#[DataProvider] (no copy-pasted test bodies), and add both to the required
comparison-category guard.

Ref: qs-14 php-jssdk-parity-comparisons-exists-segment-keys

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dk parity

Change SegmentsKeys::VisitorType from 'visitor_type' to 'visitorType' to match
js-sdk VISITOR_TYPE, and update the hardcoded routing literal in
DataManager::filterReportSegments() plus the two test/doc surfaces.

Fixes a latent round-trip bug: filterReportSegments() emitted 'visitor_type'
while the generated VisitorSegments model keys strictly on 'visitorType', so
SegmentsManager::getSegments() silently dropped the visitor-type value. A new
parity test locks the enum value and the VisitorSegments round-trip.

Ref: qs-14 php-jssdk-parity-comparisons-exists-segment-keys

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DmytroConvert
DmytroConvert previously approved these changes Jun 9, 2026
…ts-segment-keys-qs14

feat(parity): add exists/not_exists comparison operators + align visitor_type segment key
@abbaseya
abbaseya merged commit e1e660f into main Jun 9, 2026
11 checks passed
@abbaseya
abbaseya deleted the chore/php-sdk-discriminator-followups-qs13 branch June 9, 2026 18:47
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