# Interface declarations

<?php

interface ThrowableInterface {
	public function getMessage();
}

class Exception_foo implements ThrowableInterface {
	public $foo = "foo";

	public function getMessage() {
		return $this->foo;
	}
}

==>

Template(
  TextInterpolation(PhpOpen),
  InterfaceDeclaration(interface,
    Name,
    DeclarationList(
      MethodDeclaration(
        Visibility,
        function,
        Name,
        ParamList))),
  ClassDeclaration(
    class,
    Name,
    ClassInterfaceClause(implements, Name),
    DeclarationList(
      PropertyDeclaration(
        Visibility,
        VariableDeclarator(VariableName, AssignOp, String)),
      MethodDeclaration(
        Visibility,
        function,
        Name,
        ParamList,
        Block(
          ReturnStatement(return,MemberExpression(
            VariableName,
            Name)))))))

# Use declarations

<?php

trait AbstractTrait
{
    use LoggerAwareTrait;
    use LoggerAwareTrait, OtherTrait {}
    use LoggerAwareTrait, OtherTrait;
}

class AbstractCache
{
    use AbstractTrait {
        deleteItems as private;
        AbstractTrait::deleteItem as delete;
        AbstractTrait::hasItem as has;
    }
}

==>

Template(
  TextInterpolation(PhpOpen),
  TraitDeclaration(trait,
    Name,
    DeclarationList(
      UseDeclaration(use,Name),
      UseDeclaration(use,Name, Name, UseList),
      UseDeclaration(use,Name, Name))),
  ClassDeclaration(
    class,
    Name,
    DeclarationList(
      UseDeclaration(use,
        Name,
        UseList(
          UseAsClause(Name, as, Visibility),
          UseAsClause(ScopedExpression(Name, ClassMemberName(Name)), as, Name),
          UseAsClause(ScopedExpression(Name, ClassMemberName(Name)), as, Name))))))

# Namespace names in namespaces

<?php

namespace Be \ ta {
    class A {}
    class B {}
}

==>

Template(
  TextInterpolation(PhpOpen),
  NamespaceDefinition(namespace,
    QualifiedName(NamespaceName(Name), Name),
    Block(
      ClassDeclaration(class,
        Name,
        DeclarationList),
      ClassDeclaration(class,
        Name,
        DeclarationList))))

# Class declarations

<?php
class foo {
  function __construct($name) {
    $GLOBALS['List']= &$this;
    $this->Name = $name;
    $GLOBALS['List']->echoName();
  }

  function echoName() {
    $GLOBALS['names'][]=$this->Name;
  }
}

==>

Template(
  TextInterpolation(PhpOpen),
  ClassDeclaration(class,
    Name,
    DeclarationList(
      MethodDeclaration(function,
        Name,
        ParamList(
          Parameter(
            VariableName)),
        Block(
          ExpressionStatement(AssignmentExpression(
            SubscriptExpression(VariableName, String),
            AssignOp,
            VariableName)),
          ExpressionStatement(AssignmentExpression(
            MemberExpression(
              VariableName,
              Name),
            AssignOp,
            VariableName)),
          ExpressionStatement(CallExpression(
            MemberExpression(SubscriptExpression(VariableName, String), Name),
            ArgList)))),
      MethodDeclaration(function,
        Name,
        ParamList,
        Block(
          ExpressionStatement(AssignmentExpression(
            SubscriptExpression(SubscriptExpression(VariableName, String)),
            AssignOp,
            MemberExpression(VariableName,Name))))))))

# Class declarations with base classes

<?php
class A extends B {

}

==>

Template(
  TextInterpolation(PhpOpen),
  ClassDeclaration(class,
    Name,
    BaseClause(extends, Name),
    DeclarationList))

# Function parameters

<?php
function test(int $a, string ...$b)
{
}

==>

Template(
  TextInterpolation(PhpOpen),
  FunctionDefinition(function,
    Name,
    ParamList(
      Parameter(
        NamedType(Name),
        VariableName),
      VariadicParameter(
        NamedType(Name),
        VariableName)),
    Block))

# Functions with default parameters

<?php
function a($arg = self::bar) {
  echo $arg;
}

==>

Template(
  TextInterpolation(PhpOpen),
  FunctionDefinition(function,
    Name,
    ParamList(
      Parameter(
        VariableName,
        AssignOp,
        ScopedExpression(Name, ClassMemberName(Name)))),
    Block(EchoStatement(echo,VariableName))))

# Static variables in functions

<?php
function blah()
{
  static $hey=0, $yo=0;
}

==>

Template(
  TextInterpolation(PhpOpen),
  FunctionDefinition(function,
    Name,
    ParamList,
    Block(
      FunctionStaticDeclaration(static,
        VariableDeclarator(VariableName, AssignOp, Integer),
        VariableDeclarator(VariableName, AssignOp, Integer)))))

# Defining Constants

<?php

define("CONSTANT", "Hello world.");
const CONSTANT = 'Hello World';
const ANOTHER_CONST = CONSTANT.'; Goodbye World';
const ANIMALS = array('dog', 'cat', 'bird');
define('ANIMALS', array(
    'dog',
    'cat',
    'bird'
));

==>

Template(
  TextInterpolation(PhpOpen),
  ExpressionStatement(
    CallExpression(
      Name,
      ArgList(String,String)
    )
  ),
  ConstDeclaration(const,
    VariableDeclarator(
      Name,
      AssignOp,
      String
    )
  ),
  ConstDeclaration(const,
    VariableDeclarator(
      Name,
      AssignOp,
      BinaryExpression(
        Name,
        ConcatOp,
        String
      )
    )
  ),
  ConstDeclaration(const,
    VariableDeclarator(
      Name,
      AssignOp,
      ArrayExpression(array, ValueList(String,String,String))
    )
  ),
  ExpressionStatement(
    CallExpression(
      Name,
      ArgList(
        String,
        ArrayExpression(array, ValueList(String,String,String))
      )
    )
  )
)

# Attributes

<?php

#[Test]
function a(#[Test] $a) {
  $c;
}

class PostsController
{
  #[Test]
  const CONSTANT = 'constant value';

  #[Test]
  private string $a = '';

  #[Route("/api/posts/{id}", ["GET"])]
  public function get(#[Test] $id) { /* ... */ }
}

#[MyAttribute]
#[\MyExample\MyAttribute]
#[MyAttribute(1234)]
#[MyAttribute(MyAttribute::VALUE)]
#[MyAttribute(array("key" => "value"))]
#[MyAttribute(100 + 200)]
class Thing
{
}

==>

Template(
  TextInterpolation(PhpOpen),

  FunctionDefinition(
    Attributes(Attribute(Name)),
    function,
    Name,
    ParamList(
      Parameter(
        Attributes(Attribute(Name)),
        VariableName
      )
    ),
    Block(ExpressionStatement(VariableName))
  ),

  ClassDeclaration(class,
    Name,
    DeclarationList(
      ConstDeclaration(
        Attributes(Attribute(Name)),
        const,
        VariableDeclarator(Name, AssignOp, String)
      ),

      PropertyDeclaration(
        Attributes(Attribute(Name)),
        Visibility,
        NamedType(Name),
        VariableDeclarator(VariableName, AssignOp, String)
      ),
      MethodDeclaration(
        Attributes(
          Attribute(
            Name,
            ArgList(
              String,
              ArrayExpression(String)
            )
          )
        ),
        Visibility,
        function,
        Name,
        ParamList(
          Parameter(
            Attributes(Attribute(Name)),
            VariableName
          )
        ),
        Block(BlockComment)
      )
    )
  ),
  ClassDeclaration(
    Attributes(Attribute(Name)),
    Attributes(Attribute(QualifiedName(NamespaceName(Name),Name))),
    Attributes(Attribute(Name,ArgList(Integer))),
    Attributes(Attribute(Name,ArgList(ScopedExpression(Name,ClassMemberName(Name))))),
    Attributes(Attribute(Name,ArgList(ArrayExpression(array, ValueList(Pair(String, String)))))),
    Attributes(Attribute(Name,ArgList(BinaryExpression(Integer, ArithOp, Integer)))),
    class,
    Name,
    DeclarationList
  )
)

# Enums

<?php

enum A {}
enum B implements Bar, Baz {
}
enum C: int implements Bar {}

enum Suit: string
{
  case Hearts = 'H';
  case Diamonds;
  case Clubs = 'C';
  case Spades = 'S';

  // Fulfills the interface contract.
  public function color(): string {
    return match($this) {
      Suit::Hearts, Suit::Diamonds => 'Red',
      Suit::Clubs, Suit::Spades => 'Black',
    };
  }
}

==>

Template(
  TextInterpolation(PhpOpen),
  EnumDeclaration(enum,
    Name,
    EnumBody
  ),
  EnumDeclaration(enum,
    Name,
    ClassInterfaceClause(implements, Name, Name),
    EnumBody
  ),
  EnumDeclaration(enum,
    Name,
    NamedType(Name),
    ClassInterfaceClause(implements, Name),
    EnumBody
  ),
  EnumDeclaration(enum,
    Name,
    NamedType(Name),
    EnumBody(
      EnumCase(case, Name, AssignOp, String),
      EnumCase(case, Name),
      EnumCase(case, Name, AssignOp, String),
      EnumCase(case, Name, AssignOp, String),
      
      LineComment,
      
      MethodDeclaration(
        Visibility,
        function,
        Name,
        ParamList,
        NamedType(Name),
        Block(
          ReturnStatement(return,
            MatchExpression(match,
              ParenthesizedExpression(
                VariableName
              ),
              MatchBlock(
                MatchArm(
                  ScopedExpression(Name, ClassMemberName(Name)),
                  ScopedExpression(Name, ClassMemberName(Name)),
                  String
                ),
                MatchArm(
                  ScopedExpression(Name, ClassMemberName(Name)),
                  ScopedExpression(Name, ClassMemberName(Name)),
                  String
                )
              )
            )
          )
        )
      )
    )
  )
)
