Introduce Draft-based architecture: eliminate legacy processors, centralize modifier/validator registration#123
Conversation
added 10 commits
March 26, 2026 01:35
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
Pull Request Test Coverage Report for Build 23736910562Details
💛 - Coveralls |
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.
Summary
Draftsystem (src/Draft/) that centralises per-type modifier and validator-factory registrations for JSON Schema Draft 7, replacing the scattered processor class hierarchyAbstractPropertyProcessor,AbstractValueProcessor,AbstractTypedValueProcessor,AbstractNumericProcessor,BaseProcessor, all per-type processors,PropertyProcessorFactory,ComposedValueProcessorFactory,MultiTypeProcessor,PropertyMetaDataCollection) in favour of a singlePropertyFactorythat applies ordered modifiersAbstractValidatorFactorysubclasses inDraft_07, making it straightforward to add or override behaviour per draft versionConstModifier,IntToFloatModifier,NullModifier,TypeCheckModifier,DefaultValueModifier,DefaultArrayToEmptyArrayModifier, andObjectModifieras first-class modifier classesAutoDetectionDraftfor backwards-compatible draft auto-selectionDraftTest,DefaultValueModifierTest,TypeCheckModifierTestcustomDraft.rstpage, updatedgettingStarted.rstandpostProcessor.rstTest plan
./vendor/bin/phpunit --no-coveragepasses green./vendor/bin/phpcs --standard=phpcs.xml src/ tests/Draft/reports no issuesDraft,DefaultValueModifier, andTypeCheckModifier