[WIP] TypeInfo refactoring / finetuning#1
Open
skalpa wants to merge 1 commit into
Open
Conversation
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.
Introduction
My initial objective was to get full types "comparison", so we can determine that
resolve('iterable')->accepts(resolve('array<string, MyClass>')).I use the component in my own serializer (just saw the Vienna schedule... it's called ObjectFactory and Normalizer here and not ObjectMapper but it's something we may talk about later), and in code generators (I generate Typescript classes from my PHP domain).
Therefore I appreciate the fact that the
Typeclasses are (near) independent of the PHP runtime and allowsType::object('NonExistentClass')(but it needs to be improved: some classes depend onis_a()).Uses cases to keep in mind:
number=int|floatDateTimethat declares that it implements an interface namedDateInterfacebut NOTDateTimeInterfaceType identifiers / primitives
Each type must identity itself using a type identifier. They represent the primitive types supported by a type system (null, int, string, array, object...) and are used as the foundation of the types comparison system (so a
boolwill not accept astring, but aniterablewill accept anarray).I originally went for
TypeIdentifier|stringbut settled for the current implementation as it keeps theTypeclasses decoupled from the coreTypeIdentifier::accepts()logic and makes it reusable (see below how we can now create custom atomic types that behave likeiterableor whatever).Note: We cannot completely decouple all our
Typesubclasses fromTypeIdentifieras some of them need to know what the canonobjecttype is in the system. That makes them kind of PHP-only asTypeIdentifieris really PHP primitives. That works but I've been thinking of extracting aTypeIdentifierenum with "universal" primitives one is likely to find in any language and add aPhpTypeIdentifierwith the PHP primitives (so we can makeObjectTypeandCollectionTypedepend onTypeIdentifierbut not onPhpTypeIdentifier). Now that would come with more BC breaks and might feel too anal so I'm not sure.Atomic types
Atomic types are basically defined by a name followed by optional variable types. I.e.
MyType<int>.Support for generics/variable types is implemented there and can be reused by subclasses. It is assumed that the variable types restrict a base type (so
MyType<int>is a subset ofMyType).The class can be extended or used directly (actually I started to refactor our own classes in dogfood mode so they now extend
AtomicType).The constructor accepts a
TypeIdentifier, making the type behavior independent of its name (so we can create aType::atomic(TypeIdentifier::INT, 'positive-int')that behaves like anINTor create anever<int>type that behaves like a FLOAT withType::atomic(TypeIdentifier::FLOAT, 'never', Type::int()).Nullability can also be configured. If you set
isNullableto true,AtomicTypewill behave exactly likenull(isNullable() will return true, and asNonNullable() will throw an exception). More complex nullability behavior can be implemented by subclasses (that's howBuiltinTypehandlesMIXED).$compareNameis here to allow configuring whether types that identify using the same primitive should be considered compatible (so the user can create anodd-numberand aneven-numberthat are bothINTbut don't accept each others).Note: that came up after talking to stof. We want the component to be able to make sense out of some types (i.e. know what the non nullable version of
mixedis), but also allow it to describe types that do not make any sense in PHP likenever<void>. This class allows us to make a difference betweennever, which would be the corresponding BuiltinType, andnever<void>which is a custom atomic type named "never".Object type [WIP]
Wrapping concept was removed. Object types are now atomic types (generics supports is handled by the base class) that identify themselves as
TypeIdentifier::OBJECT.If we want to decouple this class from the PHP runtime, we need to be able to specify which contracts the type supports when creating it. I.e.
Type::object(['DateTime', 'DateInterface'])should be accepted wherever aDateTimeor aDateInterfaceis accepted, but NOT where aDateTimeInterfaceis accepted, even though there's a PHP class that behaves this way. (We can update the resolvers and make them create types will all the necessary parents/interfaces during resolution).Collections
Composite types