Объектно-ориентированный PHP: автоматическая загрузка классов, сериализация и получение информации об объектах

Добро пожаловать в четвертый урок из серии, посвященной ООП в PHP! Если вы пропустили первые три урока, вам возможно, захочется рассмотреть и их, дабы узнать подробнее о классах и объектах в PHP:

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

  • Как автоматически загружать классы;
  • Как конвертировать объекты в строку и строку в объекты для удобства хранения и передачи;
  • Как получить подробную информацию об определенном классе или объекте.

Готовы? Вперед!

Автоматическая загрузка классов

Как правило, полезно хранить каждый PHP класс в отдельном файле. Например, в приложении веб-форума, вы бы хранили класс Member в файле Member.php, а класс Topic - в файле Topic.php. Скорее всего, вы будете хранить все эти файлы в папке classes где-то на сайте:

classes/
  Member.php
  Topic.php

Затем, если понадобится создать объект класса, например, Member, PHP сначала придется подключить файл с этим классом:

<?php
require_once( "classes/Member.php" );
$member = new Member();
?>

Хранение классов таким образом - вещь полезная не только для вашей психики, но и для использования такого удобного свойства, как автоматическая загрузка (autoloading).

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

Давайте приведем пример. Напишем функцию __autoload() для автоматической загрузки классов из папки classes:

<?php
 
function __autoload( $className ) {
  $className = str_replace( "..", "", $className );
  require_once( "classes/$className.php" );
  echo "Loaded classes/$className.php<br>";
}
 
$member = new Member();
echo "Created object: ";
print_r( $member );
 
?>

Вот, как это работает. Сперва, создаем функцию __autoload() с входным параметром $className. В начале, функция убирает все подстроки “..” из полученного параметра, это делается в целях безопасности. Затем, с помощью функции require_once(), она подгружает нужный файл. Функции известно, что он находится в папке classes, и его расширение - .php. Функция также выводит на страницу сообщение, так что мы сразу увидим, что она отработала.

Затем протестим нашу функцию, создав объект класса Member. Так как мы предварительно не подгрузили файл с данным классом, PHP запустит функцию __autoload(), передав ей имя класса - “Member”. Она, в свою очередь, будет искать файл classes/Member.php. Затем PHP создает-таки объект Member. В завершении, отображаем сообщение о том, что объект создан.

Чтобы протестировать работу данного скрипта, создадим папку classes в том же каталоге, что и скрипт. Создадим в ней файл Member.php с простеньким классом:

<?php
  
class Member {
}
 
?>

Теперь, когда мы запустим скрипт, загрузится файл classes/Member.php, создастся объект класса Member, и на экране отобразится следующее:

Loaded classes/Member.php
Created object: Member Object ( )

Автоматическая загрузка поможет вам сэкономить уйму времени, тем более, если у вас огромное количество классов. Вместо того, чтобы вызывать функцию require_once() в начале каждого файла, вы просто создаете в начале всего приложения функцию __autoload(), и отдаете управление загрузкой классов в руки PHP!

Сериализация объектов

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

  • Передача объектов через поля веб-форм;
  • Передача объектов через адресную строку;
  • Хранение объекта в текстовом файле или одном поле таблицы базы данных.

Для конвертации объекта в строку, и обратно, используются следующие функции:

  • serialize() - принимает объект и возвращает строковое представление его класса и свойств;
  • unserialize() - принимает строку, созданную при помощи serialize(), и возвращает объект.

Давайте посмотрим на эти две функции в деле:

<?php
  
class Member
{
  public $username = "";
  private $loggedIn = false;
  
  public function login() {
    $this->loggedIn = true;
  }
  
  public function logout() {
    $this->loggedIn = false;
  }
  
  public function isLoggedIn() {
    return $this->loggedIn;
  }
}
 
$member = new Member();
$member->username = "Fred";
$member->login();
 
$memberString = serialize( $member );
echo "Converted the Member object to a string: '$memberString'<br>";
echo "Converting the string back to an object...<br>";
$member2 = unserialize( $memberString );
echo $member2->username . " is " . ( $member2->isLoggedIn() ? "logged in" : "logged out" ) . "<br>";
  
?>

Мы создали простенький класс Member с полем public $username, полем private $loggedIn и тремя методами public: login(), logout() и isLoggedIn(). Затем наш скрипт создает объект класса Member, дает ему имя "Fred" и логинит его.

Затем вызываем функцию serialize(), передав ей объект класса Member. serialize() возвращает строковое представление данного объекта, которое мы сохраним в переменной $memberString и отобразим на странице:

Converted the Member object to a string:
'O:6:"Member":2:{s:8:"username";s:4:"Fred";s:16:"MemberloggedIn";b:1;}'

Затем конвертируем нашу строку обратно в объект класса Member, вызвав функцию unserialize(), и сохраняем полученный объект в переменной $member2. Чтобы проверить, что наш объект сконвертировался верно и полностью, мы отображаем значение его поля $username и вызываем его метод isLoggedIn(), чтобы проверить, залогинился ли пользователь. Вот, что отобразится на странице:

Converting the string back to an object...
Fred is logged in

Как видите, строка, созданная функцией serialize(), содержит имя класса, а также названия всех его полей и их значения для конкретного объекта. (Перед полями private записывается имя класса, без пробелов.) Тем не менее, названия методов класса не записываются в строку.

Чтобы сработала функция unserialize(), класс объекта, который должен быть сконвертирован из строки, должен подгружаться еще до того, как идет вызов unserialize(). Вы можете написать сам класс в том же скрипте, где вызывается unserialize(), или подгрузить файл с данным классом через функцию require_once(). Также вы можете создать функцию __autoload(), о которой мы говорили ранее. PHP вызовет __autoload(), если не сможет найти класс, объект которого вы пытаетесь сконвертировать.

На заметку: функции serialize() и unserialize() работают также и с другими типами данных, таких как массивы. Тем не менее, они не работают с ресурсами.

Функции __sleep() и __wakeup()

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

В PHP есть несколько специальных методов, которые помогут вам в этом:

  • __sleep() вызывается строго перед тем, как объект сериализуется с помощью функции serialize().
  • __wakeup() вызывается сразу после того, как объект десериализуется с помощью unserialize().

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

Давайте добавим в наш предыдущий пример методы __sleep() и __wakeup():

<?php
  
class Member
{
  public $username = "";
  private $loggedIn = false;
  
  public function login() {
    $this->loggedIn = true;
  }
  
  public function logout() {
    $this->loggedIn = false;
  }
  
  public function isLoggedIn() {
    return $this->loggedIn;
  }
 
  public function __sleep() {
    echo "Cleaning up the object...<br>";
    return array( "username" );
  }
 
  public function __wakeup() {
    echo "Setting up the object...<br>";
  }
 
}
 
$member = new Member();
$member->username = "Fred";
$member->login();
 
$memberString = serialize( $member );
echo "Converted the Member object to a string: '$memberString'<br>";
echo "Converting the string back to an object...<br>";
$member2 = unserialize( $memberString );
echo $member2->username . " is " . ( $member2->isLoggedIn() ? "logged in" : "logged out" ) . "<br>";
  
?>

Вот, что отобразится на странице:

Cleaning up the object...
Converted the Member object to a string: 'O:6:"Member":1:{s:8:"username";s:4:"Fred";}'
Converting the string back to an object...
Setting up the object...
Fred is logged out

Обратите внимание на то, что:

  • Наши методы __sleep() и __wakeup() в действительности ничего не подчищают и не задают; вместо этого, они просто выводят сообщения "Cleaning up the object..." и "Setting up the object...".
  • Так как мы включаем только поле $username в массив, который возвращает __sleep(), в результирующей строке не будет присутствовать поле $loggedIn.
  • В результате, поле $loggedIn десериализованного объекта примет значение по умолчанию - false, поэтому при вызове метода isLoggedIn() от десериализованного объекта, он вернет false. Вот, почему скрипт отобразит сообщение "Fred is logged out".

Если вы хотите написать метод __sleep() и чтобы все поля были сериализованы, тогда вам понадобится перечислять все поля для массива, который возвращает метод __sleep(). Этого легко достичь при помощи функций PHP array_keys() и get_object_vars() таким образом:

public function __sleep() {
  // почистить
  return array_keys( get_object_vars( $this ) );
}

На заметку: другой классный способ сериализации объектов (причем, кросс-платформенный) - это конвертирование строк JSON.

Получение информации о классах и объектах

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

Вот краткий перечень наиболее часто употребляемых из них:

Функция Описание
get_class( $obj ) Возвращает имя класса, которому принадлежит объект $obj
get_parent_class($className или $obj ) Возвращает имя класса, который является родителем класса $className, или родителем класса, чьим объектом является $obj
is_a( $obj, $className ) Возвращает true, если $obj - объект класса $className, или если $obj - объект класса - потомка $className
$obj instanceof $className Делает то же самое, что и функция is_a()
get_class_methods($className или $obj ) Возвращает массив названий всех методов класса $className или объекта $obj
get_class_vars( $className ) Возвращает ассоциативный массив всех полей класса $className вместе с их значениями по умолчанию
get_object_vars( $obj ) Возвращает ассоциативный массив всех полей объекта $obj, вместе с их текущими значениями

Важно отметить, что такие функции, как get_class_methods(), get_class_vars() и get_object_vars(), возвращают только те методы и поля, которые находятся в одной области действия с кодом, их вызывающим. Например, метод private будет включен в возвращаемое значение функции get_class_methods(), только если get_class_methods() вызывается внутри метода класса.

Давайте поэкспериментируем с некоторыми из этих функций на таком примере:

<?php
  
class Member {
  
  public $username = "";
  private $loggedIn = false;
  
  public function login() {
    $this->loggedIn = true;
  }
  
  public function logout() {
    $this->loggedIn = false;
  }
  
  public function isLoggedIn() {
    return $this->loggedIn;
  }
 
  public function listMyProperties() {
    echo "My properties are: ";
    print_r( get_object_vars( $this ) );
  }
}
  
class Administrator extends Member {
  
  public $adminLevel = 1;
 
  public function createForum( $forumName ) {
    echo "$this->username created a new forum: $forumName<br>";
  }
  
  public function banMember( $member ) {
    echo "$this->username banned the member: $member->username<br>";
  }
  
}
 
$admin = new Administrator();
$admin->username = "Mary";
$admin->login();
 
echo "1. $admin->username's class is " . get_class( $admin ) . "<br>";
echo "2. $admin->username's parent class is " . get_parent_class( $admin ) . "<br>";
echo "3. Is $admin->username a Member? " . ( is_a( $admin, "Member" ) ? "Yes" : "No" ) .  "<br>";
echo "4. $admin->username's methods are: ";
print_r( get_class_methods( $admin ) );
echo "<br>5. $admin->username's class properties are: ";
print_r( get_class_vars( get_class( $admin ) ) );
echo "<br>6. $admin->username's object properties are: ";
print_r( get_object_vars( $admin ) );
echo "<br>7. ";
$admin->listMyProperties();
 
?>

В скрипте создается класс Member с несколькими полями и методами. Также в нем создается класс Administrator - дочерний от класса Member. В классе Administrator добавляется поле public $adminLevel, а также пара методов: createForum() и banMember(), которые будут только у объектов класса Administrator.

После создания класса, мы создаем объект класса Administrator, сохраняем его в переменной $admin, даем ему имя "Mary" и логиним админа при помощи вызова метода login().

В последней части скрипта (строки 44-54), мы применяем различные функции к нашим объектам и классам. Вот результат работы данного кода:

Mary's class is Administrator
Mary's parent class is Member
Is Mary a Member? Yes
Mary's methods are: Array ( [0] => createForum [1] => banMember [2] => login [3] => logout [4] => isLoggedIn [5] => listMyProperties )
Mary's class properties are: Array ( [adminLevel] => 1 [username] => )
Mary's object properties are: Array ( [adminLevel] => 1 [username] => Mary )
My properties are: Array ( [adminLevel] => 1 [username] => Mary [loggedIn] => 1 )

Вот, как отработают данные функции:

  1. get_class( $admin ) возвращает строковое значение "Administrator".
  2. get_parent_class( $admin ) возвращает строковое значение "Member".
  3. is_a( $admin, "Member" ) возвращает true.
  4. get_class_methods( $admin ) возвращает массив названий методов для классов Administrator и Member. Так как все эти методы - public, они все будут записаны в массив.
  5. get_class_vars( $admin ) возвращает массив названий полей public и соответствующих им значений по умолчанию для классов Administrator и Member.
  6. get_object_vars( $admin ) возвращает массив названий полей public и соответствующих им текущих значений.
  7. В конце вызываем метод класса Member - listMyProperties(). Этот метод вызывает функцию get_object_vars() и отображает результат. Тем не менее, так как get_object_vars() теперь вызывается изнутри класса, он также возвращает поле private - $loggedIn.

Как видите, в PHP очень легко извлечь любую информацию об объекте или классе. В PHP есть еще функции для получения инфы об объекте, классе и интерфейсе, например, class_exists(), get_called_class(), get_declared_classes(), get_declared_interfaces(), interface_exists(), is_subclass_of(), method_exists() и property_exists().

Итоги

В этом уроке вы ознакомились с тремя полезнейшими свойствами PHP объектов:

  1. Использование функции __autoload() для автоматической загрузки файлов с необходимыми классами;
  2. Как конвертировать объекты в строки и обратно при помощи функций PHP serialize() и unserialize();
  3. Получение информации об объектах, классах, предках при помощи PHP функций.

С помощью этих трех свойств работать с классами и объектами в PHP не составит огромного труда.

Надеюсь, вам понравилась данная серия уроков, посвященная ООП в PHP! Если вы прошли все 4 урока, у вас достаточно знаний для написания классных PHP сайтов и приложений. Однако, на этом история не заканчивается — в PHP еще много полезных свойств, предназначенных для работы с ООП, например:

  • Итерация: как последовательно пройтись по всем полям объекта;
  • Больше специальных методов, включая __callStatic(), __isset(), __unset(), __toString(), __invoke(), __set_state() и __clone()
  • Паттерны: техника, позволяющая достичь много целей при помощи объектов;
  • Копирование и сравнение объектов;
  • Статическое позднее связывание, которое дает кучу возможностей при вызове статических наследуемых методов.

Если у меня будет время, и если у вас будет такая необходимость, я обязательно расскажу о них в следующих уроках.

Удачного кодирования!

Источник: http://feedproxy.google.com/~r/ruseller/CdHX/~3/mY6IWaHuUNo/lessons.php

Читать комменты и комментировать

Добавить комментарий / отзыв



Защитный код
Обновить

Объектно-ориентированный PHP: автоматическая загрузка классов, сериализация и получение информации об объектах | | 2012-06-19 12:05:36 | | Статьи Web-мастеру | | Добро пожаловать в четвертый урок из серии, посвященной ООП в PHP! Если вы пропустили первые три урока, вам возможно, захочется рассмотреть и их, дабы узнать подробнее о классах и объектах в PHP:Если | РэдЛайн, создание сайта, заказать сайт, разработка сайтов, реклама в Интернете, продвижение, маркетинговые исследования, дизайн студия, веб дизайн, раскрутка сайта, создать сайт компании, сделать сайт, создание сайтов, изготовление сайта, обслуживание сайтов, изготовление сайтов, заказать интернет сайт, создать сайт, изготовить сайт, разработка сайта, web студия, создание веб сайта, поддержка сайта, сайт на заказ, сопровождение сайта, дизайн сайта, сайт под ключ, заказ сайта, реклама сайта, хостинг, регистрация доменов, хабаровск, краснодар, москва, комсомольск |
 
Поделиться с друзьями: