objects06 8

Функции для работы с классами и объектами

В PHP предусмотрен широкий набор функций для тестирования классов и объектов. Для чего это нужно? Вы, наверняка, сами написали большинство классов, которые используются в сценарии. На самом деле во время выполнения программы не всегда известно, какие классы используются. Например, вы могли разработать систему для "прозрачной" работы со сложными классами, разработанными сторонними производителями. В подобном случае экземпляр объекта обычно создается только на основании имени класса. В PHP разрешается использовать строки, чтобы ссылаться на классы динамически, как показано ниже:

Код PHP
// Файл Task.php
namespace tasks;

class Task {
	function doSpeak() { 
    	echo "Привет!";
	}
}

// Файл TaskRunner.php 
$classname = "Task";

require_once "{$classname}.php";
$classname = "tasks\\$classname";

$myObj = new $classname(); 
$myObj->doSpeak();

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

Обычно подобные операции выполняют, когда нужно, чтобы система могла выполнять созданные пользователем подключаемые дополнительные модули (plug-ins). Но прежде чем делать такие рискованные вещи в реальном проекте, вы должны проверить, что указанный класс существует, у него есть нужные вам методы и т.д.

В PHP 5 часть функций для работы с классами была заменена более мощным Reflection API, который мы будем изучать в следующей статье. Однако простота и удобство использования в некоторых случаях делают эти функции более предпочтительными. По этой причине мы сейчас и приступим к их рассмотрению.

Поиск классов

Функции class_exists() передается строка, содержащая имя класса. Она возвращает значение true, если класс существует, и false — в противном случае. С помощью этой функции можно сделать предыдущий фрагмент кода более безопасным:

Код PHP
// Файл TaskRunner.php
$classname = "Task";
$path = "{$classname}.php";

if ( !file_exists( $path )) {
    throw new Exception( "Файл {$path} не найден" );
}

require_once( $path );
$qclassname = "tasks\\$classname";

if ( !class_exists( $qclassname ) ) {
    throw new Exception( "Класс $qclassname не найден" );
}

$myObj = new $qclassname();
$myObj->doSpeak();

Конечно, мы не можем быть уверенными, что для конструктора рассматриваемого класса не потребуются аргументы. Для достижения такого уровня безопасности программирования необходимо обратиться к Reflection API, который мы будем изучать в следующей статье. Тем не менее перед его использованием с помощью функции class_exists() мы должны убедиться в том, что нужные нам классы существуют.

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

Чтобы получить массив всех классов, определенных в сценарии, можно воспользоваться функцией get_declared_classes():

Код PHP
print_r(get_declared_classes());

В результате будет выведен список встроенных и определенных пользователем классов. Помните, что данная функция возвращает только те классы, которые были объявлены к моменту ее вызова.

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

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

Код PHP
function getProduct() {
	return new CDProduct('Первый снег', 'Группа', 
				'Моральный кодекс', 2.99, 55.01);
}

$product = getProduct();
if (get_class($product) == 'CDProduct')
	echo '$product - объект класса CDProduct';

Функция getProduct() просто создает экземпляр объекта CDProduct и возвращает его. Мы воспользуемся данной функцией в этом разделе. Функция get_class() выдает очень специфическую информацию. Обычно же нужна более общая информация о принадлежности к семейству классов. Предположим, нам нужно знать, что объект принадлежит семейству ShopProduct, но при этом не имеет значения, к какому классу конкретно: BookProduct или CDProduct. Для этой цели в PHP предусмотрен оператор instanceof.

Оператор instanceof работает с двумя операндами: объектом, который нужно протестировать (указывается слева от ключевого слова instanceof), и именем класса или интерфейса справа. Оператор возвращает значение true, если объект является экземпляром класса указанного типа:

Код PHP
$product = getProduct();
if ($product instanceof ShopProduct)
	echo '$product - объект класса CDProduct';

Получение информации о методах

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

Код PHP
foreach (get_class_methods('CDProduct') as $key=>$value) {
	echo "<b>$key</b> => {$value}()<br>";
}
Получение всех методов класса CDProduct

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

Получение информации о свойствах

Точно так же, как можно запросить список методов класса, можно запросить и список его полей. Функции get_class_vars() передается имя класса, а она возвращает ассоциативный массив. Имена полей сохраняются в виде ключей этого массива, а значения полей — в виде значений.

Получение информации о наследовании

С помощью функций для работы с классами можно также выявлять отношения наследования. Например, с помощью функции get_parent_class() можно узнать имя родительского класса для указанного класса. Этой функции передается ссылка на объект или имя класса, а она возвращает имя суперкласса, если таковой существует. Если же такого класса нет, т.е. если у проверяемого класса нет родительского класса, то функция вернет значение false. В результате вызова

Код PHP
echo get_parent_class('CDProduct');

мы получим имя родительского класса ShopProduct, как и можно было ожидать.

С помощью функции is_subclass_of() можно также проверить, является ли класс дочерним для другого класса. Этой функции передается ссылка на дочерний объект и имя родительского класса. Функция возвращает значение true, если второй класс является суперклассом первого аргумента:

Код PHP
function getProduct() {
	return new CDProduct('Первый снег', 'Группа', 
				'Моральный кодекс', 2.99, 55.01);
}

$product = getProduct(); // Получим объект
if (is_subclass_of($product, 'ShopProduct'))
	echo 'CDProduct является подклассом класса ShopProduct';

Функция is_subclass_of() сообщит информацию только об отношениях наследования в классе. Но она не поможет вам узнать, реализует ли класс интерфейс. Для этой цели следует использовать оператор instanceof. Кроме того, можно воспользоваться функцией class_implements(), которая является частью SPL (Standard PHP Library — стандартная библиотека PHP). Этой функции передается имя класса или ссылка на объект, а она возвращает массив имен интерфейсов:

Код PHP
$product = getProduct();

if (in_array('IChargeable', class_implements($product)))
	echo 'CDProduct реализует интерфейс IChargeable';

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

Сборки и пространства имен
Рефлексия

Комментарии (0)

Результаты поиска по запросу

Система Orphus