abstractLet’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 abstractnow 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 Aequivalent 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 abstractconcept emerge in the source code. A programming feature often emerges from a common coding pattern, and abstractis 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.