Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • Net
  • None
  • PHP
  • system
    • base
    • caching
      • dependencies
    • collections
    • console
    • db
      • ar
      • schema
        • cubrid
        • mssql
        • mysql
        • oci
        • pgsql
        • sqlite
    • i18n
      • gettext
    • logging
    • test
    • utils
    • validators
    • web
      • actions
      • auth
      • filters
      • form
      • helpers
      • renderers
      • services
      • widgets
        • captcha
        • pagers
  • Text
    • Highlighter
  • zii
    • behaviors
    • widgets
      • grid
      • jui

Classes

  • BaseDocsMassAction
  • CApplication
  • CApplicationComponent
  • CBehavior
  • CComponent
  • CEnumerable
  • CErrorEvent
  • CErrorHandler
  • CEvent
  • CExceptionEvent
  • CModel
  • CModelBehavior
  • CModelEvent
  • CModule
  • CommonFieldsBehavior
  • CSecurityManager
  • CStatePersister
  • Expression
  • MassAction
  • MassAddToList
  • MassCompleteAction
  • MassMoveFileSysObjToFolder
  • MassRemoveFromList
  • MassRenameFileSysObj
  • MassUncompleteAction
  • MobileRecentItems
  • ModulePanelItem
  • NewListFromSelection
  • PanelItem
  • QuickCRUDBehavior
  • RecentItemPanelItem
  • ServiceRoutingBehavior
  • SettingsPanelItem
  • X2AddressBehavior
  • X2AuthCache
  • X2BaseListViewBehavior

Interfaces

  • IAction
  • IApplicationComponent
  • IAuthManager
  • IBehavior
  • IFilter
  • IStatePersister
  • IUserIdentity
  • IViewRenderer
  • IWebServiceProvider
  • IWebUser

Exceptions

  • CException
  • CHttpException
  • TwitterFeedWidgetException
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * This file contains the error handler application component.
  4:  *
  5:  * @author Qiang Xue <qiang.xue@gmail.com>
  6:  * @link http://www.yiiframework.com/
  7:  * @copyright 2008-2013 Yii Software LLC
  8:  * @license http://www.yiiframework.com/license/
  9:  */
 10: 
 11: Yii::import('CHtml',true);
 12: 
 13: /**
 14:  * CErrorHandler handles uncaught PHP errors and exceptions.
 15:  *
 16:  * It displays these errors using appropriate views based on the
 17:  * nature of the error and the mode the application runs at.
 18:  * It also chooses the most preferred language for displaying the error.
 19:  *
 20:  * CErrorHandler uses two sets of views:
 21:  * <ul>
 22:  * <li>development views, named as <code>exception.php</code>;
 23:  * <li>production views, named as <code>error&lt;StatusCode&gt;.php</code>;
 24:  * </ul>
 25:  * where &lt;StatusCode&gt; stands for the HTTP error code (e.g. error500.php).
 26:  * Localized views are named similarly but located under a subdirectory
 27:  * whose name is the language code (e.g. zh_cn/error500.php).
 28:  *
 29:  * Development views are displayed when the application is in debug mode
 30:  * (i.e. YII_DEBUG is defined as true). Detailed error information with source code
 31:  * are displayed in these views. Production views are meant to be shown
 32:  * to end-users and are used when the application is in production mode.
 33:  * For security reasons, they only display the error message without any
 34:  * sensitive information.
 35:  *
 36:  * CErrorHandler looks for the view templates from the following locations in order:
 37:  * <ol>
 38:  * <li><code>themes/ThemeName/views/system</code>: when a theme is active.</li>
 39:  * <li><code>protected/views/system</code></li>
 40:  * <li><code>framework/views</code></li>
 41:  * </ol>
 42:  * If the view is not found in a directory, it will be looked for in the next directory.
 43:  *
 44:  * The property {@link maxSourceLines} can be changed to specify the number
 45:  * of source code lines to be displayed in development views.
 46:  *
 47:  * CErrorHandler is a core application component that can be accessed via
 48:  * {@link CApplication::getErrorHandler()}.
 49:  *
 50:  * @property array $error The error details. Null if there is no error.
 51:  * @property Exception|null $exception exception instance. Null if there is no exception.
 52:  *
 53:  * @author Qiang Xue <qiang.xue@gmail.com>
 54:  * @package system.base
 55:  * @since 1.0
 56:  */
 57: class CErrorHandler extends CApplicationComponent
 58: {
 59:     /**
 60:      * @var integer maximum number of source code lines to be displayed. Defaults to 25.
 61:      */
 62:     public $maxSourceLines=25;
 63: 
 64:     /**
 65:      * @var integer maximum number of trace source code lines to be displayed. Defaults to 10.
 66:      * @since 1.1.6
 67:      */
 68:     public $maxTraceSourceLines = 10;
 69: 
 70:     /**
 71:      * @var string the application administrator information (could be a name or email link). It is displayed in error pages to end users. Defaults to 'the webmaster'.
 72:      */
 73:     public $adminInfo='the webmaster';
 74:     /**
 75:      * @var boolean whether to discard any existing page output before error display. Defaults to true.
 76:      */
 77:     public $discardOutput=true;
 78:     /**
 79:      * @var string the route (eg 'site/error') to the controller action that will be used to display external errors.
 80:      * Inside the action, it can retrieve the error information by Yii::app()->errorHandler->error.
 81:      * This property defaults to null, meaning CErrorHandler will handle the error display.
 82:      */
 83:     public $errorAction;
 84: 
 85:     private $_error;
 86:     private $_exception;
 87: 
 88:     /**
 89:      * Handles the exception/error event.
 90:      * This method is invoked by the application whenever it captures
 91:      * an exception or PHP error.
 92:      * @param CEvent $event the event containing the exception/error information
 93:      */
 94:     public function handle($event)
 95:     {
 96:         // set event as handled to prevent it from being handled by other event handlers
 97:         $event->handled=true;
 98: 
 99:         if($this->discardOutput)
100:         {
101:             $gzHandler=false;
102:             foreach(ob_list_handlers() as $h)
103:             {
104:                 if(strpos($h,'gzhandler')!==false)
105:                     $gzHandler=true;
106:             }
107:             // the following manual level counting is to deal with zlib.output_compression set to On
108:             // for an output buffer created by zlib.output_compression set to On ob_end_clean will fail
109:             for($level=ob_get_level();$level>0;--$level)
110:             {
111:                 if(!@ob_end_clean())
112:                     ob_clean();
113:             }
114:             // reset headers in case there was an ob_start("ob_gzhandler") before
115:             if($gzHandler && !headers_sent() && ob_list_handlers()===array())
116:             {
117:                 if(function_exists('header_remove')) // php >= 5.3
118:                 {
119:                     header_remove('Vary');
120:                     header_remove('Content-Encoding');
121:                 }
122:                 else
123:                 {
124:                     header('Vary:');
125:                     header('Content-Encoding:');
126:                 }
127:             }
128:         }
129: 
130:         if($event instanceof CExceptionEvent)
131:             $this->handleException($event->exception);
132:         else // CErrorEvent
133:             $this->handleError($event);
134:     }
135: 
136:     /**
137:      * Returns the details about the error that is currently being handled.
138:      * The error is returned in terms of an array, with the following information:
139:      * <ul>
140:      * <li>code - the HTTP status code (e.g. 403, 500)</li>
141:      * <li>type - the error type (e.g. 'CHttpException', 'PHP Error')</li>
142:      * <li>message - the error message</li>
143:      * <li>file - the name of the PHP script file where the error occurs</li>
144:      * <li>line - the line number of the code where the error occurs</li>
145:      * <li>trace - the call stack of the error</li>
146:      * <li>source - the context source code where the error occurs</li>
147:      * </ul>
148:      * @return array the error details. Null if there is no error.
149:      */
150:     public function getError()
151:     {
152:         return $this->_error;
153:     }
154: 
155:     /**
156:      * Returns the instance of the exception that is currently being handled.
157:      * @return Exception|null exception instance. Null if there is no exception.
158:      */
159:     public function getException()
160:     {
161:         return $this->_exception;
162:     }
163: 
164:     /**
165:      * Handles the exception.
166:      * @param Exception $exception the exception captured
167:      */
168:     protected function handleException($exception)
169:     {
170:         $app=Yii::app();
171:         if($app instanceof CWebApplication)
172:         {
173:             if(($trace=$this->getExactTrace($exception))===null)
174:             {
175:                 $fileName=$exception->getFile();
176:                 $errorLine=$exception->getLine();
177:             }
178:             else
179:             {
180:                 $fileName=$trace['file'];
181:                 $errorLine=$trace['line'];
182:             }
183: 
184:             $trace = $exception->getTrace();
185: 
186:             foreach($trace as $i=>$t)
187:             {
188:                 if(!isset($t['file']))
189:                     $trace[$i]['file']='unknown';
190: 
191:                 if(!isset($t['line']))
192:                     $trace[$i]['line']=0;
193: 
194:                 if(!isset($t['function']))
195:                     $trace[$i]['function']='unknown';
196: 
197:                 unset($trace[$i]['object']);
198:             }
199: 
200:             $this->_exception=$exception;
201:             $this->_error=$data=array(
202:                 'code'=>($exception instanceof CHttpException)?$exception->statusCode:500,
203:                 'type'=>get_class($exception),
204:                 'errorCode'=>$exception->getCode(),
205:                 'message'=>$exception->getMessage(),
206:                 'file'=>$fileName,
207:                 'line'=>$errorLine,
208:                 'trace'=>$exception->getTraceAsString(),
209:                 'traces'=>$trace,
210:             );
211: 
212:             if(!headers_sent())
213:             {
214:                 $httpVersion=Yii::app()->request->getHttpVersion();
215:                 header("HTTP/$httpVersion {$data['code']} ".$this->getHttpHeader($data['code'], get_class($exception)));
216:             }
217: 
218:             $this->renderException();
219:         }
220:         else
221:             $app->displayException($exception);
222:     }
223: 
224:     /**
225:      * Handles the PHP error.
226:      * @param CErrorEvent $event the PHP error event
227:      */
228:     protected function handleError($event)
229:     {
230:         $trace=debug_backtrace();
231:         // skip the first 3 stacks as they do not tell the error position
232:         if(count($trace)>3)
233:             $trace=array_slice($trace,3);
234:         $traceString='';
235:         foreach($trace as $i=>$t)
236:         {
237:             if(!isset($t['file']))
238:                 $trace[$i]['file']='unknown';
239: 
240:             if(!isset($t['line']))
241:                 $trace[$i]['line']=0;
242: 
243:             if(!isset($t['function']))
244:                 $trace[$i]['function']='unknown';
245: 
246:             $traceString.="#$i {$trace[$i]['file']}({$trace[$i]['line']}): ";
247:             if(isset($t['object']) && is_object($t['object']))
248:                 $traceString.=get_class($t['object']).'->';
249:             $traceString.="{$trace[$i]['function']}()\n";
250: 
251:             unset($trace[$i]['object']);
252:         }
253: 
254:         $app=Yii::app();
255:         if($app instanceof CWebApplication)
256:         {
257:             switch($event->code)
258:             {
259:                 case E_WARNING:
260:                     $type = 'PHP warning';
261:                     break;
262:                 case E_NOTICE:
263:                     $type = 'PHP notice';
264:                     break;
265:                 case E_USER_ERROR:
266:                     $type = 'User error';
267:                     break;
268:                 case E_USER_WARNING:
269:                     $type = 'User warning';
270:                     break;
271:                 case E_USER_NOTICE:
272:                     $type = 'User notice';
273:                     break;
274:                 case E_RECOVERABLE_ERROR:
275:                     $type = 'Recoverable error';
276:                     break;
277:                 default:
278:                     $type = 'PHP error';
279:             }
280:             $this->_exception=null;
281:             $this->_error=array(
282:                 'code'=>500,
283:                 'type'=>$type,
284:                 'message'=>$event->message,
285:                 'file'=>$event->file,
286:                 'line'=>$event->line,
287:                 'trace'=>$traceString,
288:                 'traces'=>$trace,
289:             );
290:             if(!headers_sent())
291:             {
292:                 $httpVersion=Yii::app()->request->getHttpVersion();
293:                 header("HTTP/$httpVersion 500 Internal Server Error");
294:             }
295: 
296:             $this->renderError();
297:         }
298:         else
299:             $app->displayError($event->code,$event->message,$event->file,$event->line);
300:     }
301: 
302:     /**
303:      * whether the current request is an AJAX (XMLHttpRequest) request.
304:      * @return boolean whether the current request is an AJAX request.
305:      */
306:     protected function isAjaxRequest()
307:     {
308:         return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';
309:     }
310: 
311:     /**
312:      * Returns the exact trace where the problem occurs.
313:      * @param Exception $exception the uncaught exception
314:      * @return array the exact trace where the problem occurs
315:      */
316:     protected function getExactTrace($exception)
317:     {
318:         $traces=$exception->getTrace();
319: 
320:         foreach($traces as $trace)
321:         {
322:             // property access exception
323:             if(isset($trace['function']) && ($trace['function']==='__get' || $trace['function']==='__set'))
324:                 return $trace;
325:         }
326:         return null;
327:     }
328: 
329:     /**
330:      * Renders the view.
331:      * @param string $view the view name (file name without extension).
332:      * See {@link getViewFile} for how a view file is located given its name.
333:      * @param array $data data to be passed to the view
334:      */
335:     protected function render($view,$data)
336:     {
337:         $data['version']=$this->getVersionInfo();
338:         $data['time']=time();
339:         $data['admin']=$this->adminInfo;
340:         include($this->getViewFile($view,$data['code']));
341:     }
342: 
343:     /**
344:      * Renders the exception information.
345:      * This method will display information from current {@link error} value.
346:      */
347:     protected function renderException()
348:     {
349:         $exception=$this->getException();
350:         if($exception instanceof CHttpException || !YII_DEBUG)
351:             $this->renderError();
352:         else
353:         {
354:             if($this->isAjaxRequest())
355:                 Yii::app()->displayException($exception);
356:             else
357:                 $this->render('exception',$this->getError());
358:         }
359:     }
360: 
361:     /**
362:      * Renders the current error information.
363:      * This method will display information from current {@link error} value.
364:      */
365:     protected function renderError()
366:     {
367:         if($this->errorAction!==null)
368:             Yii::app()->runController($this->errorAction);
369:         else
370:         {
371:             $data=$this->getError();
372:             if($this->isAjaxRequest())
373:                 Yii::app()->displayError($data['code'],$data['message'],$data['file'],$data['line']);
374:             elseif(YII_DEBUG)
375:                 $this->render('exception',$data);
376:             else
377:                 $this->render('error',$data);
378:         }
379:     }
380: 
381:     /**
382:      * Determines which view file should be used.
383:      * @param string $view view name (either 'exception' or 'error')
384:      * @param integer $code HTTP status code
385:      * @return string view file path
386:      */
387:     protected function getViewFile($view,$code)
388:     {
389:         $viewPaths=array(
390:             Yii::app()->getTheme()===null ? null :  Yii::app()->getTheme()->getSystemViewPath(),
391:             Yii::app() instanceof CWebApplication ? Yii::app()->getSystemViewPath() : null,
392:             YII_PATH.DIRECTORY_SEPARATOR.'views',
393:         );
394: 
395:         foreach($viewPaths as $i=>$viewPath)
396:         {
397:             if($viewPath!==null)
398:             {
399:                  $viewFile=$this->getViewFileInternal($viewPath,$view,$code,$i===2?'en_us':null);
400:                  if(is_file($viewFile))
401:                      return $viewFile;
402:             }
403:         }
404:     }
405: 
406:     /**
407:      * Looks for the view under the specified directory.
408:      * @param string $viewPath the directory containing the views
409:      * @param string $view view name (either 'exception' or 'error')
410:      * @param integer $code HTTP status code
411:      * @param string $srcLanguage the language that the view file is in
412:      * @return string view file path
413:      */
414:     protected function getViewFileInternal($viewPath,$view,$code,$srcLanguage=null)
415:     {
416:         $app=Yii::app();
417:         if($view==='error')
418:         {
419:             $viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR."error{$code}.php",$srcLanguage);
420:             if(!is_file($viewFile))
421:                 $viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR.'error.php',$srcLanguage);
422:         }
423:         else
424:             $viewFile=$viewPath.DIRECTORY_SEPARATOR."exception.php";
425:         return $viewFile;
426:     }
427: 
428:     /**
429:      * Returns server version information.
430:      * If the application is in production mode, empty string is returned.
431:      * @return string server version information. Empty if in production mode.
432:      */
433:     protected function getVersionInfo()
434:     {
435:         if(YII_DEBUG)
436:         {
437:             $version='<a href="http://www.yiiframework.com/">Yii Framework</a>/'.Yii::getVersion();
438:             if(isset($_SERVER['SERVER_SOFTWARE']))
439:                 $version=$_SERVER['SERVER_SOFTWARE'].' '.$version;
440:         }
441:         else
442:             $version='';
443:         return $version;
444:     }
445: 
446:     /**
447:      * Converts arguments array to its string representation
448:      *
449:      * @param array $args arguments array to be converted
450:      * @return string string representation of the arguments array
451:      */
452:     protected function argumentsToString($args)
453:     {
454:         $count=0;
455: 
456:         $isAssoc=$args!==array_values($args);
457: 
458:         foreach($args as $key => $value)
459:         {
460:             $count++;
461:             if($count>=5)
462:             {
463:                 if($count>5)
464:                     unset($args[$key]);
465:                 else
466:                     $args[$key]='...';
467:                 continue;
468:             }
469: 
470:             if(is_object($value))
471:                 $args[$key] = get_class($value);
472:             elseif(is_bool($value))
473:                 $args[$key] = $value ? 'true' : 'false';
474:             elseif(is_string($value))
475:             {
476:                 if(strlen($value)>64)
477:                     $args[$key] = '"'.substr($value,0,64).'..."';
478:                 else
479:                     $args[$key] = '"'.$value.'"';
480:             }
481:             elseif(is_array($value))
482:                 $args[$key] = 'array('.$this->argumentsToString($value).')';
483:             elseif($value===null)
484:                 $args[$key] = 'null';
485:             elseif(is_resource($value))
486:                 $args[$key] = 'resource';
487: 
488:             if(is_string($key))
489:             {
490:                 $args[$key] = '"'.$key.'" => '.$args[$key];
491:             }
492:             elseif($isAssoc)
493:             {
494:                 $args[$key] = $key.' => '.$args[$key];
495:             }
496:         }
497:         $out = implode(", ", $args);
498: 
499:         return $out;
500:     }
501: 
502:     /**
503:      * Returns a value indicating whether the call stack is from application code.
504:      * @param array $trace the trace data
505:      * @return boolean whether the call stack is from application code.
506:      */
507:     protected function isCoreCode($trace)
508:     {
509:         if(isset($trace['file']))
510:         {
511:             $systemPath=realpath(dirname(__FILE__).'/..');
512:             return $trace['file']==='unknown' || strpos(realpath($trace['file']),$systemPath.DIRECTORY_SEPARATOR)===0;
513:         }
514:         return false;
515:     }
516: 
517:     /**
518:      * Renders the source code around the error line.
519:      * @param string $file source file path
520:      * @param integer $errorLine the error line number
521:      * @param integer $maxLines maximum number of lines to display
522:      * @return string the rendering result
523:      */
524:     protected function renderSourceCode($file,$errorLine,$maxLines)
525:     {
526:         $errorLine--;   // adjust line number to 0-based from 1-based
527:         if($errorLine<0 || ($lines=@file($file))===false || ($lineCount=count($lines))<=$errorLine)
528:             return '';
529: 
530:         $halfLines=(int)($maxLines/2);
531:         $beginLine=$errorLine-$halfLines>0 ? $errorLine-$halfLines:0;
532:         $endLine=$errorLine+$halfLines<$lineCount?$errorLine+$halfLines:$lineCount-1;
533:         $lineNumberWidth=strlen($endLine+1);
534: 
535:         $output='';
536:         for($i=$beginLine;$i<=$endLine;++$i)
537:         {
538:             $isErrorLine = $i===$errorLine;
539:             $code=sprintf("<span class=\"ln".($isErrorLine?' error-ln':'')."\">%0{$lineNumberWidth}d</span> %s",$i+1,CHtml::encode(str_replace("\t",'    ',$lines[$i])));
540:             if(!$isErrorLine)
541:                 $output.=$code;
542:             else
543:                 $output.='<span class="error">'.$code.'</span>';
544:         }
545:         return '<div class="code"><pre>'.$output.'</pre></div>';
546:     }
547:     /**
548:      * Return correct message for each known http error code
549:      * @param integer $httpCode error code to map
550:      * @param string $replacement replacement error string that is returned if code is unknown
551:      * @return string the textual representation of the given error code or the replacement string if the error code is unknown
552:      */
553:     protected function getHttpHeader($httpCode, $replacement='')
554:     {
555:         $httpCodes = array(
556:             100 => 'Continue',
557:             101 => 'Switching Protocols',
558:             102 => 'Processing',
559:             118 => 'Connection timed out',
560:             200 => 'OK',
561:             201 => 'Created',
562:             202 => 'Accepted',
563:             203 => 'Non-Authoritative',
564:             204 => 'No Content',
565:             205 => 'Reset Content',
566:             206 => 'Partial Content',
567:             207 => 'Multi-Status',
568:             210 => 'Content Different',
569:             300 => 'Multiple Choices',
570:             301 => 'Moved Permanently',
571:             302 => 'Found',
572:             303 => 'See Other',
573:             304 => 'Not Modified',
574:             305 => 'Use Proxy',
575:             307 => 'Temporary Redirect',
576:             310 => 'Too many Redirect',
577:             400 => 'Bad Request',
578:             401 => 'Unauthorized',
579:             402 => 'Payment Required',
580:             403 => 'Forbidden',
581:             404 => 'Not Found',
582:             405 => 'Method Not Allowed',
583:             406 => 'Not Acceptable',
584:             407 => 'Proxy Authentication Required',
585:             408 => 'Request Time-out',
586:             409 => 'Conflict',
587:             410 => 'Gone',
588:             411 => 'Length Required',
589:             412 => 'Precondition Failed',
590:             413 => 'Request Entity Too Large',
591:             414 => 'Request-URI Too Long',
592:             415 => 'Unsupported Media Type',
593:             416 => 'Requested range unsatisfiable',
594:             417 => 'Expectation failed',
595:             418 => 'I’m a teapot',
596:             422 => 'Unprocessable entity',
597:             423 => 'Locked',
598:             424 => 'Method failure',
599:             425 => 'Unordered Collection',
600:             426 => 'Upgrade Required',
601:             449 => 'Retry With',
602:             450 => 'Blocked by Windows Parental Controls',
603:             500 => 'Internal Server Error',
604:             501 => 'Not Implemented',
605:             502 => 'Bad Gateway ou Proxy Error',
606:             503 => 'Service Unavailable',
607:             504 => 'Gateway Time-out',
608:             505 => 'HTTP Version not supported',
609:             507 => 'Insufficient storage',
610:             509 => 'Bandwidth Limit Exceeded',
611:         );
612:         if(isset($httpCodes[$httpCode]))
613:             return $httpCodes[$httpCode];
614:         else
615:             return $replacement;
616:     }
617: }
618: 
API documentation generated by ApiGen 2.8.0