rfc:anonymous_classes_v2

PHP RFC: Anonymous Classes v2

Introduction

We have looked at anonymous classes before http://wiki.php.net/rfc/anonymous_classes

There has been many requests from the community to incorporate this feature, most recently here: http://grokbase.com/t/php/php-internals/146wpa11s5/revisiting-anonymous-classes from Sebastian Bergmann, author of PHPUnit.

Follows is an edited version of that communication, based on the proposed patch.

Proposal

In a unit test, test doubles (dummies, fakes, stubs, spys, and mocks) are used to simulate the behavior of complex, real objects when a real object is difficult or impossible to incorporate into a unit test. A test double can be used anywhere in the program where the program expects an object of the original class.

Traditionally, frameworks and libraries are used to automatically generate test double objects based on the original objects using reflection. This idea stems from a time when IDEs were not powerful enough to do this generation for us.

Quoting Robert C. Martin:

  Stubs and spies are very easy to write. My IDE makes it trivial.
  I just point at the interface and tell the IDE to implement it.
  Voila! It gives me a dummy. Then I just make a simple modification
  and turn it into a stub or a spy. [...]
  I don't like the strange syntax of mocking tools, and the
  complications they add to my setups. I find writing my own test
  doubles to be simpler in most cases.

The more I think about this (especially in the aftermath of the unserialize() issue), the more it appeals to me to have “real code”, generated in parts by my IDE, for my test doubles.

The question remains, though, where to put this code. Obviously it belongs with the test suite. But how to name the test double classes? Unique names are required to be able to have multiple (differently configured) test doubles for the same original class. Mocking tools (such as PHPUnit_MockObject, for instance) use random names for their test double classes.

Anonymous classes could be a solution for this problem.

This RFC proposes the following syntax for creating an object of an anonymous class, where the class does not extend from another and implements no interfaces.

$test = new class("Hello World") {
    public function __construct($greeting) {
        $this->greeting = $greeting;
    }
};

The normal rules of inheritance apply to anonymous classes, an anonymous class that inherits a parent would be declared as follows:

<?php
$test = new class("Hello World")
            extends AnotherClass {
    public function __construct($greeting) {
        $this->greeting = $greeting;
    }
};

An anonymous class, as any other class, can implement many interfaces as follows:

<?php
interface SomeInterface {
    public function method();
}
 
$test = new class("Hello World")
            extends AnotherClass implements SomeInterface {
    public function __construct($greeting) {
        $this->greeting = $greeting;
    }
 
    /* implement SomeInterface */
    public function method() {}
};

An anonymous class may also use traits, in the normal way:

<?php
$test = new class(["hello", "world"])
            implements ArrayAccess {
    /* assume this trait implements ArrayAccess and contains ::setArray */
    use ArrayAccessor;
 
    public function __construct($greeting) {
        $this->setArray("data", $greeting);
    }
 
    protected $data;
};
?>

The ReflectionClass has been updated with ReflectionClass::isAnonymous()

Proposed PHP Version(s)

7

RFC Impact

To Existing Extensions

Existing extensions performing magic or analysis of opcodes may require revision.

To Opcache

Opcache might need some revision, and a new optimization may also be possible. Discussion with dmitry required.

Proposed Voting Choices

A straight yes/no vote should be conducted.

A 2/3 majority is required for the RFC to pass.

Patches and Tests

References

Links to external references, discussions or RFCs

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/anonymous_classes_v2.txt · Last modified: 2017/09/22 13:28 (external edit)