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

Класс Image

В предыдущих уроках мы с вами реализовали класс Tag для работы с тегами.

Пусть с помощью этого класса мы хотим сделать HTML картинку:

<?php $image = new Tag('img'); echo $image->setAttr('src', 'img.png')->open(); // <img src="img.png"> ?>

Давайте сделаем работу с картинками более удобной. Не будем использовать класс Tag, а создадим специальный класс для работы с картинками, назвав его Image.

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

<?php $image = new Image(); echo $image->setAttr('src', 'img.png')->open(); // <img src="img.png"> ?>

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

К примеру, в теге img атрибут src является обязательным. Было бы удобно, чтобы новый класс работал так: если атрибут src не задан через setAttr, то он все равно будет созданном теге, но с пустым значением:

<?php $image = new Image(); echo $image->open(); // <img src=""> ?>

Кроме того, было бы неплохо сделать то же самое и для атрибута alt (важен для SEO, желательно, чтобы всегда был):

<?php $image = new Image(); echo $image->open(); // <img src="" alt=""> - alt тоже будет ?>

Реализация класса Image

Очевидно, что класс Image - это тот же класс Tag, но с некоторыми дополнениями.

Логично в этом случае не создавать этот класс с нуля, а унаследовать его от класса Tag:

<?php class Image extends Tag { } ?>

Как мы описали выше, класс Image отличается от класса Tag тем, что в Image по умолчанию задаются атрибуты src и alt.

Кроме того, обратите внимание на то, как мы вызываем конструкторы классов:

<?php $image = new Tag('img'); $image = new Image(); ?>

Как вы видите, класс Tag ожидает первым параметром имя тега, а класс Image вызывается без параметра - имя тега в данном случае нет необходимости задавать, так как наш класс всегда делает один и тот же тег img.

Для того, чтобы реализовать такое поведение, класс Image должен переопределить конструктор родителя:

<?php class Image extends Tag { public function __construct() { // Вызовем конструктор родителя, передав имя тега: parent::__construct('img'); } } ?>

Давайте в этом же конструкторе зададим атрибуты src и alt:

<?php class Image extends Tag { public function __construct() { $this->setAttr('src', ''); // установим атрибут src $this->setAttr('alt', ''); // установим атрибут alt parent::__construct('img'); // вызовем конструктор родителя } } ?>

Установку атрибутов можно упростить и выполнить в виде цепочки:

<?php class Image extends Tag { public function __construct() { $this->setAttr('src', '')->setAttr('alt', ''); parent::__construct('img'); } } ?>

При использовании нашего класса в дальнейшем мы можем затереть эти атрибуты с помощью setAttr на свое значение, а можем не затереть - тогда они так и останутся со значением '', смотрите пример:

<?php // Затрем src, но не alt: echo (new Image())->setAttr('src', 'img.png')->open(); // <img src="img.png" alt=""> ?>

Самостоятельно напишите реализацию описанного класса Image.

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

Установите созданной вами картинке атрибут width в значение 300, а атрибут height - в значение 200.

Откажемся от open

В классе Tag у нас есть два метода, которые используются для завершения цепочки и вывода тега на экран: это метод open и метод show.

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

Очевидно, что при использовании класса Image мы всегда будем завершать цепочку методом open.

Раз так, то давайте сделаем использование метода open не обязательным. То есть вместо этого:

<?php echo (new Image())->setAttr('src', 'img.png')->open(); ?>

Мы будем писать вот так:

<?php echo (new Image())->setAttr('src', 'img.png'); ?>

Используем для этого магический метод __toString:

<?php class Image extends Tag { public function __construct() { $this->setAttr('src', '')->setAttr('alt', ''); parent::__construct('img'); } public function __toString() { return parent::open(); // вызываем метод родителя } } ?>

Теперь получается, что при попытке преобразования объекта в строку, например, при выводе его через echo, автоматически будет вызываться метод __toString, внутри которого будет вызываться метод open.

Напоминаю, что если не выводить объект на экран, а, например, записать в переменную, то в эту переменную попадет объект, а не его строковое представление:

<?php // В переменную $image запишется объект: $image = (new Image())->setAttr('src', 'img.png'); $image->setAttr('width', '200'); // вызовем еще метод echo $image; // тут сработает __toString ?>

Самостоятельно напишите реализацию метода __toString.