# Класс Image

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

`<?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`.

←→
↑