Как получить список свойств класса при помощи рефлексии в PHP
lang_of_article_differ
want_proper_trans
Reflection
дает пользователю возможность получить информацию о классе, функциях (методе класса), свойствах класса и параметрах методов. Также Reflection дает возможность создавать экземпляры классов (даже с закрытыми конструкторами), вызывать методы классов изменять значения свойств, читать phpdoc'и и так далее.
Рассмотрим следующий класс Post
:
class Post {
public $id;
/**
* это название статьи
*/
public $title;
/**
* это ее текст
*/
public $body;
public function save() {
// этот метод должен сохранить обьект в базе данных (но это не точно)
}
}
Представьте, что это класс модели, который связан с таблицей базы данных и может быть создан или обновлен в ней с помощью метода save ()
:
$somePost->save();
Для простоты понимания напишем что-то простенькое в теле метода сейв который это будет делать
public function save() {
$sqlQuery = '';
$setClause = '`title`="'.$this->title.'",'.
'`body` = "'.$this->body.'"';
if ($this->id > 0) {
$sqlQuery = 'UPDATE `post` SET '.$setClause.' WHERE id = '.$this->id;
} else {
$sqlQuery = 'INSERT INTO `post` SET '.$setClause.', id = '.$this->id;
}
return $this->db->execute($sqlQuery);
}
Что мы можем сказать об этом коде? Он захардкожен и может использоваться только в случае этого же класса Post. И что произойдет, если мы добавим дополнительное поле, например, num_views
, который сохранит количество просмотров поста? - Да, мы должны переписать наш метод save
. И так с каждым классом? - да. Да, это печально и неинтересно
Как мы можем решить эту проблему в общем случае? один из способов - использовать рефлексию. Давайте же использовать ее!
Первое использование рефлексии по назначению
Давайте слегонца перепишем метод сейва, чтобы он был более гибким:
public function save() {
$class = new \ReflectionClass($this);
$tableName = strtolower($class->getShortName());
$propsToImplode = [];
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { // обрабатываем только public поля
$propertyName = $property->getName();
$propsToImplode[] = '`'.$propertyName.'` = "'.$this->{$propertyName}.'"';
}
$setClause = implode(',',$propsToImplode); // склеиваим все пары ключ = значение запятыми
$sqlQuery = '';
if ($this->id > 0) {
$sqlQuery = 'UPDATE `'.tableName.'` SET '.$setClause.' WHERE id = '.$this->id;
} else {
$sqlQuery = 'INSERT INTO `'.tableName.'` SET '.$setClause.', id = '.$this->id;
}
try {
return $this->db->execute($sqlQuery);
} catch (\Exception $e) {
// обрабатываем как-то исключение
}
}
Вот смотрите, мы уже научились генерировать SQL запрос на обновление и вставку всех полей класса в бд, Не круто ли? - Круто.
Выходит написал клас, заэкстендил этот метод и вуаля - можно сразу сохранять в бд (пррр, не спешите - создание таблиц в mysql еще никто не отменял ) - да, выходит именно так.
В этом случае мы динамически получаем имя таблицы (мы используем имя класса в нижнем регистре в качестве имени таблицы) и все поля, которые должны быть обновлены или вставлены.
Теперь мы можем использовать этот метод не только в классе Post
, но и в любом классе, который должен иметь такую функциональность, и он будет работать, как мы ожидаем.
Я получаю свойства класса, который имеет только публичную видимость, предоставляя параметр для метода getProperties
экземпляраReflectionClass
:
$class->getProperties(\ReflectionProperty::IS_PUBLIC);
Такой подход позволит иметь в классах не только поля данных а и поля для внутренней спрятаной логики.
Метод getProperties
возвращает массив объектовReflectionProperty
. В этом уроке мы использовали только метод getName
, который возвращает имя свойства. например, title
для паблик поля обьявленого следующим образом:
public $title;
Вы можете опустить этот параметр, чтобы получить все свойства. Или вы можете получить поля с модификаторам private или protected наоборот.
Вы также можете проверить видимость поля с помощью методов ReflectionProperty
:isPublic()
, isPrivate()
илиisProtected()
.
Полученная информация о полях класса может вам здорово помочь, как в примере выше где мы генерируем SQL для запроса, по факту же можно делать очень интересные вещи основываясь на информации о полях класса полученой при помощи рефлексии в пхп.