PhpDoc Types

In this example, the grammar for describing types in phpdoc is compatible with psalm and phpstan, for example:

array {
    field1: callable(Example, int): mixed,
    field2: list<Some>,
    field3: iterable<array-key, array{ int, non-empty-string }>,
    Some::CONST_*,
    ...
}

Grammar

// Literals

%token  T_DQ_STRING_LITERAL     "([^"\\]*(?:\\.[^"\\]*)*)"
%token  T_SQ_STRING_LITERAL     '([^'\\]*(?:\\.[^'\\]*)*)'
%token  T_FLOAT_LITERAL         (?i)(?:-?[0-9]++\.[0-9]*+(?:e-?[0-9]++)?)|(?:-?[0-9]*+\.[0-9]++(?:e-?[0-9]++)?)|(?:-?[0-9]++e-?[0-9]++)
%token  T_INT_LITERAL           \-?(?i)(?:(?:0b[0-1_]++)|(?:0o[0-7_]++)|(?:0x[0-9a-f_]++)|(?:[0-9][0-9_]*+))
%token  T_BOOL_LITERAL          \b(?i)(?:true|false)\b
%token  T_NULL_LITERAL          \b(?i)(?:null)\b

// Name

%token  T_NAME                  [a-zA-Z_\x80-\xff][a-zA-Z0-9\-_\x80-\xff]*

// Special Chars

%token  T_ANGLE_BRACKET_OPEN    <
%token  T_ANGLE_BRACKET_CLOSE   >
%token  T_PARENTHESIS_OPEN      \(
%token  T_PARENTHESIS_CLOSE     \)
%token  T_BRACE_OPEN            \{
%token  T_BRACE_CLOSE           \}
%token  T_SQUARE_BRACKET_OPEN   \[
%token  T_SQUARE_BRACKET_CLOSE  \]
%token  T_COMMA                 ,
%token  T_ELLIPSIS              \.\.\.
%token  T_DOUBLE_COLON          ::
%token  T_COLON                 :
%token  T_EQ                    =
%token  T_NS_DELIMITER          \\
%token  T_NULLABLE              \?
%token  T_NOT                   \!
%token  T_OR                    \|
%token  T_AND                   &
%token  T_ASTERISK              \*

// Other

%skip   T_WHITESPACE            \s+
%skip   T_BLOCK_COMMENT         \h*/\*.*?\*/\h*

%pragma root Statement

// Literals

Literal
  : StringLiteral()
  | FloatLiteral()
  | IntLiteral()
  | BoolLiteral()
  | NullLiteral()
  | ClassConstLiteral()
  ;

StringLiteral
  : <T_SQ_STRING_LITERAL>
  | <T_DQ_STRING_LITERAL>
  ;

FloatLiteral
  : <T_FLOAT_LITERAL>
  ;

IntLiteral
  : <T_INT_LITERAL>
  ;

BoolLiteral
  : <T_BOOL_LITERAL>
  ;

NullLiteral
  : <T_NULL_LITERAL>
  ;

ClassConstLiteral
  : Name() ::T_DOUBLE_COLON:: (<T_NAME><T_ASTERISK>|<T_NAME>|<T_ASTERISK>)
  ;

// Templates

TemplateParameters
  : ::T_ANGLE_BRACKET_OPEN::
      TemplateParameter() (::T_COMMA:: TemplateParameter())* ::T_COMMA::?
    ::T_ANGLE_BRACKET_CLOSE::
  ;

TemplateParameter
  : Statement()
  ;

// Shapes

ShapeArguments
  : ::T_BRACE_OPEN::
      ShapeArgument()? (::T_COMMA:: ShapeArgument())* ::T_COMMA::?
      IsSealed() ::T_COMMA::?
    ::T_BRACE_CLOSE::
  ;

IsSealed
  : <T_ELLIPSIS>?
  ;

ShapeArgument
  : OptionalNamedShapeArgument()
  | NamedShapeArgument()
  | AnonymousShapeArgument()
  ;

OptionalNamedShapeArgument
  : ShapeKey() ::T_NULLABLE:: ::T_COLON:: ShapeValue()
  ;

NamedShapeArgument
  : ShapeKey() ::T_COLON:: ShapeValue()
  ;

AnonymousShapeArgument
  : ShapeValue()
  ;

ShapeKey
  : <T_NAME>
  | IntLiteral()
  | BoolLiteral()
  | NullLiteral()
  | StringLiteral()
  ;

ShapeValue
  : Statement()
  ;

// Callables

CallableTypeStmt
  : Name()
    ::T_PARENTHESIS_OPEN::
        CallableArguments()?
    ::T_PARENTHESIS_CLOSE::
    CallableReturnType()?
  ;

CallableArguments
  : CallableArgument() (::T_COMMA:: CallableArgument())* ::T_COMMA::?
  ;

CallableArgument
  : PrefixedVariadicCallableArgument()
  ;

PrefixedVariadicCallableArgument
  : <T_ELLIPSIS> Statement()
  | SuffixedCallableArgument()
  ;

SuffixedCallableArgument
  : Statement() (<T_EQ> | <T_ELLIPSIS>)?
  ;

CallableReturnType
  : ::T_COLON:: Statement()
  ;

// Type definition

NamedTypeStmt
  : Name() (TemplateParameters() | ShapeArguments())?
  ;

// Other common rules

Name
  : FullQualifiedName()
  | RelativeName()
  ;

FullQualifiedName
  : ::T_NS_DELIMITER:: NamePart() (::T_NS_DELIMITER:: NamePart())*
  ;

RelativeName
  : NamePart() (::T_NS_DELIMITER:: NamePart())*
  ;

NamePart
  : <T_NAME>
  ;


/**
 * -----------------------------------------------------------------------------
 *  Constant Statement
 * -----------------------------------------------------------------------------
 *
 *  A constant statement can be evaluated during translation rather than
 *  runtime, and accordingly may be used in any place that a constant may be.
 *
 */

Statement
  : BinaryStatement()
  ;

// Binary statements/expressions

BinaryStatement
  : UnionTypeStatement()
  ;

UnionTypeStatement
  : IntersectionTypeStatement() (::T_OR:: UnionTypeStatement())?
  ;

IntersectionTypeStatement
  : UnaryStatement() (::T_AND:: IntersectionTypeStatement())?
  ;

// Unary statements/expressions

UnaryStatement
  : PrefixedNullableTypeStatement()
  ;

// stmt = ?Type
PrefixedNullableTypeStatement
  : <T_NULLABLE> TypesListStatement()
  | SuffixedNullableTypeStatement()
  ;

// stmt = Type?
SuffixedNullableTypeStatement
  : TypesListStatement() <T_NULLABLE>?
  ;

TypesListStatement
  : PrimaryStatement() (
      <T_SQUARE_BRACKET_OPEN>
      ::T_SQUARE_BRACKET_CLOSE::
    )*
  ;

// Primary

PrimaryStatement
  : ::T_PARENTHESIS_OPEN:: Statement() ::T_PARENTHESIS_CLOSE::
  | Literal()
  | CallableTypeStmt()
  | NamedTypeStmt()
  ;