Уроки PHP - урок 13 - Основы ООП (объектно-ориентированного программирования)
На прошлых уроках я уже писал про следующие типы данных PHP: логические, целочисленные, дробные, текстовые, массивы. В этом уроке я расскажу об еще одном типе данных доступном в PHP - объект.
Объекты чем-то похожи на массивы, у объекты могут содержать в себе различные типы данных как и массивы: числа, текст, массивы и даже другие объекты.
Объекты могут содержать в себе другие типы данных, как массивы. Тогда эти данные мы называем свойствами (полями) объекта. Также объекты могут содержать функции в самих себя, тогда эти функции мы называем методами.
Но у объектов есть 2 больших отличия от массивов:
1. Объекты могут содержать методы.
Методы - это функции вызываемые от имени объекта. Привязка функций объекту позволяет обозначить какие операции выполняет объект и операции выполняемые над объектами.
2. Объекты создаются из классов.
Перед тем как создать объект мы должны определить какой-нибудь класс. В классе мы описываем какие поля и методы будут у объекта. Это позволяет определить структуру объектов и упрощает понимание того, какие данные должен хранить объект и какие операции выполнять. При создание объекта из класса мы можем заполнить объект исходными данные, так чтобы все вновь созданные объекты были не пустыми.
Объект пожалуй самый удобный контейнер, чтобы хранить данные. Объекты в php помогают сравнивать объекты реального мира, например у нас есть база данных в которой мы храним информацию о книгах. У книги есть автор, количество страниц, издательство, номер издания, рецензия, типографические данные. Поэтому для удобства работы с данными мы группируем их в класс Книга, в котором есть свойства (поля) Автор, Количество страниц, Издательство, Номер издания, Рецензия, Типографические данные и т.д. Тем самым мы уже создаем объекты, которые похожи на настоящие книги. Это особенно удобно, потому данные наших сайтов мы будем хранить в таблицах с полями, так что мы можем задать имена таблиц как классам, а поля таблиц как имена свойств класса. В результате работа с данными существенно упростится.
Более подробно мы рассмотрим отличие массивов от объектов на примерах.
Возможности PHP 5.2 и выше:
Давайте начнем с определения класса наших объектов. Заметьте, что класс в единственном числе, а объекты во множественном, тем самым я заостряю ваше внимание на том что из одного класса можно создать много объектов:
<?php class book{ } ?>
С помощью слова class мы определяем класс для будущих объектов, а теперь давайте создадим объекты:
<?php class book{ } $book1 = new book; $book2 = new book; print_r($book1); print_r($book2); ?>
С помощью ключевого слова new мы создаем новые объекты класса book. Каждый объект класса мы будем называет экземпляром класса. Таким образом у нас два экземпляра класса book, а именно $book1, $book2. Еще мы дописали функции print_r, чтобы вывести на экран содержимое переменных $book1, $book2 и мы видим:
Как вы видите PHP указывает из какого класса был создан объект, также если бы в объекте были данные, то они тоже были выведены на экран. Давайте теперь добавим эти данные. Для начала нам нужно определить свойства класса. Начиная с PHP 5.2 свойства определяются с помощью трех слов: public, protected, private (также поддерживается старая запись var, об этом ниже). Если вы работали с Delphi или C++, то вам будет проще, потому что эти ключевые слова работают абсолютно также как и в этих языках программирования.
public - определяет открытые (публичные) свойства и методы класса. К этим свойствам можно обращаться после создания объекта, оперируя этим объектом. Через это слово мы зададим все свойства для данных о книге.
protected - определяет защищенные свойства и методы класса. Такие свойства и методы можно вызывать только внутри класса и внутри расширенных (дочерних, наследуемых) классов. Если создать объект и обратится к методу определенному как protected, то это вызовет ошибку. При изучение наследования классов мы рассмотрим protected подробнее.
private - определяет закрытые (частные) свойства и методы. Такие свойства и методы можно вызывать внутри класса и всё, нигде больше эти свойства и методы работать не будут, даже в дочерних классах.
Давайте пока не заморачиваться с protected и private и опишем все свойства и методы как public.
class book{ public $author; public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; }
Теперь у нас есть свойства в классе, что позволит обращаться в экземлярах класса к свойствам:
class book{ public $author; public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; } $book1 = new book; $book1->author = 'Abramenko I.A'; $book1->numberOfPages = 192; $book1->year = 2013; $book1->publishingOffice = 'Moscow'; $book1->editionNumber = 1; $book2 = new book; print '<pre>'; print_r($book1); print_r($book2); print '</pre>';
В результате у нас получится один заполненный объект и один пустой:
Я думаю теперь вам стало ясно как записывать в объекты необходимые данные. Теперь давайте еще разберемся с методами класса. Как я писал раньше, методы это обычные функции вызываемые от именни объекта или класса. Давайте добавим методы на вывод для каждого из свойств класса:
<?php class book{ public $author; public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; public function getAuthor(){ return $this->author; } public function getNumberOfPages(){ return $this->numberOfPages; } public function getYear(){ return $this->year; } public function getPublishingOffice(){ return $this->publishingOffice; } public function getEditionNumber(){ return $this->editionNumber; } } $book1 = new book; $book1->author = 'Abramenko I.A'; $book1->numberOfPages = 192; $book1->year = 2013; $book1->publishingOffice = 'Moscow'; $book1->editionNumber = 1; $book2 = new book; print '<pre>'; print $book1->getAuthor() . '<br />'; print $book1->getNumberOfPages() . '<br />'; print $book1->getYear() . '<br />'; print $book1->getPublishingOffice() . '<br />'; print $book1->getEditionNumber() . '<br />'; print '</pre>'; ?>
Таким образом мы можем обращаться к объекту с помощью методов, а не свойств. Обратите внимание на переменную $this. Ее значения совпадают со значениями текущего экземпляра класса. То есть если экземпляр $book1, то все значения $book1 есть в значение $this внутри класса. Таким образом мы можем задать через переменную $this работу с переменными внутри класса. Снаружи класса переменная $this уже не будет работать. Давайте теперь зададим еще два метода для вывода книги различными способами таблицей и div'ом.
class book{ public $author; public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; public function getAuthor(){ return $this->author; } public function getNumberOfPages(){ return $this->numberOfPages; } public function getYear(){ return $this->year; } public function getPublishingOffice(){ return $this->publishingOffice; } public function getEditionNumber(){ return $this->editionNumber; } public function displayTable(){ $content = '<table style="border: 1px solid #000">'; $content .= '<tr><td>Author</td><td>'. $this->getAuthor() . '</td></tr>'; $content .= '<tr><td>Number of pages</td><td>'. $this->getNumberOfPages() . '</td></tr>'; $content .= '<tr><td>Year</td><td>'. $this->getYear() . '</td></tr>'; $content .= '<tr><td>Publishing office</td><td>'. $this->getPublishingOffice() . '</td></tr>'; $content .= '<tr><td>Edition number</td><td>'. $this->getEditionNumber() . '</td></tr>'; $content .= '</table>'; return $content; } public function displayUnformatted(){ $content = '<div style="border:1px solid #ddd">'; $content .= '<label>Author:</label><div>'. $this->getAuthor() . '</div>'; $content .= '<label>Number of pages:</label><div>'. $this->getNumberOfPages() . '</div>'; $content .= '<label>Year:</label><div>'. $this->getYear() . '</div>'; $content .= '<label>Publishing office:</label><div>'. $this->getPublishingOffice() . '</div>'; $content .= '<label>Edition number:</label><div>'. $this->getEditionNumber() . '</div>'; $content .= '</div>'; return $content; } } $book1 = new book; $book1->author = 'Abramenko I.A'; $book1->numberOfPages = 192; $book1->year = 2013; $book1->publishingOffice = 'Moscow'; $book1->editionNumber = 1; $book2 = new book; $book2->author = 'Leshkina S.O.'; $book2->numberOfPages = 200; $book2->year = 2013; $book2->publishinOffice = 'Moscow'; $book2->editionNumber = 1; print $book1->displayTable(); print $book2->displayUnformatted();
У нас уже набралась горстка кода, который как вы видете довольно просто читается. Ах, я совсем забыл о названии для книги, давайте добавим еще одно поле name и методы для его вывода:
<?php class book{ public $author; public $name; // arghh, I forgot about it. public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; public function getAuthor(){ return $this->author; } public function getName(){ return $this->name; } public function getNumberOfPages(){ return $this->numberOfPages; } public function getYear(){ return $this->year; } public function getPublishingOffice(){ return $this->publishingOffice; } public function getEditionNumber(){ return $this->editionNumber; } public function displayTable(){ $content = '<table style="border: 1px solid #000">'; $content .= '<tr><td>Author</td><td>'. $this->getAuthor() . '</td></tr>'; $content .= '<tr><td>Name</td><td>'. $this->getName() . '</td></tr>'; $content .= '<tr><td>Number of pages</td><td>'. $this->getNumberOfPages() . '</td></tr>'; $content .= '<tr><td>Year</td><td>'. $this->getYear() . '</td></tr>'; $content .= '<tr><td>Publishing office</td><td>'. $this->getPublishingOffice() . '</td></tr>'; $content .= '<tr><td>Edition number</td><td>'. $this->getEditionNumber() . '</td></tr>'; $content .= '</table>'; return $content; } public function displayUnformatted(){ $content = '<div style="border:1px solid #ddd">'; $content .= '<label>Author:</label><div>'. $this->getAuthor() . '</div>'; $content .= '<label>Name:</label><div>'. $this->getName() . '</div>'; $content .= '<label>Number of pages:</label><div>'. $this->getNumberOfPages() . '</div>'; $content .= '<label>Year:</label><div>'. $this->getYear() . '</div>'; $content .= '<label>Publishing office:</label><div>'. $this->getPublishingOffice() . '</div>'; $content .= '<label>Edition number:</label><div>'. $this->getEditionNumber() . '</div>'; $content .= '</table>'; return $content; } } $book1 = new book; $book1->author = 'Abramenko I.A'; $book1->name = 'Drupal: User guide'; $book1->numberOfPages = 192; $book1->year = 2013; $book1->publishingOffice = 'Moscow'; $book1->editionNumber = 1; $book2 = new book; $book2->author = 'Leshkina S.O.'; $book2->name = 'Drupal: Fields, Images, Views'; $book2->numberOfPages = 200; $book2->year = 2013; $book2->publishinOffice = 'Moscow'; $book2->editionNumber = 1; print $book1->displayTable(); print $book2->displayUnformatted(); ?>
Возможности PHP 5.1.3 и ниже:
Если мы хотим определить свойства (переменные) внутри класса, то мы используем слово var.
<?php
class myClass{
}
?>
Давайте рассмотрим пример c массивами и функциями:
<?php $newArray =array(1,2,3,4,5); function _increment_all_elements($customArray){ foreach($customArray as $key){ $customArray[] = $key+1; } return $customArray; } $newArray = _increment_all_elements($newArray); print_r($newArray); ?>
Сам по себе пример особого смысла не имеет, мы добавили к существующему массиву его же элементы увеличенные на 1. Но главное как мы это сделали, мы сделали это с помощью функции, которые как нам уже известно лежат в основе функционального программирования на PHP. А объекты занимают особую роль и программирование с их использованием называется объектно-ориентированным.
Позже мы вернемся к нашему примеру и повторим его с помощью объекта, но пока нужно разобрать, что такое объект.
Объекты как и функции позволяют прикреплять функции, данные, но это имеет вид более приблеженный к реальному миру. Например объект товар может иметь свойства цена, вес, ширина, длина. Приближенность к реальному миру облегчает понимание кода.
Объекты создаются (обычно) как экземпляры какого-то класса, например объект "мяч" может быть экземпляра класса "товар". Класс это обобщение с описание свойств и функций, а объект это реализация класса.
Давайте уже создадим наш первый класс и объект:
<?php class WorkWithArray{ } ?>
С помощью слова class мы создаем класс, в фигурных скобках мы пишем свойства и функции класса. Кстати в классах функции называются методами, поэтому дальше вместо слова функция, я буду говорить метод класса. Тоже самое касается переменных, те переменные которые я записываю в классах, буду называть свойствами класса.
Теперь добавим первое свойство нашему объекту, а именно прикрепим к нему массив.
<?php class WorkWithArray{ var $myArray = array(); } ?>
Теперь наш класс работы с массивами имеет свойство массив, также еще называют свойства классов как переменные экземлпяра или членом данных. Добавляется переменные (свойства) классов с помощью слова var.
И еще было бы неплохо добавить функцию к нашему классу:
<?php class WorkWithArray{ var $myArray = array(); function increment_all_elements($newArray){ foreach($newArray as $key){ $newArray[] = $key+1; } return $newArray; } } ?>
Как видите и функции добавляются к классу как обычная функция, ничего необычного. Итак, мы создали класс, давайте теперь обратимся к нему и создадим объект.
<?php $myObject = new WorkWithArray; print_r($myObject); ?>
Выводим объект на экран мы с помощью функции print_r(), как мы это делали с массивами. А теперь, когда мы создали объект, давайте обратимся к нему, исходя из объявления класса у нас есть одно свойство myArray и один метод increment_all_elements():
<?php class WorkWithArray{ var $myArray = array(); function increment_all_elements($newArray){ foreach($newArray as $key){ $newArray[] = $key+1; } return $newArray; } } $myObject = new WorkWithArray; $myObject->myArray = array(2,4,6,7); print_r($myObject); ?>
Так мы обращаемся к свойствам объекта через два символа тире и больше, которые составляют знак принадлежности, это можно прочитать как "объекту myObject принадлежит свойство myArray".
Теперь мы можем оперировать с этим свойством, используя заданный метод:
<?php class WorkWithArray{ var $myArray = array(); function increment_all_elements($newArray){ foreach($newArray as $key){ $newArray[] = $key+1; } return $newArray; } } $myObject = new WorkWithArray; $myObject->myArray = array(2,4,6,7); $myObject->myArray = $myObject->increment_all_elements($myObject->myArray); print_r($myObject); ?>
Обращаемся мы к методам объекта так же как и к его свойству через знаки -> , обращение к методу, так же как и обращение к функции называется "вызовом". Мы вызваем метод increment_all_elements и в качестве параметра указали свойство объекта и этоже свойство потом перезаписали, в результате мы вывели измененным массив данных.
Это довольно простой пример показывающий как создаются объекты и как можно вызывать их свойства и методы. Дальше мы более углубленно рассмотрим объектно-ориентированное программирование.