Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

Объектно-ориентированное программирование (ООП) в PHP Интерфейсы, классы, абстрактные классы, объекты

Лекция



Привет, сегодня поговорим про объектно-ориентированное программирование, обещаю рассказать все что знаю. Для того чтобы лучше понимать что такое объектно-ориентированное программирование, ооп в php, интерфейсы, классы, абстрактные классы, объекты , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend) .

Как и большинство современных языков программирования, язык PHP является объектно-ориентированным. Что же это означает? Объектно ориентированное программирование, или ООП - это подход, при котором основными элементами программы являются классы , интерфейсы и объекты .

ООП зиждется на трех принципах, которые зовутся Инкапсуляция, Наследование и Полиморфизм. Эти понятия будут расшифрованы ниже. А пока - расскажу о базовых терминах:

Классы, интерфейсы, объекты

Класс - это описание некоторой сущности на языке программирования. Классы заключают в себе описание нижеследующего:

  • Членов данных, или полей. По сути, поле данных - это переменная, объявленная внутри класса. В членах данных хранится состояние объектов класса.
  • Поведения. Поведение класса описывается функциями-членами класса, которые в ООП называются "методами".
  • Прав доступа к полям данных и методам.
  • Наследование. Каждый класс может быть потомком другого класса, и в свою очередь быть предком следующего класса. Наследник, простите, наследует от своего родителя методы и члены данных, то есть поведение и информация о состоянии.

Объект - это экземпляр класса. Экземпляров одного и того же класса можно насоздавать столько, сколько нужно. Теперь проще понять, почему "класс" так называется: класс описывает целый класс объектов, которые могут обладать одинаковым поведением и свойствами, присущими данному классу.

Интерфейс - это описание возможностей, предоставляемых классом, реализующим данный интерфейс. Интерфейс не описывает, в отличие от класса, конкретное поведение, но содержит описание методов без их тела. Интерфейс возлагает обязанность по реализации этих методов классу, заявившему, что он реализует данный интерфейс.

Объявление интерфейса, реализация интерфейса и пример использования - все это демонстрирует фрагмент кода ниже. Можете скопировать его в PHP-файл и поиграть с ним. Эта программка будет выводить числа 1, 2, 3 и так далее, при нажатии в браузере на кнопку F5.

// Интерфейс DataStorage.
interface DataStorage {
  // setData - сохраняет значение $value под именем $name.
  public function setData($name, $value);

  // getData - возвращает значение, предварительно сохраненное
  // под именем $name
  public function getData($name);
}

// Класс FileDataStorage - Реализация интерфейса DataStorage,
// работающая с файлами.
class FileDataStorage implements DataStorage {
  private $directory;

  // Это - конструктор, то есть метод, который вызывается PHP
  // при создании экземпляра класса оператором new.
  // В конструкторе происходит инициализация объекта.
  public function __construct($directory) {
    $this->directory = $directory;
  }

  private function getFileName($dataName) {
    return $this->directory . "/" . $dataName . ".txt";
  }

  public function getData($name) {
    $fname = $this->getFileName($name);
    return is_file($fname)? file_get_contents($fname): null;
  }

  public function setData($name, $value) {
    if(!is_dir($this->directory)) {
      mkdir($this->directory, 0777, true);
    }
    return file_put_contents($this->getFileName($name), $value);
  }
}

// Функция getNextNumber() - обращается к элементу данных 'value',
// увеличивает его на единицу и возвращает полученное значение.
// Эта функция может работать с любым DataStorage.
function getNextNumber(DataStorage $storage) {
  $value = $storage->getData('value');
  $value ++;
  $storage->setData('value', $value);
  return $value;
}

// Создаем экземпляр класса FileDataStorage
// и передаем его в функцию.
$storage = new FileDataStorage('data');
echo getNextNumber($storage);

замечание 

Объектно-ориентированное программирование (ООП) в PHP Интерфейсы, классы, абстрактные классы, объекты

interface someInterface{ public function someMethod(); } 

interface anotherInterface{ public function someMethod(); } 

class Foo implements someInterface,anotherInterface 
{
 public function someMethod(){ echo 'someMethod() was called'.PHP_EOL; }
 }

 $foo = new Foo();
 $foo->someMethod();

Итак что выведет на экран скрипт в результате своей работы.
 . 
Правильный ответ: «Fatal error: Can't inherit abstract function …»
 в PHP класс не может имплементировать несколько интерфейсов содержащих одинаковые методы, так как это порождает неоднозначность. 

Интерфейсы используются для предварительного наброска класса. вот пример, однако интерфейс это лишь первый черновой набросок решения посттавленой задачи

Представьте вопрос заказчика: сколько времени понадобится, чтобы реализовать новую деталь, связанную с вкладами?

Посмотрите на код:

interface Bank {
	boolean depositFunds(Funds funds);
}


Все, что вам нужно сделать, это реализовать интерфейс. Сколько времени вам понадобится? Подсказка: здесь нет правильного или неправильного ответа и вам, вероятно, нужно сделать некоторые предположения.

Чем отличаются абстрактный класс и интерфейс? Этот вопрос часто задают на собеседованиях при приеме на работу на должность программиста. Причем задают его не только "джуниорам" но и вполне себе "сеньорам", причем последние часто "плывут" на этом вопросе, пытаясь припомнить тонкости реализации ООП в их любимом языке.

Для них, а также для себя (чтобы не забыть) опишу эти отличия детально. Все перечисленное касается PHP, не ручаюсь за особенности реализации в других языках.

Итак:

  • В грубом приближении, интерфейс можно считать крайней степенью абстрактного класса.
  • Интерфейсы, в отличии от абстрактных классов, поддерживают множественное наследование. Т.е. класс-потомок может реализовывать 2 или более интерфейсов одновременно: class A implements Int1, Int2, но class A extends AbstrB
  • Интерфейс содержит исключительно объявления методов, но не содержит реализации. Абстрактный класс же может содержать как абстрактные методы (без реализации), так и обыкновенные.
  • Интерфейс не может включать в себя свойства, в абстрактном классе, как и в любом другом, свойства могут присутствовать.
  • Класс-потомок обязан реализовывать все методы интерфейса, методы абстрактного класса же могут в нем не присутствовать.
  • В интерфейсе все методы открытые, в абстактном классе они могут содержать спецификаторы доступа (private, protected, pubic).

Как мы видим, абстрактный класс и интерфейс имеют весьма существенные отличия. Если я что-то не упомянул - дополните меня в комментариях.

Также, хотелось бы узнать ваше мнение - когда имеет смысл применять абстрактный класс вместо интерфейса?

интерфейс часто используется при проектированиии . напрмер сеньер задает какие метода вообще одязатень для чего то

а джунион его реализует

.так же интефейсы нужны для сопряжения разлиных классов, наобля в них чтото общее или требуя общее и не менящееся при изменении одного класса на другой в системе

В приведенном примере в объявлении класса встречаются служебные слова public и private. Они задают уровень доступа к методам и членам данных класса. Возможны несколько уровней "допуска" к членам класса:

  • public - к такому члену класса может обращаться любой программный код.
  • protected - к такому члену можно обращаться только из метода класса, где объявлен этот член, а также из классов-потомков, которые унаследовали этот член от родителя.
  • private - доступен только для методов класса, где объявлен этот член. В классах-потомках private методы и поля данных будут недоступны.
  • по умолчанию, если не указан ни один из спецификаторов доступа, используется public.

Что же дает это разделение по уровням доступа? Благодаря возможности "спрятать" внутренние члены данных от внешнего кода, достигается лучшая изоляция деталей реализации от пользователей класса. При грамотно разработанном интерфейсе класса, методы, через которые класс взаимодействует с внешним миром, будут доступны извне, а внутренние и вспомогательные методы и поля будут недоступны. Это повышает наглядность интерфейса класса, а часто и предотвращает грубые ошибки.

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


PHP 5.4 дал нам новое API для замыканий и метод Closure#bind().
Closure#bind() в принципе позволяет получить экземпляр замыкания с областью видимости данного класса, или объекта. Изящно! Вот так можно добавить API к существующим объектам!
Давайте же нарушим инкапсуляцию объектов в соответствии с нашими потребностями.
Методы доступа к приватным свойствам уже объяснялись в документации, но я все равно приведу пример.
Украдем приватное свойство Kitchen#yummy:


Определим замыкание для получения этого поля:

yummy;
}


А теперь украдем yummy из экземпляра Kitchen:


К сожалению, мы получим фатальную ошибку в $sweetsThief:

Fatal error: Cannot access private property Kitchen::$yummy in [...] on line [...]


Сделаем нашего вора умнее Closure#bind():


Удача!

Closure::bind vs Reflection: быстродействие


Я сделал простой бенчмарк для 100000 итераций инициализации:

yummy;
    }, null, 'Kitchen');
}

setAccessible(true);
}


На только что скомпилированном PHP 5.5 (Ubuntu 13.04 amd64 box), первый тест занял 0.325 секунд, а второй 0.658.

Рефлексия здесь гораздо медленнее.(На 49%)

Но это совсем не интересно, так как никому не потребуется инициализировать 100000 раз одно и то же, по крайней мере мне точно. Что на самом деле интересно — так это доступ к приватным свойствам. Я протестировал и это тоже:

yummy;
}, null, 'Kitchen');

for ($i = 0; $i < 100000; $i += 1) {
    $sweetsThief($kitchen);
}

setAccessible(true);

for ($i = 0; $i < 100000; $i += 1) {
    $sweetsThief->getValue($kitchen);
}


Первый тест занял ~ 0.110 секунд, а второй ~ 0.199!
Это гораздо быстрее рефлексии, впечатляет!(На 55%)

Доступ к приватным свойствам по ссылкам


Есть еще одно преимущество, используя замыкания вместо рефлексии мы можем работать с свойствами объекта по ссылкам!

yummy;
}, null, $kitchen);


$cake = & $sweetsThief($kitchen);
$cake = 'lie';

var_dump('the cake is a ' . $sweetsThief($kitchen));

Универсальный метод доступа


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

$property;
    }, $object, $object)->__invoke();

    return $value;
};

$kitchen = new Kitchen();
$cake    = & $reader($kitchen, 'cake');
$cake    = 'sorry, I ate it!';

var_dump($kitchen);


Рабочий пример. https://3v4l.org/nCMor
У нас есть доступ к любому свойству, в любом месте, и даже по ссылке. Ура! Мы нарушили правила еще раз!

Благодаря возможности ограничения доступа к членам класса, класс можно считать "черным ящиком", предоставляющим простой интерфейс для связи с внешним миром, но имеющий, возможно, сложную реализацию внутри, но об этой реализации пользователям класса знать не надо. Этот принцип называется Инкапсуляцией.

В примере выше, FileDataStorage предоставляет пользователям два внешне простых метода. Внутри этих методов происходят действия, о которых клиент (в нашем примере это функция getNextNumber) даже не догадывается. Для функции-потребителя "услуги" под названием DataStorage, детали реализации нашего FileDataStorage, который на самом деле передается в функцию, не имеют никакого значения. Функция работает с объектом $storage как с "черным ящиком", вызывая его методы и передавая в них параметры, как того требует интерфейс DataStorage. В этот момент мы подходим к следующему важному понятию:

Полиморфизм

Полиморфизм, как следует из самого слова - возможность сущностей одного класса принимать различные формы, то есть, обладать различным поведением. В ООП полиморфизм обеспечивается возможностью наследования классов друг от друга, а также возможностью реализации интерфейсов.

Снова обратимся к нашему примеру выше. В этом примере функция getNextNumber получает для своих внутренних нужд экземпляр DataStorage. DataStorage - это интерфейс, то есть, нашей функции подошла бы любая реализация этого интерфейса, ибо в любой реализации DataStorage обязаны быть методы getData и setData. Но какая конкретно реализация DataStorage будет передана, функция getNextNumber не имеет ни малейшего представления, да ей это и не нужно знать. В нашем примере есть только одна реализация DataStorage - FileDataStorage, но никто не мешает наделать и других реализаций, например, умеющих хранить данные в базе данных или на удаленном сервере. Функция getNextNumber будет одинаково работать с любой из этих реализаций.

Это свойство DataStorage, принимать различное поведение в зависимости от конкретной реализации, и называется полиморфизмом.

Для чего все это нужно?

На практике, оформление различной функциональности в виде классов весьма удобно. Класс представляет собой изолированный набор функций-методов и данных, с которыми эти методы работают. Ограничение доступа обеспечивает то, что только лишь методы класса могут работать с этими данными. Все это позволяет создавать всевозможные "кирпичики", то есть, компоненты, реализующие ту или иную функциональность: работу с базой данных, работу с изображениями, обработку ввода пользователя и так далее и т.п.. Если у каждого такого кирпичика есть простой интерфейс и минимум зависимостей от других кирпичиков, то такой компонент будет легко переносим и возможно будет использовать его в более чем одном проекте, экономя силы и время.

Зависимость, связность

В нашем примере (опять!) функция getNextNumber зависит только от интерфейса DataStorage. getNextNumber не зависит ни от какой конкретной реализации DataStorage, что позволяет использовать данную функцию в любой среде, достаточно лишь создать подходящую реализацию DataStorage. Это пример слабой зависимости.

Связность (сцепление, cohesion) - понятие, определяющее насколько тесно связаны между собой элементы одного модуля. Можно считать "модулем" класс, а "элементами" - его методы. Тогда связность определяет, по сути, насколько узкий (специфичный) функционал реализует данный класс.

ООП позволяет обеспечить слабую зависимость (связанность, coupling) компонентов программы друг от друга, при их высокой связности (cohesion). Слабая зависимость компонентов друг от друга при их высокой связности - признак хорошего программного дизайна, ведь это означает, что:

  • Каждый класс выполняет лишь свою определенную обязанность, часть функционала системы. И наоборот, за каждый элемент функционала системы отвечает только один класс. Например, если в системе понадобилось изменить политику безопасности, добавив ограничение на минимальную длину пароля, то изменения должны быть сделаны лишь в одном месте программы, отвечающем за проверку пароля на безопасность.
  • Классы слабо зависят друг от друга: можно заменять один объект другим, и при этом не надо будет вносить изменения в соседние компоненты системы.

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

Эпилог

Вышенаписанное можно считать весьма кратким введением в ООП, причем язык программирования не имеет значения. Достаточно заменить пример в середине страницы на другой язык - и все станет справедливо для другого языка.

Чтобы правильно составлять программы с использованием принципов ООП, надо "почувствовать" этот подход, что может получиться не сразу. Часто оказывается, что объектный дизайн программы - одно из самых сложных мест в разработке больших программных систем. А порой и дизайн этот меняется в ходе разработки! Поэтому классы-"кубики", большие и поменьше - надо видеть "издалека", чтобы оперировать ими, составляя структуру приложения. Об этом говорит сайт https://intellect.icu . В этом сильно помогают различные фреймворки, предлагая уже отчасти готовый дизайн, на который остается навесить возможности, необходимые для конкретного приложения.

Существуют так называемые "паттерны проектирования", это тактические приемы решения различных задач проектирования приложений. Каждый такой прием, или паттерн, имеет общепринятое название, что позволяет разработчикам, общаясь (часто прямо через комментарии в коде), парой слов объяснить, как в программе организовано взаимодействие тех или иных программных компонентов. Впрочем, паттерны проектирования - тема для отдельной статьи, даже нет, для целой книжки (такие книжки и на самом деле есть, и много).

В дальнейшем, по ходу изучения, будем возвращаться к различным аспектам ООП, так что базовые знания дополнятся более детальными, а также опытом использования на практике.

Изменения в PHP5

Новые уровни доступа private и public

В PHP5 добавлены новые модификаторы уровня доступа для переменных классов. Как и во многих других языках программирования, они носят названия private, protected и public.

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

Protected - расширение области private, добавляющее к ней возможность обращаться к переменной из классов-потомков.

Public - расширяющий protected модификатор, задающий наиболее широкую область доступа. К возможности использовать переменную в классах-потомках, добавляется возможность обращаться к переменной непосредственно из другого кода. Строго говоря, public не является новой областью доступа. Ранее в PHP все переменные классов являлись public переменными.

Private переменные используются для алгоритмов, которые используются только в текущем классе и не могут быть переопределенны в классах-потомках. Protected может быть использован, когда организовывается семейство объектов, обладающих сходными алгоритмами и организованных в иерархию. Использование public переменных, обычно, не является хорошей практикой но иногда оправданно. Их можно использовать, если у класса много свойств, которые должны быть доступны всем алгоритмам, использующим этот класс.

Аналогично, private/protected/public мидификаторы применяются к методам класса. Методы, объявленные без модификатора, являются public методами.

Если метод или переменная переопределяются в классе-наследнике, то уровень доступа должен быть таким-же или выше. Например, protected метод в классе-потомке можно сделать public, но нельзя private.

Для примера рассмотрим классы NewClass и NewClass1.

class NewClass {
// new PHP5 modifiers
private $myPrivateVar = 'myPrivateVar';
protected $myProtectedVar = 'myProtectedVar';
public $myPublicVar = 'myPublicVar';
// old PHP declaration
var $myVar = 'myVar';
}

class NewClass1 extends NewClass {
function getProtectedVar() {
return $this->myProtectedVar;
}
}

NewClass содержит несколько переменных с разными областями доступа. NewClass1 используется для тестирования областей видимости, связанных с наследованием.

Создаем объекты классов:

$c = new NewClass();
$c1 = new NewClass1();

Обращаемся к переменным:
print $c->myPrivateVar;
Непосредственное обращение к private переменной приводит к ошибке.
print $c->myProtectedVar;
Непосредственное обращение к protected переменной приводит к ошибке.
print $c->myPublicVar;
Обращение к public переменной возвращает ее значение.
print $c->myVar;
Обращение к переменной, объявленной в старом стиле, равносильно обращению к public переменной.
print $c1->myPrivateVar;
Private переменная не была унаследованна классом NewClass1. Обращение к ней равнозначно обращению к необъявленной переменной.
print $c1->myProtectedVar;
Protected переменная была унаследованна и непосредственное обращение к ней приводит к ошибке. Для проверки, что она была унаследованна вместе с начальным значением, можно вызвать "print $c1->getProtectedVar();".
print $c1->myPublicVar;
Public переменная была унаследованна и обращение к ней возвращает ее значение.

абстрактные классы и методы (abstract)

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

Пример создания и использования абстрактного класса:

abstract class NewClass {
abstract function myMethod();
}

class NewClass1 extends NewClass {
function myMethod() {
return 'myMethod';
}
}

$c = new NewClass1();
print $c->myMethod();

Если метод определяется как abstract, он должен быть переопределен в классе-потомке. При этом параметры переопределенного метода должны совпадать с параметрами абстрактного метода. Модификатор уровня доступа для абстрактных методов не учитывается. Уровень доступа определяется методом, переопределяющим абстрактный.

Интерфейсы (interface)

Интерфейс похож на абстрактный класс, за исключением того, что использование интерфейсов позволяет использовать множественное наследование. Таким образом, класс может реализовывать несколько интерфейсов одновременно, а не расширять только один абстрактный класс.

Пример использования интерфейса:

interface Printable {
public function dump();
}
interface Editable {
public function edit();
}

class NewClass implements Printable, Editable {
function dump() { }
function edit() { }
}

$c = new NewClass();
print (($c instanceof Printable) ? 'true' : 'false');

Типизация пареметров функций на уровне классов

Для параметров функций можно задавать класс, объект которого может быть передан по этому параметру. Во время работы скрипта, конструкция

function myFunction(MyClass $obj) {
}

равнозначна конструкции

function myFunction($obj) {
if (!($obj instanceof MyClass || $obj == null)) {
die('Argument 1 must be an instance of ClassName');
}
}

При этом instanceof распостраняется не только на имя класса, но и на всех его предков и реализуемые интерфейсы.

Например, следующий код выполнится без ошибок:

interface Editable {
function edit();
}

abstract class View {
abstract function createView();
}

class NewClass extends View implements Editable {
function createView() { }
function edit() { }
function createMyView(View $obj) { }
function doEdit(Editable $obj) { }
}

$c = new NewClass();
$c->createMyView($c);
$c->doEdit($c);

Финальные классы и методы (final)

Финальный метод невозможно переопределить в классе-наследнике. Финальный класс невозможно использовать для создания классов-наследников. Это может пригодиться, когда необходимо сохранить алгоритм, инкапсулированный в классе, неизменным. Например, что бы ограничить программиста, использующего библиотеку, от переопределения поведения. Использование финальных классов вместе с типизацией параметров функций создает практически 100% препятствие на пути расширения или подмены функциональности. Естественно, при открытом исходном коде убрать final у класса или метода не является трудной задачей, но, например, final часто используется у классов, определенных в самом PHP или его расширениях (Exception class, DOM extention).

Пример финального класса и финального метода:

final class Security {
function createUser() {
...
}
}

class View {
final static function createView(Security $user) {
...
}
}

Поскольку класс Security является финальным, а параметром функции View::createView может быть только объект финального класса или null, это дает 100% гарантию, что в если в функцию createView будет передан объект, то это будет только объект класса Security, а не подмененный.

Клонирование объектов

В PHP4 для клонирования объекта достаточно было простой операции $clonedObject = $object. Все свойства обекта $object просто копировались в объект $clonedObject. Изменить алгоритм клоирования можно было написав собственный метод для этого. В PHP5 для этого метода ввели специальное имя __clone и упростили доступ к созданному объекту. Для обращения к новому объекту используется $this, для обращения к уже существующему (чей клон делается), соответственно, $that.

Если метода __clone нет, то вызовется стандартный метод, копирующий все свойства объекта.

На примере это выглядит так:

class Node {
private $next;
private $name;

function __clone() {
$this->name = $that->name;
$this->next = null;
}

function setName($name) { $this->name = $name; }
function getName() { return $this->name; }
function setNext(Node $next) { $this->next = $next; }
}

$n1 = new Node();
$n1->setName('Node1');

$n2 = new Node();
$n2->setName('Node2');
$n1->setNext($n2);

$n = $n2->__clone();
print_r($n);

В примере рассматривается класс для создания списка, т.е. цепочки объектов, в которой каждый объект сожержит указатель на следующий. При этом можно получить клон любого объекта в цепочке, и новый объект будет "вынутым" из цепочки (не содержать ссылки на следующий объект).

Пример также демонстрирует, что к можно внутри метода __clone можно получить доступ к private переменным объектов $this и $that.

Конструкторы

Основным недостатком структуры конструкторов в PHP4 является необходимость синхронизации имени конструктора и имени класса. Поскольку имя конструктора должно совпадать с именем класса, то, при изменении имени класса, приходится переименовывать и конструкторы. В случае, если класс имеет несколько наследников, приходится аккуратно изменять в классах наследниках наследуемый класс (extends) и вызов конструктора класса-предка (parent).

Введение в PHP5 конструктора для класса с общим именем __construct упрощает переименовывание классов во время их разработки. Если в классе есть и __construct и функция, имя которой совпадает с именем класса, то в качестве конструктора будет вызванно __construct. При перегрузке метода-конструтора вызов конструктора класса-предка осуществляется через parent::__construct().

Пример использования конструкторов:

class NewClass1 {
function __construct() {
print 'NewClass1::__construct called';
}
}

class NewClass2 extends NewClass1 {
}

class NewClass3 extends newClass2 {
function __construct() {
print 'NewClass3::__construct called';
parent::__construct();
}
}

$n1 = new NewClass1();
// выводится NewClass1::__construct called

$n2 = new NewClass2();
// выводится NewClass1::__construct called - конструктор унаследован и вызван

$n3 = new NewClass3();
// выводится NewClass3::__construct called и NewClass1::__construct called

При этом, если конструктор объявлен с модификатором private, то класс с таким конструктором создать невозможно. Однако обращение parent::__construct возможно. Это дает еще один способ избежать создания класса, помимо объявления его abstract.

Деструкторы

Деструкторы являются нововведением для PHP. Они очень полезны для совершения работы по освобождению ресурсов, таких как закрытие открытых файлов или соединения с базой данных. Для деструкторов определенно имя __destruct. Как и для конструкторов, если деструктор унаследован и не перегружен он вызовется. Если он перегружен, то вызовется только перегруженный конструктор. Для вызова деструктора объекта-предка надо использовать parent::__destruct(). Деструктор вызывается без параметров.

Пример использования деструктора:

class Computer {
function compute() {
// большие ресурсоемкие вычисления.
}

function __destruct() {
// отправить письмо, что все выполнилось
}
}

$c = new Computer();
$c->compute();

Константы

В классах могут быть объявленны константы. Это является еще одним методом (вместе с final классами и методами) для повышения структурности и удобочитаемости кода.

Пример определения и использования констант:

final class ControlTypes {
const Textbox = 1;
const Label = 2;
const Listbox = 3;
const Textarea = 4;
const Link = 7;
const Button = 6;
}

class Control {
private $type;

function __construct($type) {
$this->type = $type;
}
}

$c = new Control(ControlTypes::Textbox);

К константам невозможно применять модификаторы public, protected, private. Константы всегда public. Обращаться к константам можно только через имя класса, например ControlType::Textbox. Обращения через $this или другой указатель на объект класса не поддерживаются. В константе может быть только значение примитивного типа, т.е. строка или число. Константы наследуются и могут быть переопределены в классах-потомках.

Интересной особенностью является то, что интерфейсы могут содержать константы. Например:

interface myInterface {
const test = 2;
}

Система перехвата исключений (exceptions)

Exceptions (исключения) - это неотъемлемая часть любого современного языка. Система перехвата исключений объединяет в себе оператор throw, стрктуру языка "try { .. } catch ()[ catch () ...]" и основной объект Exception. В отличии от Java exceptions, в PHP отсутствует завершающий блок finally.

Основное применение системы исключений состоит в использовании структуры try/catch для отделения основного кода программы и блоков обработки ошибок. Механизм exceptions позволяет также корректно обрабатывать исключения, возникшие не непосредственно в выполняемом коде, а в используемых функциях.

Следующий пример демонстрирует отделение кода от обработчиков нестандартных ситуаций:

/**
* Замечания:
* Конструктор DatabaseConnection может бросить DatabaseException
* Метод getUser() может бросить UserNotFoundException
* Метод sendMail() может бросить MailServiceException
*/
try {
$cn = new DatabaseConnection();
$admin = cn->getUser('Admin');
$admin->sendMail('Database check is complete');
} catch (DatabaseException $e) {
print "Невозможно создать соединение с базой данных. Причина: " . $e->getMessage();
} catch (UserNotFoundException $e) {
print "Пользователя не существует";
} catch (MailServiceException $e) {
print "Ошибка отправки письма: " . $e->getMessage();
} catch (Exception $e) {
print "Общая ошибка: " . $e->getMessage();
}

В общем случае, использование системы исключений можно заменить на использование структур if и goto или только if, но код программы в результате становиться значительно более громоздким.

Система исключений в PHP работает только с исключениями, "бросаемыми" оператором throw. Ошибки синтаксиса языка не обрабатываются блоками try/catch по очевидным причинам.

В PHP на данный момент определен только один класс исключений: Exception. Для более гибкой работы с системой сообщений можно добавлять свои классы исключений но наследовать их от базового класса Exception, что бы всегда можно было поймать исключение (catch exception).

Основными методами класса Exception являются: getMessage(), getCode(), getTrace(), getFile(), getTraceAsString(), _toString(). Все методы являются финальными, кроме конструктора и _toString(). Таким образом, дополнительная функциональность классов-потомков Exception (отправка почты с информацией о ошибке, запись в log) может быть реализована в конструкторе.

Класс Exception объявляется непосредственно в PHP Engine, но его примерная модель может быть представленна таким образом (по материалам www.zend.com):

class Exception {
function __construct(string $message=NULL, int $code=0) {
if (func_num_args()) {
$this->message = $message;
}
$this->code = $code;
$this->file = __FILE__; // of throw clause
$this->line = __LINE__; // of throw clause
$this->trace = debug_backtrace();
$this->string = StringFormat($this);
}

protected $message = 'Unknown exception'; // exception message
protected $code = 0; // user defined exception code
protected $file; // source filename of exception
protected $line; // source line of exception

private $trace; // backtrace of exception
private $string; // internal only!!

final function getMessage() {
return $this->message;
}
final function getCode() {
return $this->code;
}
final function getFile() {
return $this->file;
}
final function getTrace() {
return $this->trace;
}
final function getTraceAsString() {
return self::TraceFormat($this);
}
function _toString() {
return $this->string;
}
static private function StringFormat(Exception $exception) {
// ... a function not available in PHP scripts
// that returns all relevant information as a string
}
static private function TraceFormat(Exception $exception) {
// ... a function not available in PHP scripts
// that returns the backtrace as a string
}
}

Использование объектов без ссылок на них

Очень серьезным неудобством в PHP4 было вызывание цепочки методов. В PHP4 невозможно создать объект без ссылки на него, поскольку объекты фактически являлись только синтаксичекой конструкцией и на уровне ядра были эквивалентны массивам. Это порождало, например, такие конструкции:

$page = &$this->getPage();
$page->registerControl($this);

Конечно, это не очень удобно. Созданная на уровне ядра PHP5, таблица ссылок на объекты делает необязательным существование ссылок на объект. Благодаря этому становится возможной следующая конструкция:

$this->getPage()->registerControl($this);

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

for($i = 0; $i < 100; $i++)
$myObject->getProperty('relatedObject')->getAncestor($i)->update();

Во время работы этого кода осуществляется создание двухсот объектов и трехсот вызов методов. Очень простым

продолжение следует...

Продолжение:


Часть 1 Объектно-ориентированное программирование (ООП) в PHP Интерфейсы, классы, абстрактные классы, объекты
Часть 2 Вау!! 😲 Ты еще не читал? Это зря! - Объектно-ориентированное программирование (ООП) в PHP Интерфейсы, классы,

См.также

На этом все! Теперь вы знаете все про объектно-ориентированное программирование, Помните, что это теперь будет проще использовать на практике. Надеюсь, что теперь ты понял что такое объектно-ориентированное программирование, ооп в php, интерфейсы, классы, абстрактные классы, объекты и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)

создано: 2014-10-16
обновлено: 2021-01-30
132568



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей



Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)

Термины: Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)