Pull to refresh

Кастомизация каптчи в Zend Framework 2

Reading time 6 min
Views 1.9K
Компонент Zend\Captcha может принимать различные формы, в том числе задавать логические вопросы, генерировать искаженные шрифты, и передавать несколько изображений, установив между ними связь. Zend\Captcha имеет целью обеспечить разнообразие серверных решений, которые могут быть использованы либо в автономном режиме либо в сочетании с Zend\Form компонентой.

Элемент Captcha имеет более одного поля, которые рендерятся друг за другом. Встроеный генератор изображений каптч (Zend\Captcha\Image.php) использует свой собственный хелпер (Zend\Form\View\Helper\Captcha\Image.php) для создания изображения. Так-же, в Zend\Captcha\Image.php находится метод 'getHelperName'. Этот метод передает имя хелпера для рендеринга изображения каптчи. По умолчанию 'getHelperName' передает 'captcha/image' — экземпляр класса Zend\Form\View\Helper\Captcha\Image.php. Если углубиться с помощью дебаггера, можно увидеть, что в свойстве экземпляра phpRenderer::__helpers, в invokablesClasses располагается хелпер 'captchaimage'. Это и есть Zend\Form\View\Helper\Captcha\Image.php, упомянутый ранее. Грубо говоря — вы просто создаете изображение, а всё остальное делает за вас рендерер используя хелпер, хотя такое положение вещей устраивает не всех.

Когда же вы создаете элемент формы Captcha (Zend\Form\Element\Captcha.php), вы передаете этому элементу изображение каптчи(Zend\Captcha\Image.php). Элемент формы Captcha, в свою очередь, имеет тоже свой хелпер (Zend\Form\View\Helper\FormCaptcha.php). В методе render этого хелпера вы увидите, что изображение каптчи (Zend\Captcha\Image.php) загружается с помощью ElementInterface:

//Zend\Form\View\Helper\FormCaptcha.php
public function render(ElementInterface $element)
    {
        $captcha = $element->getCaptcha();


после чего вызывается хелпер через метод 'getHelperName':

$helper  = $captcha->getHelperName();


В итоге мы получаем экземпляр класса хелпера посредством экземпляра PhpRenderer и возвращаем представление:

$helper = $renderer->plugin($helper);
        return $helper($element);


Нам нужно четко представлять различие между 'элементом формы Captcha и его хелпером' и 'изображением Captcha также со своим хелпером'. Мы передадим 'изображение каптчи' 'элементу формы Captcha', который уже располагает привязанным хелпером. Метод render 'элемента формы Captcha' встроит экземпляр 'изображения Captcha', который мы передали, используя хелпер для генерации изображения и вернет представление. Все, что нужно сделать, это передать 'изображению Captcha' новый хелпер, который отобразит представление так, как нас устроит и переписать Zend\Captcha\Image.php, чтоб он получил наш новый хелпер, а не свой по умолчанию.

Перед тем как начать, давайте обратим внимание на несколько деталей:
хелпер Zend\Form\View\Helper\Captcha\Image.php для отображения каптчи определяет паттерн как %s%s%s:

$pattern = '%s%s%s';


Таким образом, первое, что необходимо — наш кастомный хелпер с собственным паттерном. Давайте создадим его в модуле Application:

//module\Application\src\Application\View\Helper\Form\Captcha\ViewHelperCaptcha.php
<?php

namespace Application\View\Helper\Form\Captcha;

use Zend\Form\View\Helper\Captcha\AbstractWord;
use Application\View\Helper\Form\Captcha\CustomCaptcha as CaptchaAdapter;
use Zend\Form\ElementInterface;
use Zend\Form\Exception;

class ViewHelperCaptcha extends AbstractWord
{
/**
* Override
*
* Render the captcha
*
* @param ElementInterface $element
* @throws Exception\DomainException
* @return string
*/
    public function render(ElementInterface $element)
    {
//Мы можем установить здесь разделитель между изображением и полем ввода.
        $this->setSeparator('')
        $captcha = $element->getCaptcha();
        if ($captcha === null || !$captcha instanceof CaptchaAdapter) {
            throw new Exception\DomainException(sprintf(
                '%s requires that the element has a "captcha" attribute of type Zend\Captcha\Image; none found',
                __METHOD__
            ));
        }
//Как долго будет храниться изображение (по умолчанию 600).
        $captcha->setExpiration(10);

//Как часто выполнять очистку файлов(по умолчанию 10). При данной конфигурации старый файл затирается вновь созданным.
        $captcha->setGcFreq(1);

        $captcha->generate();
        $imgAttributes = array(
        'width' => $captcha->getWidth(),
        'height' => $captcha->getHeight(),
        'alt' => $captcha->getImgAlt(),
        'src' => $captcha->getImgUrl() . $captcha->getId() . $captcha->getSuffix(),
        );
        $closingBracket = $this->getInlineClosingBracket();
        $img = sprintf(
            '<img %s%s',
            $this->createAttributesString($imgAttributes),
            $closingBracket
            );
        $position = $this->getCaptchaPosition();
        $separator = $this->getSeparator();
        $captchaInput = $this->renderCaptchaInputs($element);

//Наш измененный паттерн
        $pattern = '<div class="captcha_image">
            %s</div>
            %s<div class="captcha_input">
            %s</div>'

        if ($position == self::CAPTCHA_PREPEND) {
                return sprintf($pattern, $captchaInput, $separator, $img);
            }
            return sprintf($pattern, $img, $separator, $captchaInput);
    }
}


Класс нашего хелпера дублирует класс Zend\Form\View\Helper\Captcha\Image.php с небольшими изменениями. Наш хелпер не использует Zend\Captcha\Image.php для генерации изображения, в отличие от оригинального. Помните, что Zend\Captcha\Image.php предоставляет метод 'getHelperName' который возвращает жёстко закодированное имя хелпера 'captcha/image', таким образом, когда форма будет генерировать изображение, она получит для этого не свой хелпер по умолчанию, а только что созданный нами. Что еще нужно, так это передать в phpRenderer наш кастомный хелпер и сгенерировать новое изображение каптчи, которое будет расширять оригинальный класс Zend\Captcha\Image.php и перепишет метод 'getHelperName' установив имя хелпера, который мы создали.

Итак, давайте добавим наш класс в конфигурацию хелперов phpRenderer invokables. Реализуем это в module.config.php:

//module\Application\config\module.config.php
...
'view_helpers' => array(
    'invokables' => array(
        'viewhelpercaptcha' => 'Application\View\Helper\Form\Captcha\ViewHelperCaptcha', 
        ),
    ),


Следующим шагом будет создание изображения каптчи, которое возвратит наш хелпер добавленный в конфигурацию модуля как phpRenderer invokables класс. Нет нужды переопределять весь класс Zend\Captcha\Image.php, достаточно указать методу 'getHelperName' наш кастомный класс хелпера в качестве параметра. Для этого создадим класс, и назовем его, например, CustomCaptcha.php в папке module\Application\src\Application\View\Helper\Form\Captcha. Мы собираемся расширить оригинальный класс Zend\Captcha\Image.php и переопределить метод 'getHelperName' чтобы он вернул наш хелпер 'viewhelpercaptcha'. Не помешает также переопределить сообщения об ошибках в свойстве $messageTemplates. На ваше усмотрение.

//module\Application\src\Application\View\Helper\Form\Captcha\CustomCaptcha.php
<?php

namespace  Application\View\Helper\Form\Captcha;
 
//Оригинальный класс, который мы расширим.
use Zend\Captcha\Image as CaptchaImage;

//Новая версия класса, в которой мы изменим только необходимое.
class CustomCaptcha extends CaptchaImage
{ 
    protected $messageTemplates = array(
        self::MISSING_VALUE => 'Отсутствует значение',
        self::MISSING_ID    => 'Поле ID отсутствует',
        self::BAD_CAPTCHA   => 'Неверно введено значение',

    public function getHelperName()
    {
        return 'viewhelpercaptcha';
    } 
}


Последнее, что нам нужно сделать — использовать наше CustomCaptcha изображение в форме. Для этого создадим две папки: одну для шрифта (zf2folder/data/fonts), который понадобится чтобы генерировать слова каптчи, и другую, для хранения файлов изображений каптч (zf2folder/public/img/captcha). Естественно, в папку fonts скопируем шрифт *.ttf, например arial.ttf.

Для примера можно взять форму из официального туториала:

<?php

namespace Album\Form;

use Zend\Form\Form;
use Application\Form\View\Helper\Captcha\CustomCaptcha;

class AlbumForm extends Form
{
    public function __construct($name = null)
    {
//Имя формы можно игнорировать
        parent::__construct('album');
        $this->setAttribute('method', 'post');

//Здесь расположены стандартные элементы
        ...

//Здесь будем создавать элемент Captcha

        $dirdata = './data';

//Создаем новый CustomCaptcha класс
        $captchaImage = new CustomCaptcha(array(
            'font'           => $dirdata . '/fonts/arial.ttf',
            'width'          => 120,
            'height'         => 60,
            'fsize'          => 20,
            'wordLen'        => 5,
            'dotNoiseLevel'  => 25,
            'lineNoiseLevel' => 2
        ));

//Назначаем директорию для хранения файлов
        $captchaImage->setImgDir('public/img/captcha/');

//Назначаем путь для загрузки файлов каптчи
        $captchaImage->setImgUrl('/img/captcha/');
        $captchaImage->setImgAlt('Вы человек или робот?');

//Создаем элемент формы Captcha куда добавим нашу CustomCaptcha, созданную выше

        $this->add(array(
            'type'       => 'Zend\Form\Element\Captcha',
            'name'       => 'captcha',
            'options'    => array(
                'captcha' => $captchaImage,
            ),
            'attributes' => array(
                'class' => 'some_class',
            )
        ));
        
        $this->add(array(
            'name' => 'submit',
            'attributes' => array(
                'type'  => 'submit',
                'value' => 'Go',
                'id' => 'submitbutton',
            ),
        ));
    }
}


Добавим в файл вида элемент нашей капчи

echo $this->formRow($form->get('captcha')) . PHP_EOL; 


Изучив структуру html документа, получившегося на выходе, вы увидите, что теперь изображение и поле ввода каптчи обернуты в div-ы. Мы получили более упорядоченный дизайн.

Использованные источники:
framework.zend.com/manual/2.2/en/modules/zend.captcha.intro.html
framework.zend.com/manual/2.2/en/modules/zend.captcha.operation.html
framework.zend.com/manual/2.2/en/modules/zend.captcha.adapters.html
framework.zend.com/manual/2.2/en/user-guide/forms-and-actions.html
zendtemple.blogspot.com/2012/12/zend-framework-2-zf2-creating-view.html
samsonasik.wordpress.com/2012/09/12/zend-framework-2-using-captcha-image-in-zend-form
Tags:
Hubs:
+2
Comments 1
Comments Comments 1

Articles