Php определить класс который вызвал другой класс
Каждое определение класса начинается с ключевого слова class , затем следует имя класса, и далее пара фигурных скобок, которые заключают в себе определение свойств и методов этого класса.
Именем класса может быть любое слово, при условии, что оно не входит в список зарезервированных слов PHP, начинается с буквы или символа подчёркивания и за которым следует любое количество букв, цифр или символов подчёркивания. Если задать эти правила в виде регулярного выражения, то получится следующее выражение: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$ .
Класс может содержать собственные константы, переменные (называемые свойствами) и функции (называемые методами).
Пример #1 Простое определение класса
class SimpleClass
// объявление свойства
public $var = ‘значение по умолчанию’ ;
?php
// объявление метода
public function displayVar () echo $this -> var ;
>
>
?>
Псевдопеременная $this доступна в том случае, если метод был вызван в контексте объекта. $this — значение вызывающего объекта.
Внимание
Вызов нестатического метода статически вызывает ошибку Error . До PHP 8.0.0 это привело бы к уведомлению об устаревании, и $this не была бы определена.
Пример #2 Некоторые примеры псевдо-переменной $this
class A
function foo ()
if (isset( $this )) echo ‘$this определена (‘ ;
echo get_class ( $this );
echo «)\n» ;
> else echo «\$this не определена.\n» ;
>
>
>
?php
$a = new A ();
$a -> foo ();
$b = new B ();
$b -> bar ();
Результат выполнения данного примера в PHP 7:
$this определена (A) Deprecated: Non-static method A::foo() should not be called statically in %s on line 27 $this не определена. Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this не определена. Deprecated: Non-static method B::bar() should not be called statically in %s on line 32 Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this не определена.
Результат выполнения данного примера в PHP 8:
$this определена (A) Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27 Stack trace: #0 thrown in %s on line 27
Классы, доступные только для чтения
Начиная с PHP 8.2.0, класс может быть помечен модификатором readonly . Пометка класса как readonly добавит модификатор readonly к каждому объявленному свойству и предотвратит создание динамических свойств. Более того, невозможно добавить их поддержку с помощью атрибута AllowDynamicProperties . Попытка это сделать приведёт к ошибке компиляции.
#[ \AllowDynamicProperties ]
readonly class Foo >
?php
// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>
Поскольку ни нетипизированные, ни статические свойства не могут быть помечены модификатором readonly , классы, доступные только для чтения также не могут их объявлять:
// Fatal error: Readonly property Foo::$bar must have type
?>
readonly class Foo
public static int $bar ;
>
?php
// Fatal error: Readonly class Foo cannot declare static properties
?>
Класс readonly может быть расширен тогда и только тогда, когда дочерний класс также является классом readonly .
new
Для создания экземпляра класса используется директива new . Новый объект всегда будет создан, за исключением случаев, когда он содержит конструктор, в котором определён вызов исключения в случае возникновения ошибки. Рекомендуется определять классы до создания их экземпляров (в некоторых случаях это обязательно).
Если с директивой new используется строка ( string ), содержащая имя класса, то будет создан новый экземпляр этого класса. Если имя находится в пространстве имён, то оно должно быть задано полностью.
Замечание:
В случае отсутствия аргументов в конструктор класса, круглые скобки после названия класса можно опустить.
Пример #3 Создание экземпляра класса
// Это же можно сделать с помощью переменной:
$className = ‘SimpleClass’ ;
$instance = new $className (); // new SimpleClass()
?>
Начиная с PHP 8.0.0, поддерживается использование оператора new с произвольными выражениями. Это позволяет создавать более сложные экземпляры, если выражение представлено в виде строки ( string ). Выражения должны быть заключены в круглые скобки.
Пример #4 Создание экземпляра с использованием произвольного выражения
В данном примере мы показываем несколько вариантов допустимых произвольных выражений, которые представляют имя класса. Пример вызова функции, конкатенации строк и константы ::class .
class ClassA extends \stdClass <>
class ClassB extends \stdClass <>
class ClassC extends ClassB <>
class ClassD extends ClassA <>
function getSomeClass (): string
return ‘ClassA’ ;
>
var_dump (new ( getSomeClass ()));
var_dump (new ( ‘Class’ . ‘B’ ));
var_dump (new ( ‘Class’ . ‘C’ ));
var_dump (new ( ClassD ::class));
?>
Результат выполнения данного примера в PHP 8:
object(ClassA)#1 (0) < >object(ClassB)#1 (0) < >object(ClassC)#1 (0) < >object(ClassD)#1 (0)
В контексте класса можно создать новый объект через new self и new parent .
Когда происходит присвоение уже существующего экземпляра класса новой переменной, то эта переменная будет указывать на этот же экземпляр класса. То же самое происходит и при передаче экземпляра класса в функцию. Копию уже созданного объекта можно создать через её клонирование.
Пример #5 Присваивание объекта
$instance = new SimpleClass ();
$assigned = $instance ;
$reference =& $instance ;
$instance -> var = ‘$assigned будет иметь это значение’ ;
$instance = null ; // $instance и $reference становятся null
var_dump ( $instance );
var_dump ( $reference );
var_dump ( $assigned );
?>
Результат выполнения данного примера:
NULL NULL object(SimpleClass)#1 (1) < ["var"]=>string(30) "$assigned будет иметь это значение" >
Создавать экземпляры объекта можно двумя способами:
Пример #6 Создание новых объектов
class Test
static public function getNew ()
return new static;
>
>
?php
class Child extends Test
<>
$obj1 = new Test ();
$obj2 = new $obj1 ;
var_dump ( $obj1 !== $obj2 );
$obj3 = Test :: getNew ();
var_dump ( $obj3 instanceof Test );
$obj4 = Child :: getNew ();
var_dump ( $obj4 instanceof Child );
?>
Результат выполнения данного примера:
bool(true) bool(true) bool(true)
Обратиться к свойству или методу только что созданного объекта можно с помощью одного выражения:
Пример #7 Доступ к свойствам/методам только что созданного объекта
echo (new DateTime ())-> format ( ‘Y’ );
?>?php
Результатом выполнения данного примера будет что-то подобное:
2016
Замечание: До PHP 7.1 аргументы не имели значения, если не определена функция конструктора.
Свойства и методы
Свойства и методы класса живут в разделённых «пространствах имён», так что возможно иметь свойство и метод с одним и тем же именем. Ссылки как на свойства, так и на методы имеют одинаковую нотацию, и получается, что получите вы доступ к свойству или же вызовете метод — определяется контекстом использования.
Пример #8 Доступ к свойству vs. вызов метода
public function bar () return ‘метод’ ;
>
>
$obj = new Foo ();
echo $obj -> bar , PHP_EOL , $obj -> bar (), PHP_EOL ;
Результат выполнения данного примера:
свойство метод
Это означает, что вызвать анонимную функцию, присвоенную переменной, напрямую не получится. Вместо этого свойство должно быть назначено, например, переменной. Можно вызвать такое свойство напрямую, заключив его в скобки.
Пример #9 Вызов анонимной функции, содержащейся в свойстве
public function __construct () $this -> bar = function() return 42 ;
>;
>
>
echo ( $obj -> bar )(), PHP_EOL ;
Результат выполнения данного примера:
extends
Класс может наследовать константы, методы и свойства другого класса используя ключевое слово extends в его объявлении. Невозможно наследовать несколько классов, один класс может наследовать только один базовый класс.
Наследуемые константы, методы и свойства могут быть переопределены (за исключением случаев, когда метод или константа класса объявлены как final) путём объявления их с теми же именами, как и в родительском классе. Существует возможность доступа к переопределённым методам или статическим свойствам путём обращения к ним через parent::
Замечание: Начиная с PHP 8.1.0, константы можно объявлять окончательными (final).
Пример #10 Простое наследование классов
class ExtendClass extends SimpleClass
// Переопределение метода родителя
function displayVar ()
echo «Расширенный класс\n» ;
parent :: displayVar ();
>
>
?php
$extended = new ExtendClass ();
$extended -> displayVar ();
?>
Результат выполнения данного примера:
Расширенный класс значение по умолчанию
Правила совместимости сигнатуры
При переопределении метода его сигнатура должна быть совместима с родительским методом. В противном случае выдаётся фатальная ошибка или, до PHP 8.0.0, генерируется ошибка уровня E_WARNING . Сигнатура является совместимой, если она соответствует правилам контравариантности, делает обязательный параметр необязательным, добавляет только необязательные новые параметры и не ограничивает, а только ослабляет видимость. Это известно как принцип подстановки Барбары Лисков или сокращённо LSP. Правила совместимости не распространяются на конструктор и сигнатуру private методов, они не будут выдавать фатальную ошибку в случае несоответствия сигнатуры.
Пример #11 Совместимость дочерних методов
class Base
public function foo ( int $a ) echo «Допустимо\n» ;
>
>
class Extend1 extends Base
function foo ( int $a = 5 )
parent :: foo ( $a );
>
>
class Extend2 extends Base
function foo ( int $a , $b = 5 )
parent :: foo ( $a );
>
>
$extended1 = new Extend1 ();
$extended1 -> foo ();
$extended2 = new Extend2 ();
$extended2 -> foo ( 1 );
Результат выполнения данного примера:
Допустимо Допустимо
Следующие примеры демонстрируют, что дочерний метод, который удаляет параметр или делает необязательный параметр обязательным, несовместим с родительским методом.
Пример #12 Фатальная ошибка, когда дочерний метод удаляет параметр
class Base
public function foo ( int $a = 5 ) echo «Допустимо\n» ;
>
>
class Extend extends Base
function foo ()
parent :: foo ( 1 );
>
>
Результат выполнения данного примера в PHP 8 аналогичен:
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
Пример #13 Фатальная ошибка, когда дочерний метод делает необязательный параметр обязательным.
class Base
public function foo ( int $a = 5 ) echo «Допустимо\n» ;
>
>
class Extend extends Base
function foo ( int $a )
parent :: foo ( $a );
>
>
Результат выполнения данного примера в PHP 8 аналогичен:
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
Внимание
Переименование параметра метода в дочернем классе не является несовместимостью сигнатуры. Однако это не рекомендуется, так как приведёт к Error во время выполнения, если используются именованные аргументы.
Пример #14 Ошибка при использовании именованных аргументов и параметров, переименованных в дочернем классе
class A public function test ( $foo , $bar ) <>
>
class B extends A public function test ( $a , $b ) <>
>
// Передача параметров согласно контракту A::test()
$obj -> test ( foo : «foo» , bar : «bar» ); // ОШИБКА!
Результатом выполнения данного примера будет что-то подобное:
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14 Stack trace: #0 thrown in /in/XaaeN on line 14
::class
Ключевое слово class используется для разрешения имени класса. Чтобы получить полное имя класса ClassName , используйте ClassName::class . Обычно это довольно полезно при работе с классами, использующими пространства имён.
Пример #15 Разрешение имени класса
Результат выполнения данного примера:
NS\ClassName
Замечание:
Разрешение имён класса с использованием ::class происходит на этапе компиляции. Это означает, что на момент создания строки с именем класса автозагрузки класса не происходит. Как следствие, имена классов раскрываются, даже если класс не существует. Ошибка в этом случае не выдаётся.
Пример #16 Отсутствует разрешение имени класса
print Does\Not\Exist ::class;
?>?php
Результат выполнения данного примера:
Does\Not\Exist
Начиная с PHP 8.0.0, константа ::class также может использоваться для объектов. Это разрешение происходит во время выполнения, а не во время компиляции. То же самое, что и при вызове get_class() для объекта.
Пример #17 Разрешение имени объекта
namespace NS class ClassName >
>
$c = new ClassName ();
print $c ::class;
?>?php
Результат выполнения данного примера:
NS\ClassName
Методы и свойства Nullsafe
Начиная с PHP 8.0.0, к свойствам и методам можно также обращаться с помощью оператора «nullsafe»: ?-> . Оператор nullsafe работает так же, как доступ к свойству или методу, как указано выше, за исключением того, что если разыменование объекта выдаёт null , то будет возвращён null , а не выброшено исключение. Если разыменование является частью цепочки, остальная часть цепочки пропускается.
Аналогично заключению каждого обращения в is_null() , но более компактный.
Пример #18 Оператор Nullsafe
// Начиная с PHP 8.0.0, эта строка:
$result = $repository ?-> getUser ( 5 )?-> name ;
// Эквивалентна следующему блоку кода:
if ( is_null ( $repository )) $result = null ;
> else $user = $repository -> getUser ( 5 );
if ( is_null ( $user )) $result = null ;
> else $result = $user -> name ;
>
>
?>
Замечание:
Оператор nullsafe лучше всего использовать, когда null считается допустимым и ожидаемым значением для возвращаемого свойства или метода. Для индикации ошибки предпочтительнее выбрасывать исключение.
User Contributed Notes 13 notes
15 years ago
I was confused at first about object assignment, because it’s not quite the same as normal assignment or assignment by reference. But I think I’ve figured out what’s going on.
First, think of variables in PHP as data slots. Each one is a name that points to a data slot that can hold a value that is one of the basic data types: a number, a string, a boolean, etc. When you create a reference, you are making a second name that points at the same data slot. When you assign one variable to another, you are copying the contents of one data slot to another data slot.
Now, the trick is that object instances are not like the basic data types. They cannot be held in the data slots directly. Instead, an object’s «handle» goes in the data slot. This is an identifier that points at one particular instance of an obect. So, the object handle, although not directly visible to the programmer, is one of the basic datatypes.
What makes this tricky is that when you take a variable which holds an object handle, and you assign it to another variable, that other variable gets a copy of the same object handle. This means that both variables can change the state of the same object instance. But they are not references, so if one of the variables is assigned a new value, it does not affect the other variable.
// Assignment of an object
Class Object public $foo = «bar» ;
>;
$objectVar = new Object ();
$reference =& $objectVar ;
$assignment = $objectVar
//
// $objectVar —>+———+
// |(handle1)—-+
// $reference —>+———+ |
// |
// +———+ |
// $assignment —>|(handle1)—-+
// +———+ |
// |
// v
// Object(1):foo=»bar»
//
?>
$assignment has a different data slot from $objectVar, but its data slot holds a handle to the same object. This makes it behave in some ways like a reference. If you use the variable $objectVar to change the state of the Object instance, those changes also show up under $assignment, because it is pointing at that same Object instance.
$objectVar -> foo = «qux» ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );
//
// $objectVar —>+———+
// |(handle1)—-+
// $reference —>+———+ |
// |
// +———+ |
// $assignment —>|(handle1)—-+
// +———+ |
// |
// v
// Object(1):foo=»qux»
//
?>
But it is not exactly the same as a reference. If you null out $objectVar, you replace the handle in its data slot with NULL. This means that $reference, which points at the same data slot, will also be NULL. But $assignment, which is a different data slot, will still hold its copy of the handle to the Object instance, so it will not be NULL.
$objectVar = null ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );
Как узнать имя метода, который вызвал метод?
Из примера думаю суть ясна, как узнать имя класса нашел, мне бы теперь узнать имя дочернего метода который вызвал метод родителя, можно конечно это имя передать параметром, но так не интересно же.
- Вопрос задан более трёх лет назад
- 3362 просмотра
8 комментариев
Оценить 8 комментариев

И где здесь ООП? Класс от которого наследуются ничего не должен знать о классах которые его наследуют, это вообще не ООП подход

Александр Шаповал @sanek_os9 Автор вопроса
Максим Федоров: он у меня умный потому знает кто дергает его методы, почему бы нет? Наверное я книжки плохо читал
Евгений @immaculate
Александр Шаповал: Такой подход — 99.99% знак говнокода. Или даже 100%. Не должны методы знать такую информацию. В нормальной архитектуре это ни к чему.

Александр Шаповал @sanek_os9 Автор вопроса
Евгений: в своём случае, в методе display я подключаю файл шаблона, что бы в дочернем методе не передавать имя файла который нужно подключить я беру это имя из его названия и подключаю. В итоге получаю что какой метод вызывает display такой файл и подключаю, как по мне вполне юзабельно.
Евгений @immaculate
Александр Шаповал: Гораздо понятнее, проще, быстрее и красивее определить у класса атрибут типа template_name .

Александр Шаповал @sanek_os9 Автор вопроса
Евгений: ну это тогда придется его определять у каждого метода (если в нем предусмотрен вывод), и хорошо когда этот атрибут соответствует имени метода, что бы когда смотришь шаблон было видно какой файл в каком классе используется, поэтому встает вопрос зачем указывать то, что заранее известно. Что касается понимания, тут все просто, достаточно один раз понять что файл шаблона подключается исходя из имени метода и класса.
Раньше я писал
class posts extends Controller < public function actionIndex() < $this->display('posts/index'); > >
class posts extends Controller < public function actionIndex() < $this->display(); > >
Один раз в методе display написал «не красиво» зато потом все красиво и структура файлов у шаблона тоже красива, потому что иначе он не будет работать.
Евгений @immaculate
Александр Шаповал: Поверьте моему опыту. Ради экономии 7-10 символов, вы усложнили код для понимания и изменения. И это потом выльется в часы отладки у вас или следующего разработчика.

Александр Шаповал @sanek_os9 Автор вопроса
Евгений: спасибо за совет. Тут суть не только в сокращении кода а и содержании в порядке файлов шаблона, ведь легко можно там развести бардак. Пока оставлю как есть, а в дальнейшем если что, понесу всю ответственность:)
Наследование классов
— это механизм, посредством которого один или несколько классов можно получить из некоторого базового класса. Класс, который получается в результате наследования от другого, называется его подклассом. Эту связь обычно описывают с помощью терминов «родительский» и «дочерний». Дочерний класс происходит от родительского и наследует его характеристики. Эти характеристики состоят из свойств и методов. Обычно в дочернем классе к функциональности родительского класса (который также называют суперклассом) добавляются новые функциональные возможности. Поэтому говорят, что дочерний класс расширяет родительский.
Прежде чем приступить к изучению синтаксиса наследования, давайте рассмотрим проблемы, которые оно поможет нам решить. Давайте вернемся к классу ShopProduct (который мы создали в предыдущей статье). В настоящее время он является достаточно обобщенным. С его помощью можно оперировать самыми разными товарами:
class ShopProduct < public $title; public $producerMainName; public $producerFirstName; public $price; function __construct($title, $firstName, $mainName, $price) < $this->title = $title; $this->producerMainName = $mainName; $this->producerFirstName = $firstName; $this->price = $price; > function getProducer() < return "producerFirstName> producerMainName>"; > > $product1 = new ShopProduct('Собачье сердце', 'Михаил', 'Булгаков', 5.99); $product2 = new ShopProduct('Первый снег', 'Группа', 'Моральный кодекс', 2.99); echo "Автор: ".$product1->getProducer()."
"; echo "Исполнитель: ".$product2->getProducer()."";
На выходе получаем следующее:

Как видим, разделение имени автора на две части пригодилось нам при работе и с книгами, и с компакт-дисками. В этом случае мы можем сортировать товары по фамилии автора (т.е. по полю, содержащему «Булгаков» и «Моральный кодекс»), а не по имени, в котором содержатся малозначимые «Михаил» и «Группа». Лень — это отличная стратегия проектирования, поэтому на данном этапе вам не следует переживать по поводу использования класса ShopProduct для более чем одного типа товара.
Но если в нашем примере добавить несколько новых требований, то все сразу усложнится. Представьте, например, что вам нужно отобразить данные, специфичные для книг и компакт-дисков. Скажем, для CD желательно вывести общее время звучания, а для книг — общее количество страниц. Конечно, могут быть и другие отличия, но эти хорошо иллюстрируют суть проблемы. Как расширить наш пример, чтобы учесть все эти изменения? На ум сразу приходят два варианта. Во-первых, можно поместить все данные в класс ShopProduct. Во-вторых, можно разбить ShopProduct на два отдельных класса.
Давайте рассмотрим первый подход. Итак, мы объединяем данные о книгах и компакт-дисках в одном классе:
class ShopProduct < public $title; public $producerMainName; public $producerFirstName; public $price; // Два новых свойства public $playLength; public $numPages; function __construct($title, $firstName, $mainName, $price, $numPages = 0, $playLength = 0) < $this->title = $title; $this->producerMainName = $mainName; $this->producerFirstName = $firstName; $this->price = $price; // Расширяем конструктор $this->numPages = $numPages; $this->playLength = $playLength; > function getProducer() < return "producerFirstName> producerMainName>"; > // Два новых метода function getNumberOfPages() < return $this->numPages; > function getPlayLength() < return $this->playLength; > >
Чтобы продемонстрировать большой объем выполняемой работы по кодированию, в данном примере были использованы методы доступа к свойствам $numPages и $playLength. В результате объект, экземпляр которого создается с помощью такого класса, будет всегда содержать избыточные методы. Кроме того, для CD экземпляр объекта нужно создавать с помощью бессмысленного аргумента конструктора. Таким образом, для CD будет сохраняться информация и функциональные возможности класса, относящиеся к книгам (общее количество страниц), а для книг — данные о времени звучания CD. Вероятно, пока вы можете с этим смириться. Но что будет, если мы добавим больше типов товаров, причем каждый — с собственными методами, а затем добавим больше методов для каждого типа? Наш класс будет становиться все более сложным и трудным для использования.
Поэтому принудительное объединение полей, относящихся к разным товарам, в один класс приведет к созданию слишком громоздких объектов с лишними свойствами и методами. Но этим проблемы не ограничиваются. С функциональностью тоже возникнут трудности. Представьте метод, который выводит краткую информацию о товаре. Скажем, отделу продаж нужна информация о товаре в виде одной строки для использования в счете-фактуре. Они хотят, чтобы мы включили в нее время звучания для компакт-диска и количество страниц для книг. Таким образом, при реализации этого метода нам придется учитывать тип каждого товара. Для отслеживания формата объекта можно использовать специальный флаг. Приведем пример:
function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; if ($this->type == 'book') $base .= ": numPages> стр."; else if ($this->type == 'cd') $base .= ": Продолжительность - playLength>"; return $base; >
Как видите, чтобы правильно установить значение свойства $type, нам нужно в конструкторе проверить значение аргумента $numPages. И снова класс ShopProduct стал более сложным, чем нужно. По мере добавления дополнительных отличий в форматы или новых форматов, нам будет трудно справляться с реализацией этого метода. Поэтому, видимо, для решения данной задачи необходимо применить второй подход.
Поскольку ShopProduct начинает напоминать «два класса в одном», мы должны это признать и создать два типа вместо одного. Однако если мы будем создавать два отдельных класса (например, CDProduct и BookProduct) то возникнет чрезмерное дублирование, т.к. в обоих классах содержаться некоторые одинаковые свойства и методы. Нам нужно создать общую функциональность в одном месте, чтобы избежать дублирования, но в то же время сделать так, чтобы при вызове метода, выводящего краткую информацию о товаре, учитывались особенности форматирования. Одним словом, нам необходимо использовать наследование.
Первый шаг в построении дерева наследования — найти элементы базового класса, которые не соответствуют друг другу или которыми нужно оперировать иначе. Мы знаем, что методы getPlayLength() и getNumberOfPages() противоречат друг другу. Нам также известно, что нужно создать разные реализации метода getSummaryLine(). Давайте используем эти различия как основу для создания двух производных классов:
class ShopProduct < public $title; public $producerMainName; public $producerFirstName; public $price; public $playLength; public $numPages; function __construct($title, $firstName, $mainName, $price, $numPages = 0, $playLength = 0) < $this->title = $title; $this->producerMainName = $mainName; $this->producerFirstName = $firstName; $this->price = $price; $this->numPages = $numPages; $this->playLength = $playLength; > function getProducer() < return "producerFirstName> producerMainName>"; > function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; return $base; > > class CDProduct extends ShopProduct < function getPlayLength() < return $this->playLength; > function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; $base .= ": Продолжительность - playLength>"; return $base; > > class BookProduct extends ShopProduct < function getNumberOfPages() < return $this->numPages; > function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; $base .= ": numPages> стр."; return $base; > >
Чтобы создать дочерний класс, необходимо использовать в объявлении класса ключевое слово extends. В данном примере мы создали два новых класса, BookProduct и CDProduct. Оба они расширяют класс ShopProduct.
Поскольку в производных классах конструкторы не определяются, при создании экземпляров объектов этих классов будет автоматически вызываться конструктор родительского класса. Дочерние классы наследуют доступ ко всем методам типа public и protected родительского класса (но не к методам и свойствам типа private). Это означает, что мы можем вызвать метод getProducer() для экземпляра объекта класса CDProduct, хотя метод getProducer() определен в классе ShopProduct:
$product2 = new CDProduct('Первый снег', 'Группа', 'Моральный кодекс', 2.99, null, 55.01); echo "Исполнитель: ".$product2->getProducer()."";
Таким образом, оба наших дочерних класса наследуют поведение общего родительского класса. И мы можем обращаться с объектом BookProduct так, как будто это объект типа ShopProduct. Обратите внимание на то, что для обеспечения собственной реализации в обоих классах CDProduct и BookProduct переопределяется метод getSummaryLine(). Производные классы могут расширять и изменять функциональность родительских классов. И в то же время каждый класс наследует свойства родительского класса.
Реализация этого метода в Суперклассе может показаться избыточной, поскольку метод переопределяется в обоих дочерних классах. Тем не менее мы предоставляем базовый набор функциональных возможностей, который можно будет использовать в новом дочернем классе. Наличие этого метода в суперклассе также гарантирует для клиентского кода, что во всех объектах типа ShopProduct будет присутствовать метод getSummaryLine(). Позже вы увидите, как можно выполнить это требование в базовом классе, не предоставляя никакой его реализации. Каждый дочерний объект класса ShopProduct унаследует все свойства своего родителя. В собственных реализациях метода getSummaryLine() для обоих классов CDProduct и BookProduct обеспечивается доступ к свойству $title.
С понятием наследования сразу разобраться непросто. Определяя класс, который расширяет другой класс, мы гарантируем, что экземпляр его объекта определяется характеристиками сначала дочернего, а затем — родительского класса. Чтобы понять это, нужно размышлять с точки зрения поиска. При вызове $product2->getProducer() интерпретатор PHP не может найти такой метод в классе CDProduct. Поиск заканчивается неудачей, и поэтому используется стандартная реализация этого метода, заданная в классе ShopProduct. С другой стороны, когда мы вызываем $product2->getSummaryLine(), то интерпретатор PHP находит реализацию метода getSummaryLine() в классе CDProduct и вызывает его.
То же самое верно и в отношении доступа к свойствам. При обращении к свойству $title в методе getSummaryLine() из класса BookProduct, интерпретатор PHP не находит определение этого свойства в классе BookProduct. Поэтому он использует определение данного свойства, заданное в родительском классе ShopProduct. Поскольку свойство $title используется в обоих подклассах, следовательно, оно должно определяться в суперклассе.
Даже поверхностного взгляда на конструктор ShopProduct достаточно, чтобы понять, что в базовом классе по-прежнему выполняется доступ к тем данным, которыми должен оперировать дочерний класс. Так, конструктору класса BookProduct должен передаваться аргумент $numPages, значение которого заносится в одноименное свойство, а конструктор класса CDProduct должен обрабатывать аргумент и свойство $playLength. Чтобы добиться этого, мы определим методы конструктора в каждом дочернем классе.

Конструкторы и наследование
При определении конструктора в дочернем классе вы берете на себя ответственность за передачу требуемых аргументов родительскому классу. Если же вы этого не сделаете, то у вас получится частично сконструированный объект. Чтобы вызвать нужный метод из родительского класса, вам понадобится обратиться к самому этому классу через дескриптор. Для этой цели в PHP предусмотрено ключевое слово parent.
Чтобы обратиться к методу в контексте класса, а не объекта, следует использовать символы «::», а не «->». Поэтому конструкция parent::__construct() означает следующее: «Вызвать метод __construct() родительского класса». Давайте изменим наш пример так, чтобы каждый класс оперировал только теми данными, которые имеют к нему отношение:
class ShopProduct < public $title; public $producerMainName; public $producerFirstName; public $price; // Здесь мы удалили два избыточных свойства function __construct($title, $firstName, $mainName, $price) < $this->title = $title; $this->producerMainName = $mainName; $this->producerFirstName = $firstName; $this->price = $price; > function getProducer() < return "producerFirstName> producerMainName>"; > function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; return $base; > > class CDProduct extends ShopProduct < public $playLength; // Конструктор класса CDProduct function __construct ($title, $firstName, $mainName, $price, $playLength) < // Вызов базового конструктора parent::__construct($title, $firstName, $mainName, $price); $this->playLength = $playLength; > function getPlayLength() < return $this->playLength; > function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; $base .= ": Продолжительность - playLength>"; return $base; > > class BookProduct extends ShopProduct < public $numPages; // Конструктор класса BookProduct function __construct ($title, $firstName, $mainName, $price, $numPages) < // Вызов базового конструктора parent::__construct($title, $firstName, $mainName, $price); $this->numPages = $numPages; > function getNumberOfPages() < return $this->numPages; > function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; $base .= ": numPages> стр."; return $base; > >
Каждый дочерний класс вызывает конструктор своего родительского класса, прежде чем определять собственные свойства. Базовый класс теперь «знает» только о собственных данных. Дочерние классы — это обычно «специализации» родительских классов. Как правило, следует избегать того, чтобы давать родительским классам какие-либо особые «знания» о дочерних классах. В случае изменения иерархии классов это часто приводило к проблемам. Множество ошибок возникало из-за того, что программисты, после непосредственного изменения «родителя» класса, забывали обновить сам конструктор. А при использовании унифицированного конструктора вызов родительского конструктора parent::__construct() означает обращение непосредственно к родительскому классу, независимо от того, какие изменения произошли в иерархии классов. Но, конечно, нужно позаботиться о том, чтобы этому родительскому классу были переданы правильные аргументы!
Вызов переопределенного метода
Ключевое слово parent можно использовать в любом методе, который переопределяет свой эквивалент в родительском классе. Когда мы переопределяем метод, то, возможно, хотим не удалить функции «родителя», а, скорее, расширить их. Достичь этого можно, вызвав метод родительского класса в контексте текущего объекта. Если вы снова посмотрите на реализации метода getSummaryLine(), то увидите, что значительная часть кода в них дублируется. И лучше этим воспользоваться, чем воспроизводить функциональность, уже разработанную в классе ShopProduct:
class ShopProduct < . function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; return $base; > > class CDProduct extends ShopProduct < . function getSummaryLine() < $base = parent::getSummaryLine().": Продолжительность - playLength>"; return $base; > > class BookProduct extends ShopProduct < . function getSummaryLine() < $base = parent::getSummaryLine().": numPages> стр."; return $base; > >
Мы определили основные функции для метода getSummaryLine() в базовом классе ShopProduct. Вместо того чтобы воспроизводить их в подклассах CDProduct и BookProduct, мы просто вызовем родительский метод, прежде чем добавлять дополнительные данные к итоговой строке.
Теперь, когда мы познакомились с основами наследования, можно, наконец, рассмотреть вопрос видимости свойств и методов в свете полной картины происходящего.

Модификаторы Public, Private и Protected: управление доступом к классам
До сих пор мы явно или неявно объявляли все свойства как public (общедоступные). Такой тип доступа задан по умолчанию для всех методов, а также свойств, объявленных с использованием устаревшего ключевого слова var. Элементы класса можно объявить как public (общедоступные), private (закрытые) или protected (защищенные). Ниже описана разница между ними:
- К общедоступным свойствам и методам можно получать доступ из любого контекста.
- К закрытому свойству и методу можно получить доступ только из того класса, в котором они объявлены. Даже подклассы данного класса не имеют доступа к таким свойствам и методам.
- К защищенным свойствам и методам можно получить доступ либо из содержащего их класса, либо из его подкласса. Никакому внешнему коду такой доступ не предоставляется.
Чем это может быть нам полезно? Ключевые слова, определяющие область видимости, позволяют показывать только те аспекты класса, которые требуются клиенту. Это позволяет создать ясный и понятный интерфейс для объекта.
Контроль доступа, позволяющий запрещать клиенту доступ к некоторым свойствам, поможет также избежать ошибок в коде. Предположим, мы хотим сделать так, чтобы в объектах типа ShopProduct поддерживались скидки. Для этого можно добавить свойство $discount и метод setDiscount():
class ShopProduct < . public $discount = 0; . function setDiscount($num) < $this->discount = $num; > >
Но тут у нас есть проблема. Мы хотим показать всем только скорректированную цену, но клиент может легко обойти метод getPrice() (если определить такой метод) и получить доступ к свойству $price:
echo "Цена - price>";
В результате будет выведена исходная цена, а не цена со скидкой, которую мы хотим представить. Чтобы предотвратить это, можно просто закрыть свойство $price. Это позволит запретить клиентам прямой доступ к нему, заставляя использовать метод getPrice(). Любая попытка получить доступ к свойству $price из-за пределов класса ShopProduct закончится неудачей. В результате для внешнего мира это свойство прекратит существование.
Но определение свойств как private — не всегда удачная стратегия, поскольку тогда дочерний класс не сможет получить доступ к закрытым свойствам. А теперь представьте, что правила вашего бизнеса таковы: при покупке только книг скидку на них делать нельзя. Мы можем переопределить метод getPrice(), чтобы он возвращал свойство $price без применения скидки:
// Класс BookProduct function getPrice () < return $this->price; >
Поскольку свойство $price объявлено в классе ShopProduct с модификатором private, а не в BookProduct, попытка в приведенном выше коде получить к нему доступ закончится неудачей. Чтобы решить эту проблему, нужно объявить свойство $price защищенным (protected) и тем самым предоставить доступ к нему дочерним классам. Помните, что к защищенному свойству или методу нельзя получить доступ из-за пределов иерархии того класса, в котором это свойство или метод были объявлены. Доступ к ним можно получить только из исходного класса или его дочерних классов.
Как правило, появление ошибок при доступе к свойствам или методам способствует созданию хорошо защищенного кода. Сначала сделайте свойства закрытыми или защищенными, а затем ослабляйте ограничения по мере необходимости. Многие (если не все) методы в ваших классах будут общедоступными, но, повторяю еще раз, если у вас есть сомнения, ограничьте доступ. Метод, предоставляющий локальные функции другим методам в классе, не нужен пользователям класса. Поэтому сделайте его закрытым или защищенным.
Методы как средство доступа к свойствам
Даже если в клиентской программе нужно будет работать со значениями, хранящимися в экземпляре вашего класса, как правило, стоит запретить прямой доступ к свойствам этого объекта. Вместо этого создайте методы, которые возвращают или устанавливают нужные значения. Такие методы называют методами доступа (accessors) или получателями (getter) и установщиками (setter).
Вы уже видели одно преимущество, которое дают методы доступа: их можно использовать для фильтрации значений свойств в зависимости от обстоятельств, как было проиллюстрировано выше с помощью метода getPrice(). Метод-установщик может также использоваться для принудительного определения типа свойства. Мы уже видели, что для ограничения типа аргументов метода можно использовать уточнения, но у нас нет непосредственного контроля над типами свойств.
В предыдущей статье мы приводили пример класса ShopProductWriter, с помощью которого выводилась информация об объектах типа ShopProduct. Давайте попробуем пойти дальше и сделать так, чтобы класс ShopProductWriter мог выводить информацию о любом количестве объектов типа ShopProduct одновременно:
class ShopProductWriter < public $products = array(); public function addProduct(ShopProduct $shopProduct) < $this->products[] = $shopProduct; > public function write() < $str = ""; foreach ($this->products as $product) < $str .= "title>: getProducer()>; getPrice()>$
"; > echo $str; > > $product1 = new BookProduct('Собачье сердце', 'Михаил', 'Булгаков', 5.99, 380); $product2 = new CDProduct('Первый снег', 'Группа', 'Моральный кодекс', 2.99, 55.01); $write = new ShopProductWriter(); $write->addProduct($product1); $write->addProduct($product2); $write->write();

Теперь класс ShopProductWriter стал намного полезнее. Он может содержать много объектов типа ShopProduct и сразу выводить информацию обо всех их. Но мы все еще должны полагаться на то, что программисты клиентского кода будут строго придерживаться правил работы с классом. Хотя мы предоставили метод addProduct(), мы не запретили программистам непосредственно выполнять операции над свойством $products. В результате можно не только добавить объект неправильного типа к массиву свойств $products, но и затереть весь массив и заменить его значением элементарного типа. Чтобы не допустить этого, нужно сделать свойство $products закрытым:
class ShopProductWriter
Теперь внешний код не сможет повредить массив свойств $products. Весь доступ к нему должен осуществляться через метод addProduct(), а уточнения типа класса, которые используются в объявлении этого метода, гарантируют, что к массиву свойств могут быть добавлены только объекты типа ShopProduct.
И в заключение давайте изменим класс ShopProduct и его дочерние классы так, чтобы ограничить доступ к свойствам:
class ShopProduct < private $title; private $producerMainName; private $producerFirstName; private $discount; protected $price; function __construct($title, $firstName, $mainName, $price) < $this->title = $title; $this->producerMainName = $mainName; $this->producerFirstName = $firstName; $this->price = $price; > public function getPrice () < return $this->price; > public function getProducerMainName() < return $this->producerMainName; > public function getProducerFirstName() < return $this->producerFirstName; > public function setDiscount( $num ) < $this->discount=$num; > public function getDiscount() < return $this->discount; > public function getTitle() < return $this->title; > public function getProducer() < return "producerFirstName> producerMainName>"; > function getSummaryLine() < $base = "title> (producerMainName>, producerFirstName>)"; return $base; > > class CDProduct extends ShopProduct < private $playLength = 0; function __construct ($title, $firstName, $mainName, $price, $playLength) < parent::__construct($title, $firstName, $mainName, $price); $this->playLength = $playLength; > function getPlayLength() < return $this->playLength; > function getSummaryLine() < $base = parent::getSummaryLine().": Продолжительность - playLength>"; return $base; > > class BookProduct extends ShopProduct < private $numPages = 0; function __construct ($title, $firstName, $mainName, $price, $numPages) < parent::__construct($title, $firstName, $mainName, $price); $this->numPages = $numPages; > function getNumberOfPages() < return $this->numPages; > function getSummaryLine() < $base = parent::getSummaryLine().": numPages> стр."; return $base; > > class ShopProductWriter < private $products = array(); public function addProduct(ShopProduct $shopProduct) < $this->products[] = $shopProduct; > public function write() < $str = ""; foreach ($this->products as $product) < $str .= "getTitle()>: getProducer()>; getPrice()>$
"; > echo $str; > >
В этой версии семейства классов ShopProduct нет ничего существенно нового. Все методы были явно сделаны общедоступными, а все свойства теперь стали либо закрытыми, либо защищенными. И для завершенности мы добавили ряд методов доступа.
PHP — вопросы и ответы (Часть 2)
Возможности ключегого свойства self не ограничиваются вызовом статических членов класса, в контексте нестатических свойств self также обеспечивает возможность обхода таблиц виртуальных методов для текущего объекта. Например, parent::methodName() вызывает метод родительского класса, а self::methodName() вызывает метод, который реализован в текщем классе.
Сколько типов данных в РНР?
- PHP поддерживает восемь простых типов данных:
- Четыре скалярных: bool, integer, float, string
- Два смешанных типа: array, object
- Два специальных типа: resource, NULL
- Существет также несколько псевдотипов: mixed (mixed говорит о том, что параметр может принимать множество (но не обязательно все) типов), number (number говорит о том, что параметр может быть либо integer, либо float.) и callback (Некоторые функции, такие как call_user_func() или usort() принимают в качестве параметра определенные пользователем callback-функции. Callback-функции могут быть не только простыми функциями, но также методами объектов, включая статические методы классов.)
Что можешь сказать про типизацию данных в PHP? — и как реализовать статическую типизацию
- Типизация данных в PHP не строгая, то есть тип переменной определяется интерпретатором автоматически.
- Делаем PHP строго типизированным
Что такое static функция и чем она отличается от “обычной” (не static)?
Статические методы удобно использовать там, где для его работы не требуется создание экземпляра класса.
Статическое свойство — общее для всех экземпляров класса (точнее ни одному из них не принадлежит). Полезно, если вы создаете объекты в разных местах программы, но отовсюду вам нужен доступ к общей переменной. Частный случай — реализация синглетона, когда вам нужен один и тот же экземпляр в разных местах программы (статик-свойство — сам объект):
Что такое конструктор?
Это специальная функция вызываемая при создании объекта.
Поддерживает ли РНР множественное наследование?
Нет. Класс в PHP может иметь только одного родителя
Какие магические методы знаешь? Что это вообще такое?
В дословном переводе — Магические Методы (Magic Methods). Эти методы зарезирвированы в php и все начинаются с двойного подчеркивания (__), вообще свои методы не рекомендуется называть используя в начале __. Список всех волшебных методов:
- __construct — конструктор класса, вызывается при создании объекта
- __destruct — деструктор класса, вызывается при уничтожении объекта
- __call — В контексте объекта при вызове недоступных методов вызывается этот метод
- __callStatic — В статическом контексте при вызове недоступных методов вызывается этот метод
- __get — будет выполнен при чтении данных из недоступных свойств.
- __set — будет выполнен при записи данных в недоступные свойства.
- __isset — будет выполнен при использовании isset() или empty() на недоступных свойствах.
- __unset — будет выполнен при вызове unset() на недоступном свойстве.
- __sleep — Функция serialize() проверяет, присутствует ли в вашем классе метод с «магическим» именем __sleep(). Если это так, то этот метод выполняется прежде любой операции сериализации. Он может очистить объект и предполагается, что будет возвращен массив с именами всех переменных объекта, который должен быть сериализован. Если метод ничего не возвращает кроме NULL, то это значит, что объект сериализован и выдается предупреждение E_NOTICE. Рекомендованное использование __sleep() состоит в завершении работы над данными, ждущими обработки или других подобных задач очистки. Кроме того, этот метод можно выполнять в тех случаях, когда нет необходимости сохранять полностью очень большие объекты.
- __wakeup — функция unserialize() проверяет наличие метода с «магическим» именем __wakeup(). Если такой имеется, то он может воссоздать все ресурсы объекта, принадлежавшие ему. Обычно __wakeup() используется для восстановления любых соединений с базой данных, которые могли быть потеряны во время операции сериализации и выполнения других операций повторной инициализации.
- __toString — позволяет классу решать самостоятельно, как он должен реагировать при преобразовании в строку. Например, что напечатает echo $obj;. Этот метод должен возвращать строку, иначе выдастся неисправимая ошибка E_RECOVERABLE_ERROR.
- __set_state — Этот статический метод вызывается для тех классов, которые экспортируются функцией var_export() начиная с PHP 5.1.0. Параметр этого метода должен содержать массив, состоящий из экспортируемых свойств в виде array(‘property’ => value, …).
- __clone — По завершении клонирования, если у класса был определен метод __clone(), то этот метод __clone() вызывается у свежесозданной копии объекта, для возможного изменения всех необходимых свойств.
- __invoke — вызывается, когда скрипт пытается выполнить объект как функцию.
- Шпаргалка по MV-паттернам для проектирования веб-приложений
- Реализация MVC паттерна на примере создания сайта-визитки на PHP
- Создание простой MVC-системы на PHP 5
- Паттерны проектирования (шаблон, pattern) — это эффективные способы решения характерных задач проектирования, в частности проектирования компьютерных программ. Паттерн не является законченным образцом проекта, который может быть прямо преобразован в код, скорее это описание или образец для того, как решить задачу, таким образом, чтобы это можно было использовать в различных ситуациях.
- Обзор паттернов проектирования
- Абстра́кция — в объектно-ориентированном программировании это придание объекту характеристик, которые отличают его от всех других объектов, четко определяя его концептуальные границы. Основная идея состоит в том, чтобы отделить способ использования составных объектов данных от деталей их реализации в виде более простых объектов, подобно тому, как функциональная абстракция разделяет способ использования функции и деталей её реализации в терминах более примитивных функций, таким образом, данные обрабатываются функцией высокого уровня с помощью вызова функций низкого уровня. Такой подход является основой объектно-ориентированного программирования. Это позволяет работать с объектами, не вдаваясь в особенности их реализации. В каждом конкретном случае применяется тот или иной подход: инкапсуляция, полиморфизм или наследование. Например, при необходимости обратиться к скрытым данным объекта, следует воспользоваться инкапсуляцией, создав, так называемую, функцию доступа или свойство. Абстракция данных — популярная и в общем неверно определяемая техника программирования. Фундаментальная идея состоит в разделении несущественных деталей реализации подпрограммы и характеристик существенных для корректного ее использования. Такое разделение может быть выражено через специальный «интерфейс», сосредотачивающий описание всех возможных применений программы. С точки зрения теории множеств, процесс представляет собой организацию для группы подмножеств своего множества. См. также Закон обратного отношения между содержанием и объемом понятия.
- Инкапсуля́ция — свойство языка программирования, позволяющее пользователю не задумываться о сложности реализации используемого программного компонента (что у него внутри?), а взаимодействовать с ним посредством предоставляемого интерфейса (публичных методов и членов), а также объединить и защитить жизненно важные для компонента данные. При этом пользователю предоставляется только спецификация (интерфейс) объекта. Пользователь может взаимодействовать с объектом только через этот интерфейс. Реализуется с помощью ключевого слова: public. Пользователь не может использовать закрытые данные и методы. Реализуется с помощью ключевых слов: private, protected, internal. Инкапсуляция — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, полиморфизмом и наследованием). Сокрытие реализации целесообразно применять в следующих случаях: предельная локализация изменений при необходимости таких изменений, прогнозируемость изменений (какие изменения в коде надо сделать для заданного изменения функциональности) и прогнозируемость последствий изменений.
- Насле́дование — один из четырёх[источник?] важнейших механизмов объектно-ориентированного программирования (наряду с инкапсуляцией, полиморфизмом и абстракцией), позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса заимствуются новым классом. Другими словами, класс-наследник реализует спецификацию уже существующего класса (базовый класс). Это позволяет обращаться с объектами класса-наследника точно так же, как с объектами базового класса. Простое наследование Класс, от которого произошло наследование, называется базовым или родительским (англ. base class). Классы, которые произошли от базового, называются потомками, наследниками или производными классами (англ. derived class). В некоторых языках используются абстрактные классы. Абстрактный класс — это класс, содержащий хотя бы один абстрактный метод, он описан в программе, имеет поля, методы и не может использоваться для непосредственного создания объекта. То есть от абстрактного класса можно только наследовать. Объекты создаются только на основе производных классов, наследованных от абстрактного. Например, абстрактным классом может быть базовый класс «сотрудник вуза», от которого наследуются классы «аспирант», «профессор» и т. д. Так как производные классы имеют общие поля и функции (например, поле «год рождения»), то эти члены класса могут быть описаны в базовом классе. В программе создаются объекты на основе классов «аспирант», «профессор», но нет смысла создавать объект на основе класса «сотрудник вуза».
- Полиморфи́зм — возможность объектов с одинаковой спецификацией иметь различную реализацию. Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию — например, реализация класса может быть изменена в процессе наследования[1]. Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс, множество реализаций». Полиморфизм — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, инкапсуляцией и наследованием). Полиморфизм позволяет писать более абстрактные программы и повысить коэффициент повторного использования кода. Общие свойства объектов объединяются в систему, которую могут называть по-разному — интерфейс, класс. Общность имеет внешнее и внутреннее выражение: внешняя общность проявляется как одинаковый набор методов с одинаковыми именами и сигнатурами (именем методов и типами аргументов и их количеством); внутренняя общность — одинаковая функциональность методов. Её можно описать интуитивно или выразить в виде строгих законов, правил, которым должны подчиняться методы. Возможность приписывать разную функциональность одному методу (функции, операции) называется перегрузкой метода (перегрузкой функций, перегрузкой операций).
Что такое MVC?
Что такое шаблоны (паттерны) проектирования?
Расскажи основные принципы ООП.
Чем отличается класс от объекта?
Класс определяет структуру и поведение. Объект определяет состояние (наполнение структуры) и меняет его в зависимости от поведения, заданным классом.
Может быть вам легче будет понять, если вы будете думать о классе как о наборе функций, а об объекте как о наборе данных.
Некоторые функции в классе могут быть использованы только с набором данных (т.е. объектом), который передается неявно, когда вы указываете $object->method().
Другие функции не требуют объекта, поэтому могут быть вызваны напрямую из класса.
С этой точки зрения очень логично выглядит Python, где у любого метода есть явный аргумент self, указывающий на объект, из которого был вызван метод. А запись object.method(5) является краткой записью ObjectClass.method(object, 5).
Класс — это бог, который есть всегда. И бог по образу и подобию своему создаёт экземпляры — человеков. Каждый человек наделён своим набором свойств (рост, вес, …) и методов (плавать, ходить, летать). А бог в свою очередь наделён общими для всех методами: принимать мольбы, карать, посылать манну небесную и сотворить чудо. Эти методы могут быть приватными. Т.е. бог например может принимать мольбы только людей, а не марсиан. А вот сотворить чудо может как для людей, так и для марсиан — ему не жалко.
Что такое абстрактный класс? Можно ли создать экземпляр абстрактного класса?
Абстрактный класс в php и абстрактный класс в любом другом языке программирования, являет собой базовый класс, не предназначенный для создания его экземпляров. Основной смысл и назначение абстрактных классов заключается в расширении возможностей его дочерних классов. Чтобы сделать класс абстрактным, нужно перед объявлением класса поставить идентификатор: abstract. дентификатор abstract, перед объявлением функции говорит о том, что данная функция должна быть обязательно определена в классе потомке.
Какая разница между абстрактным классом и интерфейсом?
Представьте себе пальчиковую батарейку. Вы ее держите в руках и точно знаете, что это пальчиковая батарейка. Если вас попросить описать те критерии, которые позволяют вам утверждать, что это батарейка (к примеру, размером с палец, если засунуть в фонарик — он засветится, вольтметр покажет 1.5 вольта, с одной стороны пимпочка ас другой плоско), то этот список и будет описанием интерфейса «пальчиковая батарейка».
Интерфейс ничего не говорит про, то как батарейка работает и позволяет манипулировать ею просто не задумываясь как она это делает. Фонарику все равно как она устроена, ему главное получить обещанные 1,5 вольта с полюса с пимпочкой и плоского полюса. Обещание — это и есть интерфейс — договор между двумя объектами. И еще, вы просто не узнаете по внешнему виду что у вас в руках — батарейка 777, алкалайн, дюраселл, NiMH аккумулятор или портативный атомный реактор. Придется заглянуть под интерфейс и посмотреть внутреннюю реализацию.
Абстрактный класс это недонаследник — наследник, который программисту предстоит еще доделать. Это уже почти сформированный класс, который вот-вот и можно будет инстансировать объекты. Он используется, если разработчик хочет направить другого разработчика в нужное русло, обеспечить его тем поведением, которое ему пригодится при завершении класса. Это как придумать Фрукт, ожидая, что в будущем кто-то додумает Яблоко, Лимон, Грушу…
Из этого всего можно сделать самый короткий вывод 5 уровня:
— интерфейс говорит о поведении класса и заставляет отвечать его на вопрос «что я делаю?», а абстрактный класс определяет класс-наследник и заставляет его отвечать на вопрос «кто я?».
Абстрактный(Abstract) класс — класс, который имеет хотя б 1 абстрактный (не определенный) метод; обозначается как abstract.
Интерфейс — такой же абстрактный класс,только в нем не может быть свойств и не определены тела у методов.
Так же стоит заметить, что абстрактный класс наследуется(etxends), а интерфейс реализуется (implements). Вот и возникает разница между ними, что наследовать мы можем только 1 класс, а реализовать сколько угодно.
ВАЖНО! При реализации интерфейса, необходимо реализовать все его методы, иначе будет Fatal error, так же это можно избежать, присвоив слово abstract.