hhvm вместе с yii-1.x
27.07.2015Несмотря на то, что yii-1.x по результатам тестов на 98% совместим с hhvm, существуют несколько ситуаций в которых код не работает или работает не по плану.
Одна из таких ситуаций - это белая страница (white page) в случае возникновения не обработанного исключения. Другими словами любое CHttpException (страница не найдена) приведет к фатальной ошибке с белой страницей. Все несовместимость заключается в схеме обработки ошибок в классе CApplication, а именно в методах handleException и handleError (если быть точней в использовании try-catch в этих методах).
Структура фреймворка позволяет выключить встроенные методы обработки ошибок c помощью констант YII_ENABLE_EXCEPTION_HANDLER и YII_ENABLE_ERROR_HANDLER. С помощью данных констант возможно выключить или включить стандартные методы обработки ошибок и исключений в ядре. Выключим эти обработчики и определим свои.
Итак, создадим класс Hhvm.php в protected.components:
<?php
/**
* How to prevent "white page" on CHttpException and other errors
*
* Instruction:
* 1. place Hhvm.php to protected.components
* 2. add to index.php lines:
*
* define('YII_DEBUG', false);
* define('YII_ENABLE_EXCEPTION_HANDLER', false); // disable yii exception handler
* define('YII_ENABLE_ERROR_HANDLER', false); // disable yii error handler
*
*
* //////
* ///// <------ config and other code here
* /////
*
* // init YII
* $app = Yii::createWebApplication($config);
*
* // add hhvm handlers
* Hhvm::initSystemHandlers();
* $app->run();
*
* 3. proffit
*
*
* @see Hhvm::initSystemHandlers
* @author hcbogdan
*/
class Hhvm
{
public static function handleException($exception)
{
// disable error capturing to avoid recursive errors
restore_error_handler();
restore_exception_handler();
$category='exception.'.get_class($exception);
if($exception instanceof CHttpException)
$category.='.'.$exception->statusCode;
// php <5.2 doesn't support string conversion auto-magically
$message=$exception->__toString();
if(isset($_SERVER['REQUEST_URI']))
$message.="\nREQUEST_URI=".$_SERVER['REQUEST_URI'];
if(isset($_SERVER['HTTP_REFERER']))
$message.="\nHTTP_REFERER=".$_SERVER['HTTP_REFERER'];
$message.="\n---";
Yii::log($message,CLogger::LEVEL_ERROR,$category);
$event=new CExceptionEvent(app(),$exception);
app()->onException($event);
if(!$event->handled)
{
// try an error handler
if(($handler=app()->getErrorHandler())!==null)
$handler->handle($event);
else
app()->displayException($exception);
}
}
public static function handleError($code,$message,$file,$line)
{
if($code & error_reporting())
{
// disable error capturing to avoid recursive errors
restore_error_handler();
restore_exception_handler();
$log="$message ($file:$line)\nStack trace:\n";
$trace=debug_backtrace();
// skip the first 3 stacks as they do not tell the error position
if(count($trace)>3)
$trace=array_slice($trace,3);
foreach($trace as $i=>$t)
{
if(!isset($t['file']))
$t['file']='unknown';
if(!isset($t['line']))
$t['line']=0;
if(!isset($t['function']))
$t['function']='unknown';
$log.="#$i {$t['file']}({$t['line']}): ";
if(isset($t['object']) && is_object($t['object']))
$log.=get_class($t['object']).'->';
$log.="{$t['function']}()\n";
}
if(isset($_SERVER['REQUEST_URI']))
$log.='REQUEST_URI='.$_SERVER['REQUEST_URI'];
Yii::log($log,CLogger::LEVEL_ERROR,'php');
Yii::import('CErrorEvent',true);
$event=new CErrorEvent(app(),$code,$message,$file,$line);
app()->onError($event);
if(!$event->handled)
{
// try an error handler
if(($handler=app()->getErrorHandler())!==null)
$handler->handle($event);
else
app()->displayError($code,$message,$file,$line);
}
}
}
/**
* Initializes the error handlers.
*/
public static function initSystemHandlers()
{
set_exception_handler('Hhvm::handleException');
if(YII_DEBUG)
set_error_handler('Hhvm::handleError', error_reporting());
}
}
Далее модифицируем index.php - переключим обработку ошибок в наш класс, который учитываем возможности виртуальной машины:
<?php
define('YII_DEBUG', false);
//
// Выключаем старые обработчики
//
define('YII_ENABLE_EXCEPTION_HANDLER', false);
define('YII_ENABLE_ERROR_HANDLER', false);
$autoload = PROJECT_ROOT.'/vendor/autoload.php';
$config = PROJECT_ROOT.'/protected/config/main.php';
$yii = PROJECT_ROOT.'/vendor/yiisoft/yii/framework/yii.php';
require_once($yii);
require_once($autoload);
$app = Yii::createWebApplication($config);
Hhvm::initSystemHandlers(); // <-------- обработка ошибок
$app->run();
Вот собственно и весь хак который позволит совместить обработку ошибок в ядре yii с особенностями hhvm.
Комментарии