Разработка

Symfony2: страница 404 и авторизация

Все знают, что в symfony2 404я страница не попадает под действие фаерволов. А это значит, что даже пытаясь кастомизировать 404ую страницу мы не сможем получить имя пользователя и его роль в системе. Так как механизм авторизации попросту не загружается.

Однако, существует решение, которое позволяет кастомизировать страницу 404 с учетом пользовательских данных.

Для этого нам надо завести роут подпадающий под действия фаерволлов и при этом откликающийся на любой (!) введенный адрес перехода.

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

namespace ProjectBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class DefaultController extends Controller
{
    //...

    /**
     * Данный роут перехватывает все переходы в системе, которые не охвачены другими роутами.
     * @Route("/{path}", name="_inner404Redirect")
     */
    public function inner404Redirect()
    {
        throw new NotFoundHttpException();
    }
}

Источник

Реклама
HowTo

Symfony2: обработка исключения 404

Допустим, что нам нужно обработать исключение 404 и вместо стандартного ответа сервера (или шаблона ошибки ответить специфической страницей.

Как кастомизировать страницы подробно описано в официальной документации.

Нас будет интересовать пункт работы с эвентом kernel.exception.

Можно посмотреть один из примеров его использования.

Наш кейс: если symfony не смог обработать роут, то управление передается своему обработчику.

namespace AppBundle\Listener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class ExceptionListener
{
    protected $templating;
    protected $kernel;

    public function __construct(EngineInterface $templating, $kernel)
    {
        $this->templating = $templating;
        $this->kernel = $kernel;
    }
    
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        if ($event->getException() instanceof NotFoundHttpException) {
            $response = new Response();
            $response->setContent("Какие-то данные");
            //$response->setStatusCode(200) работать не будет
            $response->headers->set('X-Status-Code', 200);
            $event->setResponse($response);
        }
    }
}

В config.yml прописывем что-то вроде

services:
    kernel.listener.app_exception_listener:
        class: AppBundle\Listener\ExceptionListener
        arguments: [@templating, @kernel]
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }

И обратите внимание, что переопределение статуса ответа в виде

$response->setStatusCode(200)

работать не будет. Мы не сможем переопределить статус отвта на 2хх с помощью этого метода (на любой другой можно).

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

$response->headers->set('X-Status-Code', 200);
Разработка

Symfony2, Doctrine2, Postgresql и кодировки

Суть проблемы: в doctrine2 нет возможности выбрать кодировку подключения для драйвера pdo_pgsql. Совсем никак. Нет. Даже не пытайтесь. У вас ничего не получится.

Вот незадача: в mysql есть опция драйвера pdo PDO::MYSQL_ATTR_INIT_COMMAND. Благодаря этой опции можно устанавливать кодировку подключения при помощи

set names 'utf8'

И даже драйвер mysql поддерживает установку кодировки при помощи опции charset в настройках подключения.

Если мы покопаемся в файле драйвера, то увидим, что кодировка исправно обрабатывается

<b>Doctrine\DBAL\Driver\PDOMySql\Driver</b>

    /**
     * Constructs the MySql PDO DSN.
     *
     * @param array $params
     *
     * @return string The DSN.
     */
    private function _constructPdoDsn(array $params)
    {
        $dsn = 'mysql:';
        if (isset($params['host']) && $params['host'] != '') {
            $dsn .= 'host=' . $params['host'] . ';';
        }
        if (isset($params['port'])) {
            $dsn .= 'port=' . $params['port'] . ';';
        }
        if (isset($params['dbname'])) {
            $dsn .= 'dbname=' . $params['dbname'] . ';';
        }
        if (isset($params['unix_socket'])) {
            $dsn .= 'unix_socket=' . $params['unix_socket'] . ';';
        }
        if (isset($params['charset'])) {
            $dsn .= 'charset=' . $params['charset'] . ';';
        }

        return $dsn;
    }

Для драйвера pdo_pgsql (Doctrine\DBAL\Driver\PDOPgSql\Driver) нет ничего подобного.

При этом сам драйвер вполне успешно с кодировками работает.

Однако, безвыходных ситуаций не бывает. Чтобы как-то изменить кодировку при работе с базой pgsql можно применять события symfony2. А конкретно событие postConnect из doctrine2.

Все, что нам потребуется — это реализовать собственный листенер этого события.

namespace DatabaseBundle\Event\Listeners;

use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use Doctrine\Common\EventSubscriber;

/**
 * Событие инициализации подключения pgsql.
 * Позволяет установить кодировку бд.
 */
class PgsqlConnectionInit implements EventSubscriber
{
    /**
     * Используемая кодировка
     *
     * @var string
     */
    private $_charset;

    /**
     * Конфигурирование кодировки при создании класса
     *
     * @param string         $charset   The charset.
     */
    public function __construct($charset = 'utf8')
    {
        $this->_charset = $charset;
    }

    /**
     * @param \Doctrine\DBAL\Event\ConnectionEventArgs $args
     *
     * @return void
     */
    public function postConnect(ConnectionEventArgs $args)
    {
        $args->getConnection()->executeQuery("SET NAMES ?", array($this->_charset));
    }

    /**
     * {@inheritdoc}
     */
    public function getSubscribedEvents()
    {
        return array(Events::postConnect);
    }
}

А затем подключить этот эвент в config.yml

services:
  pgsql.connection.init:
    class: DatabaseBundle\Event\Listeners\PgsqlConnectionInit
    tags:
      - { name: doctrine.event_listener, event: postConnect }

Теперь все ок 🙂

Разработка

Symfony2: cannot redeclarate class

Fatal error: include() [<a href="http://contoso.com/app/function.include">function.include</a>]: Cannot redeclare class symfony\bundle\frameworkbundle\frameworkbundle in /srv/www/contoso.com/vendor/composer/ClassLoader.php on line <i>412</i>

Да-да. Есть такая противная ошибка.

Она лечится либо отключением apc, либо установкой для него следующего набора опций

apc.include_once_override = 0
apc.canonicalize = 0
apc.stat = 0