Hello. I have an idea which seems rather useful. I would like to know your opinion,
Let's say we have class Rectangle and need to have class Square which will be used in some operations. We don't need any rectangle, this must be only square. What if we could describe a type which is the same as another type but have some restrictions for properties? Something like this: class Rectangle { private $x; private $y; private $width; private $height; public function __construct($x, $y, $width, $height) { $this->x = $x; $this->y = $y; $this->width = $width; $this->height = $height; } } class Square variant of Rectangle { public function __match() { if ($this->width != $this->height) { return ['width' => 'Width and height have to be the same']; } return true; } } function drawSquare(Square $square) { // ... } $rectangle = new Rectagle(0, 0, 10, 10); drawSquare($rectangle); When drawSquare() is called, the check 'instanceof' is performed. It calls magic method __match(). If __match() does not return true, it throws an exception with the data returned: 'Order does not match OrderForCheckout. Reason: ...'. Technically, we don't have method __match() in $rectangle object. So it can be static, or a proxy object can be created here. The second variant can be extended. We may not to inherit implementation of methods, and describe another interface instead, for example new object can be read only. class Square variant of Rectangle { public function ___match() { // ... } public function getWidth() { return parent::getWidth(); } } With some changes in syntax it can be defined in this way: class Square variant of Rectangle { ... getWidth: base::getWidth; } This is like inheritance, but semantically differs a little. Inheritance has vertical direction, type variants have horisontal one. Also type variants have an access for all methods and properties of base class, even for private. Another example. Let's say we have an order checkout process which is splitted into 3 steps - cart for products, date page, delivery address page. This is not an input validation, this is a validation in business logic. class Order { public $products; public $date; public $address; } class OrderForCheckout variant of Order { public function __match() { $errors = []; if (empty($this->products)) { $errors['products'] = 'Product list is empty'; } if ($this->date === null) { $errors['date'] = 'Date is not selected'; } if (empty($this->products)) { $errors['address'] = 'Address is not selected'; } return (empty($errors) ? true : $errors); } } public function checkout(OrderForCheckout $order) { ... } $order = findCurrentOrder(); checkout($order); // if $order does not match requirements, the exception will be thrown