Конструктор в абстрактном классе php

Обновлено: 30.04.2024

Конструкторы представляют специальные методы, которые выполняются при создании объекта и служат для начальной инициализации его свойств. Для создания конструктора надо объявить функцию с именем __construct (с двумя подчеркиваниями впереди):

Функция конструктора в данном случае принимает два параметра. Их значения передаются свойствам класса. И теперь чтобы создать объект, нам надо передать значения для соответствующих параметров:

Параметры по умолчанию

Чтобы сделать конструктор более гибким, мы можем обозначить один или несколько параметров в качестве необязательных. Тогда при создании объекта необязательно указывать все параметры. Например, изменим конструктор следующим образом:

Таким образом, если не будут заданы параметры, вместо них будут использоваться значения "Том" и 36. И теперь мы можем создать объект Person несколькими способами:

Объявление свойств через конструктор

Начиная с версии PHP 8 в языке появилась возможность определить свойства через список параметров конструктора. Любой параметр конструктора, который имеет модификатор доступа, например, public , будет автоматически представлять новое свойство.

Например, определим свойства name и age напрямую через параметры конструктора:

Хотя в данном случае в классе явным образом не определены свойства name и age, но поскольку в списке параметров конструктора заданы параметры с такими именами и с модификатором доступа (в данном случае public ), то у класса автоматически создаются подобные переменные.

Можно сочетать оба способа объявления переменных:

При подобном объявлении свойств также можно передавать им значения по умолчанию:

Деструкторы

Деструкторы служат для освобождения ресурсов, используемых программой - для освобождения открытых файлов, открытых подключений к базам данных и т.д. Деструктор объекта вызывается самим интерпретатором PHP после потери последней ссылки на данный объект в программе.

Деструктор определяется с помощью функции __destruct (два подчеркивания впереди):

Функция деструктора определяется без параметров, и когда на объект не останется ссылок в программе, он будет уничтожен, и при этом будет вызван деструктор.

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:

// Define things a product *has* to be able to do (has to implement)
interface productInterface public function doSell ();
public function doBuy ();
>

// Define our default abstraction
abstract class defaultProductAbstraction implements productInterface private $_bought = false ;
private $_sold = false ;
abstract public function doMore ();
public function doSell () /* the default implementation */
$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 () /* non-default doBuy implementation */
$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:
/*
new myProduct()
bool(true)
myProduct overrides the defaultProductImplementation's doBuy() here 1
defaultProductAbstraction doSell: 1
defaultProductImplementation doMore()

new defaultProductImplementation()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
defaultProductImplementation doMore()

new myProductImplementation
myProductImplementation's doBuy() and also my parent's dubai()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
myProductImplementation doMore() does more!

new myOtherProduct
myOtherProduct overrides myProductImplementations doBuy() here but still calls parent too
myProductImplementation's doBuy() and also my parent's dubai()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
myProductImplementation doMore() does more!

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:
abstract class Foo
static function bar ()
echo "test\n" ;
>
>

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.

interface Element /**
* Constructor function. Must pass existing config, or leave as
* is for new element, where the default will be used instead.
*
* @param array $config Element configuration.
*/
public function __construct ( $config = [] );

/**
* Get the definition of the Element.
*
* @return array An array with 'title', 'description' and 'type'
*/
public static function get_definition ();

/**
* Get Element config variable.
*
* @return array Associative array of Element Config.
*/
public function get_config ();

/**
* Set Element config variable.
*
* @param array $config New configuration variable.
*
* @return void
*/
public function set_config ( $config );
>

abstract class Base implements Element

/**
* Element configuration variable
*
* @var array
*/
protected $config = [];

/**
* Get Element config variable.
*
* @return array Associative array of Element Config.
*/
public function get_config () return $this -> config ;
>

/**
* Create an eForm Element instance
*
* @param array $config Element 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 ) // Do something here
$this -> config = $config ;
>
>

$element = new MyElement ( [
'foo' => 'bar' ,
] );

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:

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:

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.

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.

interface dbInterface public function doQuery ();
>

class myDB implements dbInterface public function doQuery () /* implementation details here */
>
>

$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 :(

interface PersonInterface () /* every person should walk, or attempt to */
public function doWalk ( $place );
/* every person should be able to age */
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 ;
>
/* every person talks differently! */
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' );
>
?>

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:

// Define things a product *has* to be able to do (has to implement)
interface productInterface public function doSell ();
public function doBuy ();
>

// Define our default abstraction
abstract class defaultProductAbstraction implements productInterface private $_bought = false ;
private $_sold = false ;
abstract public function doMore ();
public function doSell () /* the default implementation */
$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 () /* non-default doBuy implementation */
$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:
/*
new myProduct()
bool(true)
myProduct overrides the defaultProductImplementation's doBuy() here 1
defaultProductAbstraction doSell: 1
defaultProductImplementation doMore()

new defaultProductImplementation()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
defaultProductImplementation doMore()

new myProductImplementation
myProductImplementation's doBuy() and also my parent's dubai()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
myProductImplementation doMore() does more!

new myOtherProduct
myOtherProduct overrides myProductImplementations doBuy() here but still calls parent too
myProductImplementation's doBuy() and also my parent's dubai()
defaultProductAbstraction doBuy: 1
defaultProductAbstraction doSell: 1
myProductImplementation doMore() does more!

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:
abstract class Foo
static function bar ()
echo "test\n" ;
>
>

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.

interface Element /**
* Constructor function. Must pass existing config, or leave as
* is for new element, where the default will be used instead.
*
* @param array $config Element configuration.
*/
public function __construct ( $config = [] );

/**
* Get the definition of the Element.
*
* @return array An array with 'title', 'description' and 'type'
*/
public static function get_definition ();

/**
* Get Element config variable.
*
* @return array Associative array of Element Config.
*/
public function get_config ();

/**
* Set Element config variable.
*
* @param array $config New configuration variable.
*
* @return void
*/
public function set_config ( $config );
>

abstract class Base implements Element

/**
* Element configuration variable
*
* @var array
*/
protected $config = [];

/**
* Get Element config variable.
*
* @return array Associative array of Element Config.
*/
public function get_config () return $this -> config ;
>

/**
* Create an eForm Element instance
*
* @param array $config Element 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 ) // Do something here
$this -> config = $config ;
>
>

$element = new MyElement ( [
'foo' => 'bar' ,
] );

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:

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:

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.

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.

interface dbInterface public function doQuery ();
>

class myDB implements dbInterface public function doQuery () /* implementation details here */
>
>

$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 :(

interface PersonInterface () /* every person should walk, or attempt to */
public function doWalk ( $place );
/* every person should be able to age */
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 ;
>
/* every person talks differently! */
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' );
>
?>

@Sarfraz that isn't meant in an RTFM way. I just think that having a link to the official documentation is a helpful asset. And you wouldn't believe how many people don't read official docs or even know they exist.

6 Answers 6

You can now choose to sort by Trending, which boosts votes that have happened recently, helping to surface more up-to-date answers.

Trending is based off of the highest score sort and falls back to it if no posts are trending.

An abstract class is a class that contains at least one abstract method, which is a method without any actual code in it, just the name and the parameters, and that has been marked as "abstract".

The purpose of this is to provide a kind of template to inherit from and to force the inheriting class to implement the abstract methods.

An abstract class thus is something between a regular class and a pure interface. Also interfaces are a special case of abstract classes where ALL methods are abstract.

See this section of the PHP manual for further reference.

@r0ng It would be more correct to say that an abstract class may contain abstract methods, and abstract methods must always be inside an abstract class. The defining practical features in my mind are that an abstract class can not be instantiated, but you can inherit and overwrite methods, and static methods from an abstract class can be used normally.

Abstract classes are classes that contain one or more abstract methods. An abstract method is a method that is declared, but contains no implementation. Abstract classes may not be instantiated, and require subclasses to provide implementations for the abstract methods.

1. Can not instantiate abstract class: Classes defined as abstract may not be instantiated, and any class that contains at least one abstract method must also be abstract.

2. Any class that contains at least one abstract method must also be abstract: Abstract class can have abstract and non-abstract methods, but it must contain at least one abstract method. If a class has at least one abstract method, then the class must be declared abstract.

Note: Traits support the use of abstract methods in order to impose requirements upon the exhibiting class.

3. An abstract method can not contain body: Methods defined as abstract simply declare the method's signature - they cannot define the implementation. But a non-abstract method can define the implementation.

4. When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child :If you inherit an abstract class you have to provide implementations to all the abstract methods in it.

5. Same (or a less restricted) visibility: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.

Note that abstract method should not be private.

6. Signatures of the abstract methods must match:When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child;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.

7. Abstract class doesn't support multiple inheritance:Abstract class can extends another abstract class,Abstract class can provide the implementation of interface.But it doesn't support multiple inheritance.

Note: Please note order or positioning of the classes in your code can affect the interpreter and can cause a Fatal error. So, when using multiple levels of abstraction, be careful of the positioning of the classes within the source code.

below example will cause Fatal error: Class 'horse' not found

Объясните на примере по ссылке почему нельзя вместо абстрактного класса использовать обычный класс, например не реализовывая какие то функции ы нем а реализовывая в потомках Ведь по сути обычный класс ничем не отличается от абстрактного, зачем тогда он нужен (абстрактный)?

Можно вообще и без классов писать, любых. Вопрос только в удобстве и эффективности проектирования, разработки, сопровождения и расширения кода. Ну и тык мышой.

Самое актуальное в этом вопросе - просьба о примерах из жизни IMHO речь именно о PHP. -- А по сути вопрос о том, можно ли (точнее, насколько разумно) делать большие, эволюционирующие (а для чего еще нужны абстрактные классы?) системы на PHP.

Можно, конечно, и без абстрактных классов. Но с ними все же удобнее. Например в следующем случае: вам надо реализовать иерархию классов, где есть один базовый класс и несколько производных. При этом нужно реализовать некий метод, который должен быть у всех классов, но во всех он должен быть реализован по-своему. Реализовать этот метод в базовом не абстрактном классе вы не можете - в таком случае он будет общим для всех производных. Можно, конечно, сделать его виртуальным в базовом классе и переопределять в производных, но тогда напрашивается вопрос: зачем этот метод реализовывать в базовом классе, если нужны только его реализации, зависящие от конкретного производного класса. Это иллюстрируется хрестоматийным примером про геометрические фигуры, когда у вас есть базовый класс Figure и производные от него Circle, Square, каждый из которых должен иметь метод для отрисовки (скажем, Draw()). Тут можно увидеть, что во-первых, Draw() должен быть разным для каждого из производных классов, а во-вторых, его не очень-то и нужно реализовывать в базовом, так как непонятно, как должна выглядеть "просто фигура". При этом у класса Figure может быть какой-то набор базовых методов, реализация которых может быть общей для всех производных. То есть вполне разумно сделать базовый класс именно абстрактным (а не интерфейсом или обычным, не абстрактным классом)

А мне вот интересно. PHP ведь динамический язык. Зачем в динамических языках вообще интерфейсы? $foo.getBar(); Как разница, есть ли у foo метод getBar , всё равно ведь в рантайме только узнаем.

@Veikedo, например Вы пишите интерфейс CRUD, который предполагает наличие у объекта свойств create, read, update, delete. Методы Вашего класса проверяют, что им передан объект реализующий этот интерфейс и значит не нужно проверять наличие этих методов. Другой программист, решивший воспользоваться Вашим классом делает реализацию интерфейса и передает объекты реализующие этот интерфейс в Ваш класс. Не знаю, как в IDE для php, но в Eclipce очень удобно написав: class myProvider implements CRUD < увидеть автоматически дополненые методы, которые надо реализовать из интерфейса.

@Veikedo, банально проверить плагин на работоспособность до его подключения, например. Разом проверить переданный логгер на наличие дополнительного функционала. Вообще, абстрактные классы и интерфейсы - это не про "методы, которые необходимо реализовать". Это базовые реализации с "обещаниями" и контракты.

Читайте также: