Category Archives: код

Отладка проекта на продакшене

Бывают часто ситуации, когда после тестирования на деве обновляешь код на продакт сервере и внезапно оказывается что сайт не работает. При чем не всегда спасет и логирование ошибок. Фатал эроры к примеру туда не попадут.

Чаще всего это связано с нехваткой каких то модулей для php или чего угодно другого. Суть сводится к тому, что нужно включить вывод ошибок и посмотреть что происходит на сервере в момент ошибки.

Но для всех пользователей отображать ошибки конечно же неправильно, нужно как то отделить девелопера от остальных пользователей и показывать ошибки только ему.

В этом нам с легкостью поможет nginx (в апаче вроде можно сделать так же, но я им давно не пользуюсь).

Мы будем проверять наличие куки с определенным значением и если находим ее то выставляем переменную окружения APPLICATION_ENV в значение «development», а затем в коде по этому значению будем включать отображение ошибок.

Итак, редактируем файл /path/to/nginxconf/fastcgi_params (для freebsd это /usr/local/etc/nginx/fastcgi_params) и в конце дописываем:

set $appEnv 'production';
if ($http_cookie ~ "dev_cookie=secret_value" ) {
    set  $appEnv 'development';
}
fastcgi_param   APPLICATION_ENV  $appEnv;

Из этого понятно, что проверяться будет наличие куки с именем dev_cookie и значением secret_value. Конечно же для вашей реализации нужно изменить эти значения.

Установка переменной необходима потому как fastcgi_param нельзя использовать внутри if.

И после этого в коде используем такую небольшую конструкцию:

defined('APPLICATION_ENV') || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

if (APPLICATION_ENV == 'development')
{
	error_reporting(E_ALL ^ E_NOTICE);
	ini_set('display_errors',1);
}
else
{
	error_reporting(0);
	ini_set('display_errors',0);
}

И если где то в коде еще потребуется разделять логику для девелопера/обычных посетителей то можем в любом месте делать проверку

(APPLICATION_ENV == 'development')

 

Теперь, если вам нужно включить для себя отображение ошибок вы должны в браузере для нужного сайта создать куку с именем dev_cookie и значением secret_value и все. Когда закончите отладку можете просто удалить куку.

Мое решение работает сразу для всех проектов на сервере. Если вам нужно сделать данный функционал только для одного проекта или части сайта то вы можете перенести код в соответствующую секцию конфигурации.

ZendFramework, PoEdit и phtml файлы.

Одним из самых удобных способов локализации приложения, по моему мнению, является использование po/mo файлов с переводами, которые генерирует допотопная программа poedit, лучшей к сожалениею не написали :-(

Так вот, если вы прописали правильно пути к базе файлов которые нужно сканировать, ключевые слова выставлены верно, но программа не находит слова, которые находятся скажем в файлах с расширением отличных от стандартных (пример: php контент в файле *.phtml, что свойственно шаблонам вида в ZF).

Нашел вот здесь: http://web-blog.org.ua/articles/zend-framework-localizatsiya-proektov-chast-tretyaya хорошее описание и решение проблеммы. На всяк случай продублирую вкратце и у себя:

  1. Файл — Установки — Парсеры — PHP — Править
  2.  изменяем строку с расширениями на эту: *.php;*.phtml
  3.  изменяем строку с командой вызова на эту: xgettext —language=PHP —force-po -o %o %C %K %F

Подробнее в блоге по ссылке выше.

Zend Framework: разделение проекта на Core-Custom

Если вам нужно будет разделить проект на две части: ядро и кастом (например если многие части кода используются для нескольких проектов), то одним из вариантов решения такой задачи будет использовать ядро как и раньше, а все что отличается веносить в папки типа Project_custom/application|configs|models и так далее.

Так же важно что бы файлы подключались сразу из cutom папки, и только если не находились там, то брались с ядра.

Для моделей это легко делается правильной настройкой аутолоадера, а точнее даже просто прописать include_paths в правильном порядке.

А вот с контроллерами было сложнее. В конфиге одному модулю можно задать только один путь. А вешать кастомные контроллеры на отдельный модуль в итоге не давало бы возможности перезаписывать стандартные контроллеры.

В общем по итогу у меня родился такой вот изящный результат. Пишем плагин.

class Plugin_CustomController extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$front = Zend_Controller_Front::getInstance();
$dispatcher = $front->getDispatcher();
// здесь указываем путь к кастом контроллерам
$dispatcher->setControllerDirectory(APPLICATION_CUSTOM_PATH.’/controllers’);
if (!$dispatcher->isDispatchable($request))
$dispatcher->setControllerDirectory(APPLICATION_PATH.’/controllers’);   // а здесь контроллеры ядра
}
}

И в бутстрапе подключаем плагин:

Zend_Controller_Front::getInstance()->registerPlugin(new Plugin_CustomController());

Так же еще сходу не знал как сделать возможность перебивания layout-ов, но для них все оказалось еще проще — просто в конфиге проекта указать следующее:

‘resources’ => array(

‘layout’            => array(‘layoutPath’ => array(APPLICATION_PATH.’/layouts/scripts/’, APPLICATION_CUSTOM_PATH.’/layouts/’)),

)

При чем что важно, подключаться файлы будут в порядке, обратном указанном.

Так само указывается путь для подключения скриптов вида:

array(‘resources’ => array(

‘view’ => array(
‘scriptPath’ => array(APPLICATION_PATH.’/views/scripts/’,APPLICATION_CUSTOM_PATH.’/views/scripts/’),
));

UPD: как оказалось, переделать автозагрузчик оказалось не тривиальной задачей. Не буду писать всего, скажу как в итоге сделать правильно. Код правда здесь приводить не могу.

  1. создать один массив resourceTypes с описанием неймспейсов и путей к папкам нужным;
  2. создать два Zend_Loader_Autoloader_Resource в первом путь basePath задать к кастомной части, во втором к core файлам, resourceTypes в этих автозагрузчиках поставить одинаковым;
  3. для Zend_Loader_Autoloader сделать поочередно pushAutoloader сначала для кастом части, а затем для ядра;
  4. правильность подключения файлов без заковыристых namespace-ов обеспечивается указанием includePaths  в правильной последовательности: сначала путь к кастом части, затем к ядру.

Note: не забудьте убрать затем все include/require — это поломает логику правильных автолоадов файлов =)

xDebug и xdebug.profiler_enable_trigger = 1

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

Решил я пропрофилировать свой проектик, поставил xdebug. Но включать профилирование для всех страниц это просто собирать мусор, нашел опцию xdebug.profiler_enable_trigger которая позволяет включать профилирование по параметру xdebug_profile в гет/посте/сессии либо можно поставить расширение для хдебаг и тогда это вообще будет кнопочкой включаться/выключаться.

Так вот, согластно документации http://xdebug.org/docs/profiler

In order for the trigger to work properly, xdebug.profiler_enable needs to be set to 0.

И я долго не мог понять, почему же все таки оно не работает как надо, а в итоге оказалось что для корректной работы нужно xdebug.profiler_enable как раз в 1 выставить. Тогда для всех страниц профилирования не будет, а для тех что с триггером — будет.

Такие пироги, доки иногда тоже вводят в заблуждение.

Трудноуловимая ошибка с Zend_Loader_Autoloader::$_instance и доступом к контантам класса.

Продолжаю серию увлекательных особенностей ZF и PHP в общем.

Итак, если по неизвестной закономерности у вас возникает ошибка Access to undeclared static property: Zend_Loader_Autoloader::$_instance или же происходит Fatal error с ошибкой «обращение к несуществующей переменной» (что то типа Fatal error: Access to undeclared static property: Zend_Loader_Autoloader::$_instance in) , а в коде это выглядит типа как self::MYCONST то эта запись для вас.

В общем написал я недавно класс Zend_Session_SaveHandler_Memcached что бы использовать его как сторэдж для сохранения сессий.

И поначалу вроде как все работало нормально, но потом по неизвестной закономерности начала появляться ошибка что скрипт обращается к несуществующей переменной. Естественно, константа в классе объявлена. Замена self на Zend_Session_SaveHandler_Memcached результата не дало, ровно как и обновление пхп со всеми модулями. При это еще после того как скрипт меняется, страница грузится нормально, без ошибок, а последующие загрузки вызывают эту ошибку.

И еще более загадочно, без установленной закономерности, раз на сколько то вызовов появляется ошибка, озвученная выше.

В итоге установил что проблема в параметре apc.cache_by_default который задается в php.ini и который был включен на этом сервере, а на других (на которых и ошибки этой не было) данного парамера пхпинфо вообще не возвращал. Установка параметра в 0 соответственно решило проблему.

Такой день.

Так же вариант менее варварский, но мной лично не проверенный: http://stackoverflow.com/questions/7749198/getting-apc-to-play-nice-with-spl-autoload-register за ссылку спасибо Роману Яровому из комментариев.