Overview

Namespaces

  • None
  • Wei
    • Validator

Classes

  • Wei\Validator\All
  • Wei\Validator\AllOf
  • Wei\Validator\Alnum
  • Wei\Validator\Alpha
  • Wei\Validator\BaseValidator
  • Wei\Validator\Between
  • Wei\Validator\Blank
  • Wei\Validator\Callback
  • Wei\Validator\CharLength
  • Wei\Validator\Chinese
  • Wei\Validator\Color
  • Wei\Validator\Contains
  • Wei\Validator\CreditCard
  • Wei\Validator\Date
  • Wei\Validator\DateTime
  • Wei\Validator\Decimal
  • Wei\Validator\Digit
  • Wei\Validator\Dir
  • Wei\Validator\DivisibleBy
  • Wei\Validator\DoubleByte
  • Wei\Validator\Email
  • Wei\Validator\EndsWith
  • Wei\Validator\EqualTo
  • Wei\Validator\Exists
  • Wei\Validator\FieldExists
  • Wei\Validator\File
  • Wei\Validator\GreaterThan
  • Wei\Validator\GreaterThanOrEqual
  • Wei\Validator\IdCardCn
  • Wei\Validator\IdCardHk
  • Wei\Validator\IdCardMo
  • Wei\Validator\IdCardTw
  • Wei\Validator\IdenticalTo
  • Wei\Validator\Image
  • Wei\Validator\In
  • Wei\Validator\Ip
  • Wei\Validator\Length
  • Wei\Validator\LessThan
  • Wei\Validator\LessThanOrEqual
  • Wei\Validator\Lowercase
  • Wei\Validator\Luhn
  • Wei\Validator\MaxLength
  • Wei\Validator\MinLength
  • Wei\Validator\MobileCn
  • Wei\Validator\NaturalNumber
  • Wei\Validator\NoneOf
  • Wei\Validator\Null
  • Wei\Validator\Number
  • Wei\Validator\OneOf
  • Wei\Validator\Password
  • Wei\Validator\Phone
  • Wei\Validator\PhoneCn
  • Wei\Validator\PlateNumberCn
  • Wei\Validator\PositiveInteger
  • Wei\Validator\PostcodeCn
  • Wei\Validator\Present
  • Wei\Validator\QQ
  • Wei\Validator\RecordExists
  • Wei\Validator\Regex
  • Wei\Validator\Required
  • Wei\Validator\SomeOf
  • Wei\Validator\StartsWith
  • Wei\Validator\Time
  • Wei\Validator\Tld
  • Wei\Validator\Type
  • Wei\Validator\Uppercase
  • Wei\Validator\Url
  • Wei\Validator\Uuid
  • Overview
  • Namespace
  • Function
  1: <?php
  2: /**
  3:  * Wei Framework
  4:  *
  5:  * @copyright   Copyright (c) 2008-2013 Twin Huang
  6:  * @license     http://opensource.org/licenses/mit-license.php MIT License
  7:  */
  8: 
  9: namespace Wei;
 10: 
 11: /**
 12:  * A service that handles exception and display pretty exception message
 13:  *
 14:  * @property    Logger $logger The logger wei
 15:  * @property    Response $response The HTTP response wei
 16:  */
 17: class Error extends Base
 18: {
 19:     /**
 20:      * The default error message display when debug is not enable
 21:      *
 22:      * @var string
 23:      */
 24:     protected $message = 'Error';
 25: 
 26:     /**
 27:      * The detail error message display when debug is not enable
 28:      *
 29:      * @var string
 30:      */
 31:     protected $detail = 'Unfortunately, an error occurred. Please try again later.';
 32: 
 33:     /**
 34:      * The detail error message display when thrown 404 exception
 35:      *
 36:      * @var string
 37:      */
 38:     protected $notFoundDetail = 'Sorry, the page you requested was not found. Please check the URL and try again.';
 39: 
 40:     /**
 41:      * Whether ignore the previous exception handler or attach it again to the
 42:      * exception event
 43:      *
 44:      * @var bool
 45:      */
 46:     protected $ignorePrevHandler = false;
 47: 
 48:     /**
 49:      * The previous exception handler
 50:      *
 51:      * @var null|callback
 52:      */
 53:     protected $prevExceptionHandler;
 54: 
 55:     /**
 56:      * The custom error handlers
 57:      *
 58:      * @var array
 59:      */
 60:     protected $handlers = array(
 61:         'error'     => array(),
 62:         'fatal'     => array(),
 63:         'notFound'  => array()
 64:     );
 65: 
 66:     /**
 67:      * Constructor
 68:      *
 69:      * @param array $options
 70:      */
 71:     public function __construct($options = array())
 72:     {
 73:         parent::__construct($options);
 74: 
 75:         $this->registerErrorHandler();
 76:         $this->registerExceptionHandler();
 77:         $this->registerFatalHandler();
 78:     }
 79: 
 80:     /**
 81:      * Attach a handler to exception error
 82:      *
 83:      * @param callback $fn The error handler
 84:      * @return $this
 85:      */
 86:     public function __invoke($fn)
 87:     {
 88:         $this->handlers['error'][] = $fn;
 89:         return $this;
 90:     }
 91: 
 92:     /**
 93:      * Attach a handler to not found error
 94:      *
 95:      * @param callable $fn The error handler
 96:      * @return $this
 97:      */
 98:     public function notFound($fn)
 99:     {
100:         $this->handlers['notFound'][] = $fn;
101:         return $this;
102:     }
103: 
104:     /**
105:      * Attach a handler to fatal error
106:      *
107:      * @param callable $fn The error handler
108:      * @return $this
109:      */
110:     public function fatal($fn)
111:     {
112:         $this->handlers['fatal'][] = $fn;
113:         return $this;
114:     }
115: 
116:     /**
117:      * Register exception Handler
118:      */
119:     protected function registerExceptionHandler()
120:     {
121:         $this->prevExceptionHandler = set_exception_handler(array($this, 'handleException'));
122:     }
123: 
124:     /**
125:      * Register error Handler
126:      */
127:     protected function registerErrorHandler()
128:     {
129:         set_error_handler(array($this, 'handleError'));
130:     }
131: 
132:     /**
133:      * Detect fatal error and register fatal handler
134:      */
135:     protected function registerFatalHandler()
136:     {
137:         $error = $this;
138: 
139:         // When shutdown, the current working directory will be set to the web
140:         // server directory, store it for later use
141:         $cwd = getcwd();
142: 
143:         register_shutdown_function(function() use($error, $cwd) {
144:             $e = error_get_last();
145:             if (!$e || !in_array($e['type'], array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) {
146:                 // No error or not fatal error
147:                 return;
148:             }
149: 
150:             ob_get_length() && ob_end_clean();
151: 
152:             // Reset the current working directory to make sure everything work as usual
153:             chdir($cwd);
154: 
155:             $exception = new \ErrorException($e['message'], $e['type'], 0, $e['file'], $e['line']);
156: 
157:             if ($error->triggerHandler('fatal', $exception)) {
158:                 // Handled!
159:                 return;
160:             }
161: 
162:             // Fallback to error handlers
163:             if ($error->triggerHandler('error', $exception)) {
164:                 // Handled!
165:                 return;
166:             }
167: 
168:             // Fallback to internal error Handlers
169:             $error->internalHandleException($exception);
170:         });
171:     }
172: 
173:     /**
174:      * Trigger a error handler
175:      *
176:      * @param string $type The type of error handlers
177:      * @param \Exception $exception
178:      * @return bool
179:      */
180:     public function triggerHandler($type, \Exception $exception)
181:     {
182:         foreach ($this->handlers[$type] as $handler) {
183:             $result = call_user_func_array($handler, array($exception, $this->wei));
184:             if (true === $result) {
185:                 return true;
186:             }
187:         }
188:         return false;
189:     }
190: 
191:     /**
192:      * The exception handler to render pretty message
193:      *
194:      * @param \Exception $exception
195:      */
196:     public function handleException(\Exception $exception)
197:     {
198:         if (!$this->ignorePrevHandler && $this->prevExceptionHandler) {
199:             call_user_func($this->prevExceptionHandler, $exception);
200:         }
201: 
202:         if (404 == $exception->getCode()) {
203:             if ($this->triggerHandler('notFound', $exception)) {
204:                 return;
205:             }
206:         }
207: 
208:         if (!$this->triggerHandler('error', $exception)) {
209:             $this->internalHandleException($exception);
210:         }
211: 
212:         restore_exception_handler();
213:     }
214: 
215:     public function internalHandleException(\Exception $exception)
216:     {
217:         $debug = $this->wei->isDebug();
218:         $code = $exception->getCode();
219: 
220:         // HTTP status code
221:         if ($code < 100 || $code > 600) {
222:             $code = 500;
223:         }
224: 
225:         // Logger level
226:         if ($code >= 500) {
227:             $level = 'critical';
228:         } else {
229:             $level = 'info';
230:         }
231: 
232:         try {
233:             // The flowing services may throw exception too
234:             $this->response->setStatusCode($code)->send();
235:             $this->logger->log($level, $exception);
236: 
237:             $this->renderException($exception, $debug);
238:         } catch (\Exception $e) {
239:             $this->renderException($e, $debug);
240:         }
241:     }
242: 
243:     /**
244:      * Render exception message
245:      *
246:      * @param \Exception $e
247:      * @param bool $debug Whether show debug trace
248:      */
249:     public function renderException(\Exception $e, $debug)
250:     {
251:         $code = $e->getCode();
252:         $file = $e->getFile();
253:         $line = $e->getLine();
254: 
255:         // Prepare message
256:         if ($debug) {
257:             $message = $e->getMessage();
258:             $detail = sprintf('Threw by %s in %s on line %s', get_class($e), $file, $line);
259:         } else {
260:             $message = $this->message;
261:             $detail = $this->detail;
262:         }
263: 
264:         if ($code == 404) {
265:             $message = $e->getMessage();
266:             if (!$debug) {
267:                 $detail = $this->notFoundDetail;
268:             }
269:         }
270:         $title = htmlspecialchars($message, ENT_QUOTES);
271:         $message = nl2br($title);
272: 
273:         // Render error views
274:         if ($debug) {
275:             $fileInfo   = $this->getFileCode($file, $line);
276:             $trace      = htmlspecialchars($e->getTraceAsString(), ENT_QUOTES);
277: 
278:             $detail = "<h2>File</h2>"
279:                 . "<p class=\"error-text\">$file</p>"
280:                 . "<p><pre>$fileInfo</pre></p>"
281:                 . "<h2>Trace</h2>"
282:                 . "<p class=\"error-text\">$detail</p>"
283:                 . "<p><pre>$trace</pre></p>";
284:         } else {
285:             $detail = "<p>$detail</p>";
286:         }
287: 
288:         $html = '<!DOCTYPE html>'
289:             . '<html>'
290:             . '<head>'
291:             . '<meta name="viewport" content="width=device-width">'
292:             . '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>'
293:             . "<title>$title</title>"
294:             . '<style type="text/css">'
295:             . 'body { font-size: 12px; color: #333; padding: 15px 20px 20px 20px; }'
296:             . 'h1, h2, p, pre { margin: 0; padding: 0; }'
297:             . 'body, pre { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif, "\5fae\8f6f\96c5\9ed1", "\5b8b\4f53"; }'
298:             . 'h1 { font-size: 36px; }'
299:             . 'h2 { font-size: 20px; margin: 20px 0 0; }'
300:             . 'pre { line-height: 18px; }'
301:             . 'strong, .error-text { color: #FF3000; }'
302:             . '</style>'
303:             . '</head>'
304:             . '<body>'
305:             . "<h1>$message</h1>"
306:             . $detail
307:             . '</body>'
308:             . '</html>';
309: 
310:         echo $html;
311:     }
312: 
313:     /**
314:      * The error handler convert PHP error to exception
315:      *
316:      * @param int $code The level of the error raised
317:      * @param string $message The error message
318:      * @param string $file The filename that the error was raised in
319:      * @param int $line The line number the error was raised at
320:      * @throws \ErrorException convert PHP error to exception
321:      * @internal use for set_error_handler only
322:      */
323:     public function handleError($code, $message, $file, $line)
324:     {
325:         if (!(error_reporting() & $code)) {
326:             // This error code is not included in error_reporting
327:             return;
328:         }
329:         restore_error_handler();
330:         throw new \ErrorException($message, $code, 500, $file, $line);
331:     }
332: 
333:     /**
334:      * Get file code in specified range
335:      *
336:      * @param  string $file  The file name
337:      * @param  int    $line  The file line
338:      * @param  int    $range The line range
339:      * @return string
340:      */
341:     public function getFileCode($file, $line, $range = 20)
342:     {
343:         $code = file($file);
344:         $half = (int) ($range / 2);
345: 
346:         $start = $line - $half;
347:         0 > $start && $start = 0;
348: 
349:         $total = count($code);
350:         $end = $line + $half;
351:         $total < $end && $end = $total;
352: 
353:         $len = strlen($end);
354: 
355:         array_unshift($code, null);
356:         $content = '';
357:         for ($i = $start; $i < $end; $i++) {
358:             $temp = str_pad($i, $len, 0, STR_PAD_LEFT) . ':  ' . $code[$i];
359:             if ($line != $i) {
360:                 $content .= htmlspecialchars($temp, ENT_QUOTES);
361:             } else {
362:                 $content .= '<strong>' . htmlspecialchars($temp, ENT_QUOTES) . '</strong>';
363:             }
364:         }
365: 
366:         return $content;
367:     }
368: }
369: 
Wei Framework API documentation generated by ApiGen