Skip to content

Type refactoring todos #8311

@ondrejmirtes

Description

@ondrejmirtes

Feature request

The idea is we should ban $type instanceof *Type in both inside and outside of PHPStan. I've outlined the reasons in a recent article:

 PHP is a complex language. A type often stands in for a different type. A string can be a callable. A callable can be an array. Asking $type instanceof StringType doesn't cover all possible situations, because a lot of other Type implementations can be a string too.

So we changed the preferred way to ask "is this an array?" to Type::isArray(): TrinaryLogic. And "is this a string?" to Type::isString(): TrinaryLogic. Every step like that helps us to get rid of a lot of bugs.

When we replace all instances of $type instanceof *Type, the Type interface is going to have hundreds of methods. And I'm persuaded it's the correct solution to this problem 🤣

So the reasons are:

Remaining refactorings

  • TypeUtils::getConstantStrings, ConstantStringType -> Type::getConstantStrings()
  • TypeUtils::getConstantIntegers, getIntegerRanges, ConstantIntegerType, IntegerRangeType (not sure what to replace it with, IntegerRangeType complicates things.
  • TypeUtils::getConstantTypes, getAnyConstantTypes - no known usages, maybe just deprecate it
  • TypeUtils::getDirectClassNames -> Type::getObjectClassNames()
  • TypeUtils::getConstantScalars - Type::getConstantScalars()
  • TypeUtils::getEnumCaseObjects
  • TypeUtils::toBenevolentUnion
  • TypeUtils::flattenTypes
  • VoidType -> isVoid
  • ResourceType -> isResource
  • ConstantType
  • LateResolvableType

Here are some of the hardest. There isn't going to be a single replacement method for every type, we need to figure out why we ask "$type instanceof ObjectType" and come up with the right use-case method for that on Type.

  • SubtractableType
  • ObjectWithoutClassType -> isObject (&& getObjectClassNames returns empty array)
  • StaticType, ThisType
  • ObjectType
  • AccessoryType, TypeUtils::getAccessoryTypes
  • TemplateType
  • MIxedType
  • ErrorType
  • NeverType
  • CompoundType (this one has to be done as the last one). It's heavily used in Type::accepts() and Type::isSuperTypeOf(). The implementation of these methods should be a lot simpler after the other refactorings are done.

The endgame

And once we provide methods for all use cases on Type and we deprecate $type instanceof *Type, we'll be able to do:

  • Type implementations are gonna be a flat structure. No more inheritance. ConstantStringType will not extend StringType etc.
  • There isn't going to be any need for CompoundType.
  • Get rid of TypeWithClassName
  • There's going to be a TemplateType class (currently it's an interface) and all bounds will be supported "for free".
  • There's going to be a SubtractedType class (currently it's a SubtractableType interface with a few implementations) and all types will be subtractable "for free".

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions