Abstraction de classes
PHP a des classes, méthodes et propriétés abstraites.
Les classes définies comme abstraites ne peuvent pas être
instanciées, et toute classe contenant au moins une
méthode abstraite doit elle-aussi être abstraite.
Les méthodes définies comme abstraites se contentent de déclarer la signature de la méthode et d'indiquer si elle est publique ou protégée ;
elles ne peuvent pas définir l'implémentation. Les propriétés définies comme abstraites
peuvent déclarer une exigence pour le comportement de get
ou de set
,
et peuvent fournir une implémentation pour l'une de ces opérations, mais pas les deux.
Lors de l'héritage d'une classe abstraite, toutes les méthodes
marquées comme abstraites dans la déclaration de la classe parente
doivent être définies par la classe enfant et suivre les règles habituelles
d'héritage et de
compatibilité de signature.
À partir de PHP 8.4, une classe abstraite peut déclarer une propriété abstraite, publique ou protégée.
Une propriété abstraite protégée peut être satisfaite par une propriété accessible en lecture/écriture
depuis un contexte protégé ou public.
Une propriété abstraite peut être satisfaite soit par une propriété standard, soit par une propriété
avec des hooks définis, correspondant à l'opération requise.
Exemple #1 Exemple de méthode abstraite
<?php
abstract class AbstractClass
{
// Force les classes filles à définir cette méthode
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// méthode commune
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";
?>
L'exemple ci-dessus va afficher :
ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2
Exemple #2 Exemple de méthode abstraite
<?php
abstract class AbstractClass
{
// Une méthode abstraite ne doit que définir les arguments requis
abstract protected function prefixName($name);
}
class ConcreteClass extends AbstractClass
{
// Une classe enfant peut définir des arguments optionnels qui ne sont pas présents dans la signature du parent
public function prefixName($name, $separator = ".")
{
if ($name == "Pacman") {
$prefix = "Mr";
} elseif ($name == "Pacwoman") {
$prefix = "Mrs";
} else {
$prefix = "";
}
return "{$prefix}{$separator} {$name}";
}
}
$class = new ConcreteClass();
echo $class->prefixName("Pacman"), "\n";
echo $class->prefixName("Pacwoman"), "\n";
?>
L'exemple ci-dessus va afficher :
Exemple #3 Exemple de propriété abstraite
<?php
abstract class A
{
// Les classes dérivées doivent avoir une propriété publiquement accessible en lecture.
abstract public string $readable {
get;
}
// Les classes dérivées doivent avoir une propriété modifiable en écriture, protégée ou publique.
abstract protected string $writeable {
set;
}
// Les classes dérivées doivent avoir une propriété symétrique protégée ou publique.
abstract protected string $both {
get;
set;
}
}
class C extends A
{
// Cela satisfait l'exigence et rend également la propriété modifiable, ce qui est valide.
public string $readable;
// Cela NE satisferait PAS l'exigence, car la propriété n'est pas publiquement accessible en lecture.
protected string $readable;
// Cela satisfait exactement l'exigence, donc c'est suffisant.
// La propriété ne peut être modifiée qu'à partir d'un contexte protégé.
protected string $writeable {
set => $value;
}
// Cela élargit la visibilité de protégé à public, ce qui est acceptable.
public string $both;
}
?>
Une propriété abstraite dans une classe abstraite peut fournir des implémentations pour tout point d'ancrage,
mais doit avoir soit get
soit set
déclarés mais non définis (comme dans l'exemple ci-dessus).
Exemple #4 Exemple de propriété abstraite avec des hooks
<?php
abstract class A
{
// Cela fournit une implémentation par défaut (mais substituable) pour set,
// et exige que les classes dérivées fournissent une implémentation pour get.
abstract public string $foo {
get;
set {
$this->foo = $value
};
}
}
?>
ironiridis at gmail dot com ¶17 years ago
Just one more time, in the simplest terms possible:An Interface is like a protocol. It doesn't designate the behavior of the object; it designates how your code tells that object to act. An interface would be like the English Language: defining an interface defines how your code communicates with any object implementing that interface.An interface is always an agreement or a promise. When a class says "I implement interface Y", it is saying "I promise to have the same public methods that any object with interface Y has".On the other hand, an Abstract Class is like a partially built class. It is much like a document with blanks to fill in. It might be using English, but that isn't as important as the fact that some of the document is already written.An abstract class is the foundation for another object. When a class says "I extend abstract class Y", it is saying "I use some methods or properties already defined in this other class named Y".So, consider the following PHP:<?phpclass X implements Y { } class X extends Y { } ?>You would have your class implement a particular interface if you were distributing a class to be used by other people. The interface is an agreement to have a specific set of public methods for your class.You would have your class extend an abstract class if you (or someone else) wrote a class that already had some methods written that you want to use in your new class.These concepts, while easy to confuse, are specifically different and distinct. For all intents and purposes, if you're the only user of any of your classes, you don't need to implement interfaces.
mbajoras at gmail dot com ¶15 years ago
Here's an example that helped me with understanding abstract classes. It's just a very simple way of explaining it (in my opinion). Lets say we have the following code:
<?php
class Fruit {
private $color;
public function eat() {
}
public function setColor($c) {
$this->color = $c;
}
}
class Apple extends Fruit {
public function eat() {
}
}
class Orange extends Fruit {
public function eat() {
}
}
?>
Now I give you an apple and you eat it.
<?php
$apple = new Apple();
$apple->eat();
?>
What does it taste like? It tastes like an apple. Now I give you a fruit.
<?php
$fruit = new Fruit();
$fruit->eat();
?>
What does that taste like??? Well, it doesn't make much sense, so you shouldn't be able to do that. This is accomplished by making the Fruit class abstract as well as the eat method inside of it.
<?php
abstract class Fruit {
private $color;
abstract public function eat();
public function setColor($c) {
$this->color = $c;
}
}
?>
Now just think about a Database class where MySQL and PostgreSQL extend it. Also, a note. An abstract class is just like an interface, but you can define methods in an abstract class whereas in an interface they are all abstract.
shewa12kpi at gmail dot com ¶4 years ago
<?phpabstract class BaseEmployee { public $firstname, $lastname; function __construct($fn, $ln){ $this->firstname = $fn; $this->lastname = $ln; } public function getFullName() { return "$this->firstname $this->lastname"; } abstract protected static function task();}class WebDeveloper extends BaseEmployee { static function task() { return ' Develop web application'; }}class HR extends BaseEmployee { static function task() { return ' Manage human resources'; }}$webDeveloper = new WebDeveloper('shaikh','ahmed');echo $webDeveloper->getFullName();echo $webDeveloper->task();
jai at shaped dot ca ¶8 years ago
This example will hopefully help you see how abstract works, how interfaces work, and how they can work together. This example will also work/compile on PHP7, the others were typed live in the form and may work but the last one was made/tested for real:<?phpconst ¶ = PHP_EOL;interface productInterface { public function doSell(); public function doBuy();}abstract class defaultProductAbstraction implements productInterface { private $_bought = false; private $_sold = false; abstract public function doMore(); public function doSell() { $this->_sold = true; echo "defaultProductAbstraction doSell: {$this->_sold}".¶; } public function doBuy() { $this->_bought = true; echo "defaultProductAbstraction doBuy: {$this->_bought}".¶; }}class defaultProductImplementation extends defaultProductAbstraction { public function doMore() { echo "defaultProductImplementation doMore()".¶; }}class myProductImplementation extends defaultProductAbstraction { public function doMore() { echo "myProductImplementation doMore() does more!".¶; } public function doBuy() { echo "myProductImplementation's doBuy() and also my parent's dubai()".¶; parent::doBuy(); }}class myProduct extends defaultProductImplementation { private $_bought=true; public function __construct() { var_dump($this->_bought); } public function doBuy () { $this->_bought = true; echo "myProduct overrides the defaultProductImplementation's doBuy() here {$this->_bought}".¶; }}class myOtherProduct extends myProductImplementation { public function doBuy() { echo "myOtherProduct overrides myProductImplementations doBuy() here but still calls parent too".¶; parent::doBuy(); }}echo "new myProduct()".¶;$product = new myProduct();$product->doBuy();$product->doSell();$product->doMore();echo ¶."new defaultProductImplementation()".¶;$newProduct = new defaultProductImplementation();$newProduct->doBuy();$newProduct->doSell();$newProduct->doMore();echo ¶."new myProductImplementation".¶;$lastProduct = new myProductImplementation();$lastProduct->doBuy();$lastProduct->doSell();$lastProduct->doMore();echo ¶."new myOtherProduct".¶;$anotherNewProduct = new myOtherProduct();$anotherNewProduct->doBuy();$anotherNewProduct->doSell();$anotherNewProduct->doMore();?>Will result in:<?php?>
a dot tsiaparas at watergate dot gr ¶14 years ago
Abstraction and interfaces are two very different tools. The are as close as hammers and drills. Abstract classes may have implemented methods, whereas interfaces have no implementation in themselves.Abstract classes that declare all their methods as abstract are not interfaces with different names. One can implement multiple interfaces, but not extend multiple classes (or abstract classes).The use of abstraction vs interfaces is problem specific and the choice is made during the design of software, not its implementation. In the same project you may as well offer an interface and a base (probably abstract) class as a reference that implements the interface. Why would you do that?Let us assume that we want to build a system that calls different services, which in turn have actions. Normally, we could offer a method called execute that accepts the name of the action as a parameter and executes the action.We want to make sure that classes can actually define their own ways of executing actions. So we create an interface IService that has the execute method. Well, in most of your cases, you will be copying and pasting the exact same code for execute.We can create a reference implemention for a class named Service and implement the execute method. So, no more copying and pasting for your other classes! But what if you want to extend MySLLi?? You can implement the interface (copy-paste probably), and there you are, again with a service. Abstraction can be included in the class for initialisation code, which cannot be predefined for every class that you will write.Hope this is not too mind-boggling and helps someone. Cheers,Alexios Tsiaparas
swashata4u at gmail dot com ¶7 years ago
Here is another thing about abstract class and interface.Sometimes, we define an interface for a `Factory` and ease out some common methods of the `Factory` through an `abstract` class.In this case, the abstract class implements the interface, but does not need to implement all methods of the interface.The simple reason is, any class implementing an interface, needs to either implement all methods, or declare itself abstract.Because of this, the following code is perfectly ok.<?phpinterface Element { public function __construct( $config = [] ); public static function get_definition(); public function get_config(); public function set_config( $config );}abstract class Base implements Element { protected $config = []; public function get_config() { return $this->config; } public function __construct( $config = [] ) { $this->set_config( $config ); }}class MyElement extends Base { public static function get_definition() { return [ 'type' => 'MyElement', ]; } public function set_config( $config ) { $this->config = $config; }}$element = new MyElement( [ 'foo' => 'bar',] );print_r( $element->get_config() );?>You can see the tests being executed here and PHP 5.4 upward, the output is consistent. https://3v4l.org/8NqqW
shaman_master at list dot ru ¶6 years ago
Also you may set return/arguments type declaring for abstract methods (PHP>=7.0)<?phpdeclare(strict_types=1);abstract class Adapter{ protected $name; abstract public function getName(): string; abstract public function setName(string $value);}class AdapterFoo extends Adapter{ public function getName(): string { return $this->name; } public function setName(string $value): self { $this->name = $value; return $this; }}?>
Eugeny at Kostanay dot KZ ¶8 years ago
A snippet of code to help you understand a bit more about properties inside abstract classes:<?phpabstract class anotherAbsClass{ static $stProp = 'qwerty'; protected $prProp = 'walrus'; protected function callMe() { echo 'On call: ' . $this->prProp . PHP_EOL; } abstract protected function abc($arg1, $arg2); abstract public function getJunk($arg1, $arg2, $arg3, $junkCollector = true); }class someChildClass extends anotherAbsClass{ function __construct() { echo $this->callMe() . PHP_EOL; } protected function abc($val1, $val) { } function getJunk($val1, $val2, $val3, $b = false) { }}echo anotherAbsClass::$stProp; $objTest = new someChildClass; ?>
joelhy ¶14 years ago
The documentation says: "It is not allowed to create an instance of a class that has been defined as abstract.". It only means you cannot initialize an object from an abstract class. Invoking static method of abstract class is still feasible. For example:<?phpabstract class Foo{ static function bar() { echo "test\n"; }}Foo::bar();?>
sneakyimp at hotmail dot com ¶17 years ago
Ok...the docs are a bit vague when it comes to an abstract class extending another abstract class. An abstract class that extends another abstract class doesn't need to define the abstract methods from the parent class. In other words, this causes an error:
<?php
abstract class class1 {
abstract public function someFunc();
}
abstract class class2 extends class1 {
abstract public function someFunc();
}
?>
Error: Fatal error: Can't inherit abstract function class1::someFunc() (previously declared abstract in class2) in /home/sneakyimp/public/chump.php on line 7
However this does not:
<?php
abstract class class1 {
abstract public function someFunc();
}
abstract class class2 extends class1 {
}
?>
An abstract class that extends an abstract class can pass the buck to its child classes when it comes to implementing the abstract methods of its parent abstract class.
bishop ¶15 years ago
Incidentally, abstract classes do not need to be base classes:<?phpclass Foo { public function sneeze() { echo 'achoooo'; }}abstract class Bar extends Foo { public abstract function hiccup();}class Baz extends Bar { public function hiccup() { echo 'hiccup!'; }}$baz = new Baz();$baz->sneeze();$baz->hiccup();?>
jai at shaped dot ca ¶8 years ago
An interface specifies what methods a class must implement, so that anything using that class that expects it to adhere to that interface will work.eg: I expect any $database to have ->doQuery(), so any class I assign to the database interface should implement the databaseInterface interface which forces implementation of a doQuery method.<?phpinterface dbInterface { public function doQuery();}class myDB implements dbInterface { public function doQuery() { }}$myDBObj = new myDB()->doQuery();?>An abstract class is similar except that some methods can be predefined. Ones listed as abstract will have to be defined as if the abstract class were an interface.eg. I expect my $person to be able to ->doWalk(), most people walk fine with two feet, but some people have to hop along :(<?phpinterface PersonInterface() { public function doWalk($place); public function doAge();}abstract class AveragePerson implements PersonInterface() { private $_age = 0; public function doAge() { $this->_age = $this->_age+1; } public function doWalk($place) { echo "I am going to walk to $place".PHP_EOL; } abstract function talk($say);}class Joe extends AveragePerson { public function talk($say) { echo "In an Austrailian accent, Joe says: $say".PHP_EOL; }}class Bob extends AveragePerson { public function talk($say) { echo "In a Canadian accent, Bob says: $say".PHP_EOL; } public function doWalk($place) { echo "Bob only has one leg and has to hop to $place".PHP_EOL; }}$people[] = new Bob();$people[] = new Joe();foreach ($people as $person) { $person->doWalk('over there'); $person->talk('PHP rules');}?>
joebert ¶18 years ago
I don't agree with jfkallens' last comparison between Abstract Classes & Object Interfaces completely.In an Abstract Class, you can define how some methods work, where as in an Object Interface you can not.An Object Interface is essentually nothing but a list of function names that a class must define if the class implements that interface.An Abstract Class is essentually a prototype which hints towards what extending classes should be doing.An Abstract Class can also be thought of as a Base Class that provides some basic functionality, & also defines a built-in Object Interface that all extending classes will implement.So, an Object Interface is really a built-in part of an Abstract Class.
Malcolm ¶9 years ago
I've found an inconsistency with: Example #2 Abstract class exampleIf you remove the default value of $separator<?php public function prefixName($name, $separator) { }?>Then php will show this fatal message: Fatal error: Declaration of ConcreteClass::prefixName() must be compatible with AbstractClass::prefixName($name) in /index.php on line 23Stange enough it gives an incorrect declaration of "ConcreteClass::prefixName()"... It is missing both arguments. Because of that I'm assuming that this is just a bug that maybe already has been taking care of in newer versions. (Or is just specific to my version) I'm mainly noting this because it was driving me absolutely insane in some test code that I was writing derived from Example #2 (without a default value for an extra argument). Perhaps this saves some frustrations to other people.--Please note that i'm running this on php5.5. OS: ubuntu-16.04-server-amd64.isoRepo: ppa:ondrej/php# php5.5 --versionPHP 5.5.36-2+donate.sury.org~xenial+1 (cli)Copyright (c) 1997-2015 The PHP GroupZend Engine v2.5.0, Copyright (c) 1998-2015 Zend Technologies with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies
arma99eDAN at yahoo dot com ¶10 years ago
You can use an abstract class like this too:abstract class A{ public function show(){ echo 'A'; }}class B extends A{ public function hello(){ echo 'B'; parent::show(); }}$obj = new B;$obj->hello(); // BA# See that the abstract class does not have at least one abstract method# Even in this case, I'm still able to extend it, or call its non-abstract member
designbyjeeba at gmail dot com ¶14 years ago
Please be aware of the visibility of the parent fields. If the fields are private, then you are not going to see those fields in their childrens. Its basic OOP, but can be problematic sometimes.