5

As per php.net documentation :

When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility. For example, if the abstract method is defined as protected, the function implementation must be defined as either protected or public, but not private. Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. For example, if the child class defines an optional argument, where the abstract method's signature does not, there is no conflict in the signature. This also applies to constructors as of PHP 5.4. Before 5.4 constructor signatures could differ.

In php.net they have shown the following example:

abstract class AbstractClass
{
    // Force Extending class to define this method
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // Common method
    public function printOut() {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";

If I run this I get the below output:

ConcreteClass1 FOO_ConcreteClass1 ConcreteClass2 FOO_ConcreteClass2

Reading the documentation, my initial understanding was "parent classes in general can not access methods from their children if they are extend" and that's why we need abstract classes. However, when I tried the same example above with a little bit of tweak, removing all the abstractions (given below) I see the exact same output as the previous example. Therefore, why do we need abstract classes in PHP. Only use case I found is while implementing an interface in the parent class; I get error if I do not declare the parent as abstract. Is there other cases where I must or it is highly recommended to use abstract class?

//abstract
class AbstractClass
{
    // Force Extending class to define this method
    //abstract protected function getValue();
    //abstract protected function prefixValue($prefix);

    // Common method
    public function printOut() {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
Kamrul Khan
  • 3,260
  • 4
  • 32
  • 59

2 Answers2

5

If I have a group of similar things, let's say furniture.

class Chair extends Furniture {
    // define methods to handle being sat on
}
class Table extends Furniture {
    // define methods to handle holding things up
}

And so on. But "Furniture" is an abstract concept. It is not, itself, a thing the way "Chair" and "Table" are. Therefore, it would be best to define:

abstract class Furniture {
    // define methods that apply to all kinds of furniture
}

It's always a good idea to think of classes as "types of thing". Some are concrete, like chairs and tables, and others are abstract, like furniture.

The point is, it makes no sense to have new Furniture() in your code, because furniture should be a specific kind. Using abstract class disallows new Furniture() and acts as a sanity check.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • If that was all abstract classes are about `class Furniture { protected function __construct() {} }` would do the trick ;-) Abstract classes make sense when `// define methods that apply to all kinds of furniture` is a lot of code (or highly repetitive code) that works on the abstract idea yet require concrete implementations + works on states of the instance that you not necessarily want to expose to the outside world (in which case you could just separate data from strategy in different classes). – VolkerK Sep 17 '15 at 00:27
  • 1
    @Niet the Dark Absol, Thanks for your answer. I believe this will be helpful for many people. However, my question was not about abstract classes in general . In my question, i demonstrated, what we are doing using abstraction can be done using simple class extension. Therefore, i wanted to know whats "special" about abstract classes? – Kamrul Khan Sep 17 '15 at 01:49
  • @user3360140 I kind of see it as the same as type hinting (`function foo(Bar $baz)`) - it itself it is no different and you can get by just fine without, but it provides a built-in sanity check, just not "disallow instantiation on abstract classes" does. – Niet the Dark Absol Sep 17 '15 at 15:26
3

However, when I tried the same example above with a little bit of tweak, removing all the abstractions (given below) I see the exact same output as the previous example.

PHP is dynamically typed so the parent class can access methods that 'may or may not exist'1; the same polymorphism still holds (for non-private methods). In a languages like Java or C#, the code would have failed to compile. (Interface/contract checking is a separate issue2.)

Thus both examples are technically valid in PHP and will produce the same output, without error or warning.

Is there other cases where I must or it is highly recommended to use [abstract methods]?

It is 'more correct' when applying nominative types / class declarations to annotate the method as abstract as this:

  • Establishes a guarantee that an implementation is provided by concrete subclasses2

  • Provides additional information for type-checking tools; and is a safeguard if stricter type checking is applied in the future

  • Declares a method signature and documentation stub; and appears in type reflection

The linked example/documentation already explain the semantic goal of abstract methods. (However it is fraught with some .. questionable wording; eg. private methods cannot be abstract.)


1 This is also known as duck typing and is allowed in many other dynamic languages - including Ruby, Python, and JavaScript - which support OO and and the same concept of abstract methods, albeit less formally, as expressed through subtype polymorphism.

2 Interface/contract checking (which is only done for type definitions) ensures that the implementing class, or subclass for abstract classes, must conform to the specified types. In the case of abstract methods this means that subclasses must provide method implementations or be abstract themselves.

Community
  • 1
  • 1
user2864740
  • 60,010
  • 15
  • 145
  • 220