Let’s make PHP more abstract
abstract
is a conception keyword. It is decided during the conception phase that some classes and some methods uses the abstract
keyword. Later, that keyword might disappear as needed, yet abstract
never appears sponteanously. Nobody decides abstraction late in the coding phase.
That seems to be a lack of practise rather than a will. So, let’s see how it is possible to read a PHP source code, and spot classes and methods that may be abstract. Basically, it means reverting the definition of abstract
classes and methods, and finding which one fit the constraints.
Abstract classes
An abstract
class is a class that can’t be instantiated. It is the only point in the definition. The class must be inherited by another class, which, in turns can be instantiated. The original class is never instantiated directly.
Other usages of the class, such as types, or static call (methods, constants, ::class
, properties) are still valid. The impact of abstract
on class usage is limited.
So, potential abstract
classes are classes that are never instantiated in the code. Then, adding the keyword to the class definition has other impact at all. In the code below, A
and B
can be abstract
.
<?php class A {} class B extends A {} class C extends B {} new C; ?>
A note with abstract
: it may be used with any class in a class hierarchy, except the last one. In other words, an abstract
class may extend a concrete class. The former must be extended again, before being instantiated. Yet, this is legit code, and it was never see in production.
<?php abstract class A {} class B extends A {} abstract class C extends B {} class D extends C {} new B; new D; ?>
Abstract methods
Methods may also be abstract
, although the set of constraints to realize the migration is wider than for classes.
When marking a method abstract
, the method retains its signature, yet looses its body. It then requires every child class to define this method, be marked abstract
too, or be omitted.
As a consequence, the current class needs to be marked abstract
too. The constraints to make a class abstract
now apply to the class itself : it mustn’t be instantiated on its own.
Lastly, the body of the current method shouldn’t be used. Besides the inheriting of methods, child method can’t make a direct call to the soon-to-be-abstract method : such call prevents its removal. In fact, the method should not be called directly, by its child method or any other method actually.
<?php // No new A in the code class A { // function foo() is always defined in every child class function foo() { return 1; } // function bar() is not available in AC, so it can't be abstract function bar() { return 10; } // function foobar() is always defined in every child class // yet, the children methods depend on the parent implementation : call to parent::foobar() function foobar() { return 100; } } class AB extends A { function foo() { return 2; } function bar() { return 11; } function foobar() { return 1 + parent::foobar(); } } class AC extends A { function foo() { return 4; } function foobar() { return 2 + parent::foobar(); } } ?>
Note that the overwriting method doesn’t have to be in a direct child, but may be in a further generation. The intermediate class will require the abstract
keyword too, if it can.
<?php // No new A in the code class A { // function foo() is always defined in every child class function foo() { return 1; } } //No foo() method, so it will have to be made abstract too. class B extends A { } //foo() is overwritten here. class C extends B { function foo() { return 4; } } ?>
To sum up, methods may be marked abstract
, when + Their hosting class can be made abstract
+ All the child classes have a local definition of the method, or they can be abstract
too + The current method is never called directly
Advantages of abstract classes and methods
Abstract classes are an alternative to interfaces. They allow for properties definition, and default implementation for methods.
Semantically, an abstract class represents an incomplete object. It is incomplete as it needs extra methods to be able to represent an actual object.
In fact, an abstract class cannot be instantiated, so it makes type checks more complete. There is no way anymore for the the incomplete parent class to be provided as a parameter which requires the child class. This would have lead to undefined methods, for example.
<?php abstract class A {} class B extends A {} // Here, only a B class can be provided function foo(B $x) {} function bar(A $x) {} ?>
In fact, with the code above, making the B
class final will make the types A
equivalent to B
. It could simplify the type.
abstract
methods act as a template. They ensure the inherited method have a specific signature, yet do not provide a default implementation. They also apply a strong pressure on the current code, as all children class have to declare that method, and the current class has to be abstract
too.
When to go abstract?
When reviewing produced code, adding the abstract
keyword turns an observed code discipline into a enforcable specification. The class that was never instantiated, is now enforced by PHP.
As usual, the code reflects reality, not intent. For example, even a specific class that has never been instantiated so far, might be in the future, remote or not. This cannot be infered from the code, and after discussion, it may be better to leave it alone.
As a general rule of thumb, it is easier to add the abstract
keyword to any possible candidate. It makes the current code more tightly organized, and may be dropped later, when needed.
let’s make PHP more abstract
Definitely, abstract
is a conception keyword, that is used by planned code. Yet, it is also interesting to see the abstract
concept emerge in the source code. A programming feature often emerges from a common coding pattern, and abstract
is one of them. It is possible to use it before, during and after coding. In fact, 31% of PHP source code could use an average of 5 abstract
classes.
Even when the keyword is eventually set aside, seeing the abstract pattern appear in the source lead to interesting questions about its organisation. And often, it is adopted : abstract
is easily remembered and taken into consideration when writing code.
This approach to add abstract
to a written PHP source cannot be used with other keywords, such as final
. final
prevents a class or a method to be extended : so, it is always the last declaration in the hierarchy. This is too easy to spot, and leads to way too many candidates, or a strict coding convention.