winter/modules/cms/classes/CmsException.php

237 lines
7.4 KiB
PHP
Raw Normal View History

2014-05-14 23:24:20 +10:00
<?php namespace Cms\Classes;
use File;
use Twig_Error;
use October\Rain\Exception\ApplicationException;
use October\Rain\Halcyon\Processors\SectionParser;
2014-05-14 23:24:20 +10:00
use Exception;
/**
* The CMS exception class.
* The exception class handles CMS related errors. Allows the masking of other exception types which
* uses actual source CMS files -- instead of cached files -- for their error content.
*
* @package october\cms
* @author Alexey Bobkov, Samuel Georges
*/
class CmsException extends ApplicationException
{
/**
2016-03-29 14:55:25 +02:00
* @var \Cms\Classes\CmsCompoundObject A reference to a CMS object used for masking errors.
2014-05-14 23:24:20 +10:00
*/
2014-08-01 18:20:55 +10:00
protected $compoundObject;
2014-05-14 23:24:20 +10:00
/**
* @var array Collection of error codes for each error distinction.
*/
2016-03-29 14:55:25 +02:00
protected static $errorCodes = [
2014-05-14 23:24:20 +10:00
100 => 'General',
200 => 'INI Settings',
300 => 'PHP Content',
400 => 'Twig Template'
2016-03-29 14:55:25 +02:00
];
2014-05-14 23:24:20 +10:00
/**
* Creates the CMS exception object.
* @param mixed $message The message to display as a string, or a CmsCompoundObject that is used
* for using this exception as a mask for another exception type.
* @param int $code Error code to specify the exception type:
* Error 100: A general exception.
* Error 200: Mask the exception as INI content.
* Error 300: Mask the exception as PHP content.
* Error 400: Mask the exception as Twig content.
* @param \Exception $previous Previous exception.
*/
public function __construct($message = null, $code = 100, Exception $previous = null)
{
if ($message instanceof CmsCompoundObject || $message instanceof ComponentPartial) {
2014-05-14 23:24:20 +10:00
$this->compoundObject = $message;
$message = '';
}
2014-10-11 01:22:03 +02:00
if (isset(static::$errorCodes[$code])) {
2014-05-14 23:24:20 +10:00
$this->errorType = static::$errorCodes[$code];
2014-10-11 01:22:03 +02:00
}
2014-05-14 23:24:20 +10:00
parent::__construct($message, $code, $previous);
}
/**
2014-05-17 18:08:01 +02:00
* Checks some conditions to confirm error has actually occurred
2014-05-14 23:24:20 +10:00
* due to the CMS template code, not some external code. If the error
2014-05-17 18:08:01 +02:00
* has occurred in external code, the function will return false. Otherwise return
2014-05-14 23:24:20 +10:00
* true and modify the exception by overriding it's content, line and message values
* to be accurate against a CMS object properties.
* @param \Exception $exception The exception to modify.
* @return bool
*/
public function processCompoundObject(Exception $exception)
{
switch ($this->code) {
case 200:
$result = $this->processIni($exception);
break;
case 300:
$result = $this->processPhp($exception);
break;
case 400:
$result = $this->processTwig($exception);
break;
}
if ($result !== false) {
$this->file = $this->compoundObject->getFilePath();
2014-05-14 23:24:20 +10:00
if (File::isFile($this->file) && is_readable($this->file)) {
$this->fileContent = @file($this->file);
}
}
return $result;
}
/**
* Override properties of an exception specific to the INI section
* of a CMS object.
* @param \Exception $exception The exception to modify.
2016-03-30 18:17:18 +02:00
* @return bool
2014-05-14 23:24:20 +10:00
*/
protected function processIni(Exception $exception)
{
$message = $exception->getMessage();
/*
* Expecting: syntax error, unexpected '!' in Unknown on line 4
*/
2014-10-11 01:22:03 +02:00
if (!starts_with($message, 'syntax error')) {
return false;
}
if (strpos($message, 'Unknown') === false) {
return false;
}
if (strpos($exception->getFile(), 'Ini.php') === false) {
2014-10-11 01:22:03 +02:00
return false;
}
2014-05-14 23:24:20 +10:00
/*
* Line number from parse_ini_string() error.
* The last word should contain the line number.
*/
$parts = explode(' ', $message);
$line = array_pop($parts);
$this->line = (int)$line;
// Find where the ini settings section begins
$offsetArray = SectionParser::parseOffset($this->compoundObject->getContent());
$this->line += $offsetArray['settings'];
$this->message = $message;
// Account for line 0
$this->line--;
return true;
}
/**
* Override properties of an exception specific to the PHP section
* of a CMS object.
* @param \Exception $exception The exception to modify.
2016-03-30 18:17:18 +02:00
* @return bool
2014-05-14 23:24:20 +10:00
*/
protected function processPhp(Exception $exception)
{
/*
* Fatal Error
*/
if ($exception instanceof \Symfony\Component\Debug\Exception\FatalErrorException) {
$check = false;
// Expected: */modules/cms/classes/CodeParser.php(165) : eval()'d code line 7
2014-10-11 01:22:03 +02:00
if (strpos($exception->getFile(), 'CodeParser.php')) {
$check = true;
}
2014-05-14 23:24:20 +10:00
2015-02-12 21:04:05 +11:00
// Expected: */storage/cms/cache/39/05/home.htm.php
2014-10-11 01:22:03 +02:00
if (strpos($exception->getFile(), $this->compoundObject->getFileName() . '.php')) {
$check = true;
}
2014-05-14 23:24:20 +10:00
2014-10-11 01:22:03 +02:00
if (!$check) {
2014-05-14 23:24:20 +10:00
return false;
2014-10-11 01:22:03 +02:00
}
2014-05-14 23:24:20 +10:00
/*
2014-05-17 18:08:01 +02:00
* Errors occurring the PHP code base class (Cms\Classes\CodeBase)
2014-05-14 23:24:20 +10:00
*/
}
else {
2014-05-14 23:24:20 +10:00
$trace = $exception->getTrace();
if (isset($trace[1]['class'])) {
$class = $trace[1]['class'];
2014-10-11 01:22:03 +02:00
if (!is_subclass_of($class, 'Cms\Classes\CodeBase')) {
2014-05-14 23:24:20 +10:00
return false;
2014-10-11 01:22:03 +02:00
}
2014-05-14 23:24:20 +10:00
}
}
$this->message = $exception->getMessage();
// Offset the php, namespace and bracket tags from the generated class.
$this->line = $exception->getLine() - 3;
// Find where the php code section begins
$offsetArray = SectionParser::parseOffset($this->compoundObject->getContent());
$this->line += $offsetArray['code'];
// Account for line 0
$this->line--;
return true;
}
/**
* Override properties of an exception specific to the Twig section
* of a CMS object.
* @param \Exception $exception The exception to modify.
* @return bool
2014-05-14 23:24:20 +10:00
*/
protected function processTwig(Exception $exception)
{
// Must be a Twig related exception
2014-10-11 01:22:03 +02:00
if (!$exception instanceof Twig_Error) {
2014-05-14 23:24:20 +10:00
return false;
2014-10-11 01:22:03 +02:00
}
2014-05-14 23:24:20 +10:00
$this->message = $exception->getRawMessage();
$this->line = $exception->getTemplateLine();
// Find where the twig markup section begins
$offsetArray = SectionParser::parseOffset($this->compoundObject->getContent());
$this->line += $offsetArray['markup'];
// Account for line 0
$this->line--;
return true;
}
/**
* Masks this exception with the details of the supplied. The error code for
* this exception object will determine how the supplied exception is used.
* Error 100: A general exception. Inherits \System\Classes\ExceptionBase::applyMask()
* Error 200: Mask the exception as INI content.
* Error 300: Mask the exception as PHP content.
* Error 400: Mask the exception as Twig content.
* @param \Exception $exception The exception to modify.
* @return void
*/
public function applyMask(Exception $exception)
{
if ($this->code == 100 || $this->processCompoundObject($exception) === false) {
parent::applyMask($exception);
return;
}
}
2014-10-11 01:22:03 +02:00
}