Open
Conversation
Add DraftInterface/DraftFactoryInterface abstraction and wire it into GeneratorConfiguration. The pipeline does not yet use the draft — this phase establishes the structural foundation only. New classes: - DraftInterface: getDefinition(): DraftBuilder - DraftFactoryInterface: getDraftForSchema(JsonSchema): DraftInterface - DraftBuilder: fluent builder for Draft instances - Draft: built result with getCoveredTypes(string|array) — always includes 'any' - Element/Type: holds a type name and its ModifierInterface list - Modifier/ModifierInterface: modify() contract for future phases - Draft_07: registers all 7 JSON Schema types + 'any' (empty modifier lists) - AutoDetectionDraft: DraftFactoryInterface that detects $schema keyword, falls back to Draft_07 for absent/unrecognised values GeneratorConfiguration gains getDraft()/setDraft() accepting DraftInterface|DraftFactoryInterface; defaults to AutoDetectionDraft. phpcs.xml: exclude Squiz.Classes.ValidClassName.NotPascalCase to allow underscore-separated draft class names (Draft_07, Draft_2020_12, etc.).
…raft architecture Phase 2 — Eliminate PropertyMetaDataCollection: - Add bool $required parameter to PropertyFactory::create, ProcessorFactoryInterface::getProcessor and all processor constructors - Replace isAttributeRequired() lookups with $this->required throughout - Move addDependencyValidator to BaseProcessor::addPropertiesToSchema, reading directly from $json['dependencies'][$propertyName] - Update SchemaDefinition::resolveReference to accept bool $required instead of PropertyMetaDataCollection; simplify cache key accordingly - Remove PMC save/restore from AdditionalPropertiesValidator, PatternPropertiesValidator, ArrayTupleValidator, AbstractComposedValueProcessor, and IfProcessor - Delete PropertyMetaDataCollection entirely Phase 3 refinements — Draft architecture: - DraftInterface.getDefinition() returns DraftBuilder (not Draft), enabling draft extension by consumers; PropertyFactory builds and caches - Type constructor auto-installs TypeCheckModifier via TypeConverter::jsonSchemaToPhp, removing per-type boilerplate from Draft_07 - DefaultValueModifier is self-contained: reads type from schema, resolves is_* check and int->float coercion internally; no callable constructor API - Draft_07 is now a clean declarative list with no implementation detail - Replace DRAFT_BYPASS_TYPES hardcoded list with Draft::hasType() check - Add TypeConverter::jsonSchemaToPhp() to centralise JSON->PHP type mapping
…to validator factories Introduce AbstractValidatorFactory hierarchy (SimplePropertyValidatorFactory, SimpleBaseValidatorFactory) and migrate all keyword-specific validator generation out of the legacy processors into dedicated factory classes registered on Draft_07. Migrated types: string (pattern, minLength, maxLength, format), integer/number (minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf), array (items, minItems, maxItems, uniqueItems, contains), universal (enum, filter). Also adds DefaultArrayToEmptyArrayModifier for the array default-empty behaviour. Legacy processor generateValidators overrides for string, numeric, and array types are removed entirely. AbstractNumericProcessor is now empty (flattened into IntegerProcessor/NumberProcessor directly extending AbstractTypedValueProcessor). PropertyFactory exposes applyTypeModifiers/applyUniversalModifiers as public methods; MultiTypeProcessor uses these directly instead of a skipUniversalModifiers flag, eliminating the 'type-only' sentinel. FilterValidator.validateFilterCompatibilityWithBaseType now derives the effective type from getNestedSchema() for object properties instead of relying on a set/restore workaround in FilterValidatorFactory.
Multi-type properties ("type": [...]) are now handled directly in
PropertyFactory::createMultiTypeProperty instead of delegating to
MultiTypeProcessor. Each type is processed through its legacy single-type
processor and Draft type modifiers; type-check validators are collected and
consolidated into a single MultiTypeCheckValidator; decorators are forwarded
via PropertyTransferDecorator; universal modifiers run once on the main
property after all sub-properties resolve.
- Delete MultiTypeProcessor
- PropertyProcessorFactory::getProcessor now only handles string types;
the private getSingleTypePropertyProcessor wrapper is inlined
- ProcessorFactoryInterface::getProcessor parameter narrowed to string
- Type validity is checked via PropertyFactory::checkType (shared between
scalar and multi-type paths)
- Three invalidRecursiveMultiType test expectations updated: outer
InvalidItemException property name changes from "item of array property"
to "property" due to different extracted method registration order
…r architecture - Convert MinProperties, MaxProperties, PropertyNames, PatternProperties, AdditionalProperties, and Properties processing from BaseProcessor methods into validator factories (Factory/Object/) registered via addValidator() in Draft_07, consistent with the scalar/array/number/string factory pattern - ObjectModifier remains a proper modifier (structural, not keyword-driven) - PropertyFactory: add type=object path that calls processSchema directly, wires the outer property via TypeCheckModifier + ObjectModifier, and runs universal modifiers (filter/enum/default) on the outer property - PropertyFactory: add RequiredPropertyValidator for required type=object properties; strip property-level keywords before passing schema to processSchema to prevent double-application on the nested class root - SchemaProcessor: add transferComposedPropertiesToSchema (migrated from BaseProcessor) with correct use imports so allOf/anyOf/oneOf branch properties are transferred and conflict detection fires correctly - BaseProcessor: remove all methods now handled by Draft modifiers/factories
…dValueProcessorFactory Introduce Model\Validator\Factory\Composition\AbstractCompositionValidatorFactory (extends AbstractValidatorFactory) with shared composition helpers, plus five concrete factories: AllOfValidatorFactory, AnyOfValidatorFactory, OneOfValidatorFactory, NotValidatorFactory, IfValidatorFactory. A marker interface ComposedPropertiesValidatorFactoryInterface replaces ComposedPropertiesInterface for the property-transfer guard. Register all five on the 'any' type in Draft_07 via addValidator(), consistent with the keyword-keyed pattern established in Phase 4. Delete ComposedValueProcessorFactory and all legacy ComposedValue processor classes (AbstractComposedValueProcessor, AllOfProcessor, AnyOfProcessor, OneOfProcessor, NotProcessor, IfProcessor and their interfaces). AbstractPropertyProcessor is slimmed to only the RequiredPropertyValidator and isImplicitNullAllowed helpers still needed by the bridge. Update all is_a() checks and use-imports in Schema, BaseProcessor, SchemaProcessor, ConditionalPropertyValidator, and CompositionRequiredPromotionPostProcessor to reference the new factory classes.
…ConstModifier, IntToFloatModifier, NullModifier - Delete all PropertyProcessor/Property/* classes (AbstractPropertyProcessor, AbstractValueProcessor, AbstractTypedValueProcessor, AbstractNumericProcessor, StringProcessor, IntegerProcessor, NumberProcessor, BooleanProcessor, ArrayProcessor, ObjectProcessor, NullProcessor, AnyProcessor, ConstProcessor, ReferenceProcessor, BasereferenceProcessor, BaseProcessor) - Delete PropertyProcessorFactory and ProcessorFactoryInterface - Remove ProcessorFactoryInterface constructor parameter from PropertyFactory - Inline $ref / baseReference routing as private methods on PropertyFactory - Refactor PropertyFactory::create into focused dispatch + four extracted methods (createObjectProperty, createBaseProperty, createTypedProperty, buildProperty) with a single unified applyModifiers helper replacing three separate methods - Add ConstModifier (registered on 'any' type): sets PropertyType from const value type and adds InvalidConstException validator; immutability is fully respected - Add IntToFloatModifier (registered on 'number' type): adds IntToFloatCastDecorator - Add NullModifier (registered on 'null' type): clears PropertyType and adds TypeHintDecorator - Register object type in Draft_07 with typeCheck=false (PropertyFactory handles object type-check via wireObjectProperty, not via Draft modifier dispatch) - Remove stale PropertyProcessorFactory imports from validator/schema files - Add ConstPropertyTest::testConstPropertyHasNoSetterWhenImmutable - Add docs/source/generator/custom/customDraft.rst: custom draft / modifier guide - Update setDraft documentation in gettingStarted.rst with seealso link - Update CLAUDE.md architecture section to document the final Draft modifier system
Coverage Report for CI Build 24136479492Coverage decreased (-0.4%) to 98.249%Details
Uncovered Changes
Coverage Regressions4 previously-covered lines in 3 files lost coverage.
Coverage Stats
💛 - Coveralls |
…e to safely rely on them to be present
…t keys (#103) - SerializationPostProcessor: key skipNotProvidedPropertiesMap by schema name (getName()) rather than the PHP attribute name (getAttribute(true)) so the array_diff comparison against rawModelDataInput works correctly - SerializableTrait: use #[SchemaName] reflection to map PHP property names to their original JSON Schema names; fix getCustomSerializerMethod cache to be per-class (static::class key) and use array_key_exists instead of isset to correctly handle the false sentinel; remove str_starts_with('_') fallback — #[Internal] is now the sole exclusion mechanism - Add Internal attribute to production library; mark all three static trait properties with #[Internal] - Update evaluateAttribute: use jsonSerialize/toArray with correct depth propagation; cache serialization capability per class (not per call); handle depth-exhausted path after capability cache to avoid poisoning - $except now takes schema names only (breaking change) - Tests: add Issue103Test (4 combined tests), extend PhpAttributeTest with leading-digit schema name case, fix stale testBuiltinAttributes assertion Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…enerated internal properties
Previously, properties generated as internal (additional properties, pattern
properties, rawModelDataInput, errorRegistry, etc.) used a leading underscore
as both a naming convention and a serialization exclusion signal. This was an
implicit contract that polluted the namespace and conflicted with clean PHP
naming.
Changes:
- AbstractProperty::getAttribute(): no longer prepends '_' for internal properties
- Property::setInternal(true): auto-adds #[Internal] PHP attribute to the
property; the flag is one-way (calling setInternal(false) after true is a
no-op since the attribute cannot be removed from the list)
- RenderJob::getUseForSchema(): always includes Internal::class in the use list
so the hardcoded rawModelDataInput/errorRegistry declarations in Model.phptpl
can carry the #[Internal] annotation
- Model.phptpl: rawModelDataInput and errorRegistry declared with #[Internal];
all $this->_* references updated throughout
- BuilderClass.phptpl: rawModelDataInput and errorRegistry renamed
- All validator and post-processor templates updated (errorRegistry,
additionalProperties, patternProperties, patternPropertiesMap,
propertyValidationState, skipNotProvidedPropertiesMap, rawModelDataInput)
- PHP source files updated (RenderHelper, validator factories,
post-processors) where property names were emitted as strings
- SerializableTrait fallback path: removed str_starts_with('_') check —
#[Internal] is now the sole mechanism for excluding properties in the
fallback (non-generated) serialization path
- Test: fix Issue116Test to reference 'additionalProperties' (not
'_additionalProperties') when reflecting on generated class
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This reverts commit ef5550c.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.