diff --git a/_config/logging.yml b/_config/logging.yml new file mode 100644 index 000000000..557d1de26 --- /dev/null +++ b/_config/logging.yml @@ -0,0 +1,45 @@ +--- +Name: logging +--- +Injector: + ErrorHandler: + class: SilverStripe\Framework\Logging\MonologErrorHandler + properties: + Logger: %$Logger + Logger: + type: singleton + class: Monolog\Logger + constructor: + - "error-log" + calls: + DisplayErrorHandler: [ pushHandler, [ %$DisplayErrorHandler ] ] + +--- +Name: dev-logging +Only: + environment: dev +--- +Injector: + DisplayErrorHandler: + class: SilverStripe\Framework\Logging\HTTPOutputHandler + constructor: + - "notice" + properties: + Formatter: %$SilverStripe\Framework\Logging\DetailedErrorFormatter +--- +Name: live-logging +Except: + environment: dev +--- +Injector: + DisplayErrorHandler: + class: SilverStripe\Framework\Logging\HTTPOutputHandler + constructor: + - "error" + properties: + Formatter: %$FriendlyErrorFormatter + FriendlyErrorFormatter: + class: SilverStripe\Framework\Logging\DebugViewFriendlyErrorFormatter + properties: + Title: "There has been an error" + Body: "The website server has not been able to respond to your request" diff --git a/composer.json b/composer.json index 83c28849d..55c4a1e07 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ ], "require": { "php": ">=5.4.0", - "composer/installers": "~1.0" + "composer/installers": "~1.0", + "monolog/monolog": "~1.11" }, "require-dev": { "phpunit/PHPUnit": "~3.7" diff --git a/conf/ConfigureFromEnv.php b/conf/ConfigureFromEnv.php index 2542150f0..2a6d44ba9 100644 --- a/conf/ConfigureFromEnv.php +++ b/conf/ConfigureFromEnv.php @@ -50,6 +50,9 @@ * @subpackage core */ +use Monolog\Logger; +use Monolog\Handler\StreamHandler; + /* * _ss_environment.php handler */ @@ -139,7 +142,12 @@ if(defined('SS_USE_BASIC_AUTH') && SS_USE_BASIC_AUTH) { } if(defined('SS_ERROR_LOG')) { - SS_Log::add_writer(new SS_LogFileWriter(BASE_PATH . '/' . SS_ERROR_LOG), SS_Log::WARN, '<='); + $logger = Injector::inst()->get('Logger'); + if($logger instanceof Logger) { + $logger->pushHandler(new StreamHandler(BASE_PATH . '/' . SS_ERROR_LOG, Logger::WARN)); + } else { + user_error("SS_ERROR_LOG setting only works with Monolog, you are using another logger", E_USER_WARNING); + } } // Allow database adapters to handle their own configuration diff --git a/control/HTTPResponse.php b/control/HTTPResponse.php index 40d1db497..14e04d0c9 100644 --- a/control/HTTPResponse.php +++ b/control/HTTPResponse.php @@ -271,7 +271,10 @@ EOT // an error, and the response doesn't have any body yet that might contain // a more specific error description. if(Director::isLive() && $this->isError() && !$this->body) { - Debug::friendlyError($this->statusCode, $this->getStatusDescription()); + $formatter = Injector::get('FriendlyErrorFormatter'); + $formatter->setStatusCode($this->statusCode); + echo $formatter->format(array()); + } else { echo $this->body; } diff --git a/core/Core.php b/core/Core.php index 756941a1c..05c8c7615 100644 --- a/core/Core.php +++ b/core/Core.php @@ -70,9 +70,8 @@ require_once 'view/TemplateGlobalProvider.php'; require_once 'control/Director.php'; require_once 'dev/Debug.php'; require_once 'dev/DebugView.php'; +require_once 'dev/CliDebugView.php'; require_once 'dev/Backtrace.php'; -require_once 'dev/ZendLog.php'; -require_once 'dev/Log.php'; require_once 'filesystem/FileFinder.php'; require_once 'core/manifest/ManifestCache.php'; require_once 'core/manifest/ClassLoader.php'; @@ -136,8 +135,9 @@ if(Director::isLive()) { /** * Load error handlers */ -Debug::loadErrorHandlers(); +$errorHandler = Injector::inst()->get('ErrorHandler'); +$errorHandler->start(); /////////////////////////////////////////////////////////////////////////////// // HELPER FUNCTIONS diff --git a/dev/CliDebugView.php b/dev/CliDebugView.php index bec31e511..c7ee869d8 100644 --- a/dev/CliDebugView.php +++ b/dev/CliDebugView.php @@ -9,49 +9,60 @@ * @todo Perhaps DebugView should be an interface / ABC, implemented by HTMLDebugView and CliDebugView? */ -class CliDebugView extends DebugView { +class CliDebugView extends DebugView +{ /** * Render HTML header for development views */ - public function writeHeader($httpRequest = null) { + public function renderHeader($httpRequest = null) { } /** * Render HTML footer for development views */ - public function writeFooter() { + public function renderFooter() { } /** * Write information about the error to the screen */ - public function writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext) { - $errorType = self::$error_types[$errno]; - echo SS_Cli::text("ERROR [" . $errorType['title'] . "]: $errstr\nIN $httpRequest\n", "red", null, true); - echo SS_Cli::text("Line $errline in $errfile\n\n", "red"); + public function renderError($httpRequest, $errno, $errstr, $errfile, $errline) { + if(!isset(self::$error_types[$errno])) { + $errorTypeTitle = "UNKNOWN TYPE, ERRNO $errno"; + } else { + $errorTypeTitle = self::$error_types[$errno]['title']; + } + $output = SS_Cli::text("ERROR [" . $errorTypeTitle . "]: $errstr\nIN $httpRequest\n", "red", null, true); + $output .= SS_Cli::text("Line $errline in $errfile\n\n", "red"); + + return $output; } /** * Write a fragment of the a source file * @param $lines An array of file lines; the keys should be the original line numbers */ - public function writeSourceFragment($lines, $errline) { - echo "Source\n======\n"; + public function renderSourceFragment($lines, $errline) { + $output = "Source\n======\n"; foreach($lines as $offset => $line) { - echo ($offset == $errline) ? "* " : " "; - echo str_pad("$offset:",5); - echo wordwrap($line, self::config()->columns, "\n "); + $output .= ($offset == $errline) ? "* " : " "; + $output .= str_pad("$offset:", 5); + $output .= wordwrap($line, self::config()->columns, "\n "); } - echo "\n"; + $output .= "\n"; + + return $output; } /** * Write a backtrace */ - public function writeTrace($trace = null) { - echo "Trace\n=====\n"; - echo SS_Backtrace::get_rendered_backtrace($trace ? $trace : debug_backtrace(), true); + public function renderTrace($trace = null) { + $output = "Trace\n=====\n"; + $output .= SS_Backtrace::get_rendered_backtrace($trace ? $trace : debug_backtrace(), true); + + return $output; } /** @@ -60,26 +71,30 @@ class CliDebugView extends DebugView { * @param string $title * @param string $title */ - public function writeInfo($title, $subtitle, $description=false) { - echo wordwrap(strtoupper($title),self::config()->columns) . "\n"; - echo wordwrap($subtitle,self::config()->columns) . "\n"; - echo str_repeat('-',min(self::config()->columns,max(strlen($title),strlen($subtitle)))) . "\n"; - echo wordwrap($description,self::config()->columns) . "\n\n"; + public function renderInfo($title, $subtitle, $description=false) { + $output = wordwrap(strtoupper($title), self::config()->columns) . "\n"; + $output .= wordwrap($subtitle, self::config()->columns) . "\n"; + $output .= str_repeat('-', min(self::config()->columns, max(strlen($title), strlen($subtitle)))) . "\n"; + $output .= wordwrap($description, self::config()->columns) . "\n\n"; + + return $output; } - public function writeVariable($val, $caller) { - echo PHP_EOL; - echo SS_Cli::text(str_repeat('=', self::config()->columns), 'green'); - echo PHP_EOL; - echo SS_Cli::text($this->formatCaller($caller), 'blue', null, true); - echo PHP_EOL.PHP_EOL; + public function renderVariable($val, $caller) { + $output = PHP_EOL; + $output .= SS_Cli::text(str_repeat('=', self::config()->columns), 'green'); + $output .= PHP_EOL; + $output .= SS_Cli::text($this->formatCaller($caller), 'blue', null, true); + $output .= PHP_EOL.PHP_EOL; if (is_string($val)) { - print_r(wordwrap($val, self::config()->columns)); + $output .= wordwrap($val, self::config()->columns); } else { - print_r($val); + $output .= var_export($val, true); } - echo PHP_EOL; - echo SS_Cli::text(str_repeat('=', self::config()->columns), 'green'); - echo PHP_EOL; + $output .= PHP_EOL; + $output .= SS_Cli::text(str_repeat('=', self::config()->columns), 'green'); + $output .= PHP_EOL; + + return $output; } } diff --git a/dev/Debug.php b/dev/Debug.php index 3c7480775..dae6ddf56 100644 --- a/dev/Debug.php +++ b/dev/Debug.php @@ -1,4 +1,5 @@ $errno, - 'errstr' => $errstr, - 'errfile' => $errfile, - 'errline' => $errline, - 'errcontext' => $errcontext - ), - SS_Log::NOTICE - ); - - if(Director::isDev()) { - return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Notice"); - } else { - return false; - } - } - - /** - * Handle a non-fatal warning error thrown by PHP interpreter. - * - * @param unknown_type $errno - * @param unknown_type $errstr - * @param unknown_type $errfile - * @param unknown_type $errline - * @param unknown_type $errcontext - */ - public static function warningHandler($errno, $errstr, $errfile, $errline, $errcontext) { - if(error_reporting() == 0) return; - ini_set('display_errors', 0); - - // Send out the error details to the logger for writing - SS_Log::log( - array( - 'errno' => $errno, - 'errstr' => $errstr, - 'errfile' => $errfile, - 'errline' => $errline, - 'errcontext' => $errcontext - ), - SS_Log::WARN - ); - - if(Director::isDev()) { - return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Warning"); - } else { - return false; - } - } - - /** - * Handle a fatal error, depending on the mode of the site (ie: Dev, Test, or Live). - * - * Runtime execution dies immediately once the error is generated. - * - * @param unknown_type $errno - * @param unknown_type $errstr - * @param unknown_type $errfile - * @param unknown_type $errline - * @param unknown_type $errcontext - */ - public static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) { - ini_set('display_errors', 0); - - // Send out the error details to the logger for writing - SS_Log::log( - array( - 'errno' => $errno, - 'errstr' => $errstr, - 'errfile' => $errfile, - 'errline' => $errline, - 'errcontext' => $errcontext - ), - SS_Log::ERR - ); - - if(Director::isDev() || Director::is_cli()) { - self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Error"); - } else { - self::friendlyError(); - } - return false; - } - - /** - * Render a user-facing error page, using the default HTML error template - * rendered by {@link ErrorPage} if it exists. Doesn't use the standard {@link SS_HTTPResponse} class - * the keep dependencies minimal. - * - * @uses ErrorPage - * - * @param int $statusCode HTTP Status Code (Default: 500) - * @param string $friendlyErrorMessage User-focused error message. Should not contain code pointers - * or "tech-speak". Used in the HTTP Header and ajax responses. - * @param string $friendlyErrorDetail Detailed user-focused message. Is just used if no {@link ErrorPage} is found - * for this specific status code. - * @return string HTML error message for non-ajax requests, plaintext for ajax-request. - */ - public static function friendlyError($statusCode=500, $friendlyErrorMessage=null, $friendlyErrorDetail=null) { - if(!$friendlyErrorMessage) { - $friendlyErrorMessage = Config::inst()->get('Debug', 'friendly_error_header'); - } - - if(!$friendlyErrorDetail) { - $friendlyErrorDetail = Config::inst()->get('Debug', 'friendly_error_detail'); - } - - if(!headers_sent()) { - // Ensure the error message complies with the HTTP 1.1 spec - $msg = strip_tags(str_replace(array("\n", "\r"), '', $friendlyErrorMessage)); - if(Controller::has_curr()) { - $response = Controller::curr()->getResponse(); - $response->setStatusCode($statusCode, $msg); - } else { - header($_SERVER['SERVER_PROTOCOL'] . " $statusCode $msg"); - } - } - - if(Director::is_ajax()) { - echo $friendlyErrorMessage; - } else { - if(class_exists('ErrorPage')){ - $errorFilePath = ErrorPage::get_filepath_for_errorcode( - $statusCode, - class_exists('Translatable') ? Translatable::get_current_locale() : null - ); - if(file_exists($errorFilePath)) { - $content = file_get_contents($errorFilePath); - if(!headers_sent()) header('Content-Type: text/html'); - // $BaseURL is left dynamic in error-###.html, so that multi-domain sites don't get broken - echo str_replace('$BaseURL', Director::absoluteBaseURL(), $content); - } - } else { - $renderer = new DebugView(); - $renderer->writeHeader(); - $renderer->writeInfo("Website Error", $friendlyErrorMessage, $friendlyErrorDetail); - - if(Email::config()->admin_email) { - $mailto = Email::obfuscate(Email::config()->admin_email); - $renderer->writeParagraph('Contact an administrator: ' . $mailto . ''); - } - - $renderer->writeFooter(); - } - } - return false; - } - /** * Create an instance of an appropriate DebugView object. * @@ -379,84 +160,6 @@ class Debug { return Injector::inst()->get($service); } - /** - * Render a developer facing error page, showing the stack trace and details - * of the code where the error occured. - * - * @param unknown_type $errno - * @param unknown_type $errstr - * @param unknown_type $errfile - * @param unknown_type $errline - * @param unknown_type $errcontext - */ - public static function showError($errno, $errstr, $errfile, $errline, $errcontext, $errtype) { - if(!headers_sent()) { - $errText = "$errtype at line $errline of $errfile"; - $errText = str_replace(array("\n","\r")," ",$errText); - - if(!headers_sent()) header($_SERVER['SERVER_PROTOCOL'] . " 500 $errText"); - - // if error is displayed through ajax with CliDebugView, use plaintext output - if(Director::is_ajax()) { - header('Content-Type: text/plain'); - } - } - - $reporter = self::create_debug_view(); - - // Coupling alert: This relies on knowledge of how the director gets its URL, it could be improved. - $httpRequest = null; - if(isset($_SERVER['REQUEST_URI'])) { - $httpRequest = $_SERVER['REQUEST_URI']; - } elseif(isset($_REQUEST['url'])) { - $httpRequest = $_REQUEST['url']; - } - if(isset($_SERVER['REQUEST_METHOD'])) $httpRequest = $_SERVER['REQUEST_METHOD'] . ' ' . $httpRequest; - - $reporter->writeHeader($httpRequest); - $reporter->writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext); - - if(file_exists($errfile)) { - $lines = file($errfile); - - // Make the array 1-based - array_unshift($lines,""); - unset($lines[0]); - - $offset = $errline-10; - $lines = array_slice($lines, $offset, 16, true); - $reporter->writeSourceFragment($lines, $errline); - } - $reporter->writeTrace(($errcontext ? $errcontext : debug_backtrace())); - $reporter->writeFooter(); - return false; - } - - /** - * Utility method to render a snippet of PHP source code, from selected file - * and highlighting the given line number. - * - * @param string $errfile - * @param int $errline - */ - public static function showLines($errfile, $errline) { - $lines = file($errfile); - $offset = $errline-10; - $lines = array_slice($lines, $offset, 16); - echo '
';
-		$offset++;
-		foreach($lines as $line) {
-			$line = htmlentities($line, ENT_COMPAT, 'UTF-8');
-			if ($offset == $errline) {
-				echo "$offset $line";
-			} else {
-				echo "$offset $line";
-			}
-			$offset++;
-		}
-		echo '
'; - } - /** * Check if the user has permissions to run URL debug tools, * else redirect them to log in. @@ -501,65 +204,3 @@ class Debug { die(); } } - - - - - - - - - - -/** - * Generic callback, to catch uncaught exceptions when they bubble up to the top of the call chain. - * - * @ignore - * @param Exception $exception - */ -function exceptionHandler($exception) { - $errno = E_USER_ERROR; - $type = get_class($exception); - $message = "Uncaught " . $type . ": " . $exception->getMessage(); - $file = $exception->getFile(); - $line = $exception->getLine(); - $context = $exception->getTrace(); - Debug::fatalHandler($errno, $message, $file, $line, $context); - exit(1); -} - -/** - * Generic callback to catch standard PHP runtime errors thrown by the interpreter - * or manually triggered with the user_error function. Any unknown error codes are treated as - * fatal errors. - * Caution: The error levels default to E_ALL if the site is in dev-mode (set in main.php). - * - * @ignore - * @param int $errno - * @param string $errstr - * @param string $errfile - * @param int $errline - */ -function errorHandler($errno, $errstr, $errfile, $errline) { - switch($errno) { - case E_NOTICE: - case E_USER_NOTICE: - case E_DEPRECATED: - case E_USER_DEPRECATED: - case E_STRICT: - return Debug::noticeHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); - - case E_WARNING: - case E_CORE_WARNING: - case E_USER_WARNING: - case E_RECOVERABLE_ERROR: - return Debug::warningHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); - - case E_ERROR: - case E_CORE_ERROR: - case E_USER_ERROR: - default: - Debug::fatalHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); - exit(1); - } -} diff --git a/dev/DebugView.php b/dev/DebugView.php index 3ee580917..5a2c5e7c4 100644 --- a/dev/DebugView.php +++ b/dev/DebugView.php @@ -11,7 +11,8 @@ * @package framework * @subpackage dev */ -class DebugView extends Object { +class DebugView extends Object +{ /** * Column size to wrap long strings to @@ -22,6 +23,38 @@ class DebugView extends Object { private static $columns = 100; protected static $error_types = array( + 0 => array( + 'title' => 'Emergency', + 'class' => 'error' + ), + 1 => array( + 'title' => 'Alert', + 'class' => 'error' + ), + 2 => array( + 'title' => 'Critical', + 'class' => 'error' + ), + 3 => array( + 'title' => 'Error', + 'class' => 'error' + ), + 4 => array( + 'title' => 'Warning', + 'class' => 'warning' + ), + 5 => array( + 'title' => 'Notice', + 'class' => 'notice' + ), + 6 => array( + 'title' => 'Information', + 'class' => 'info' + ), + 7=> array( + 'title' => 'Debug', + 'class' => 'debug' + ), E_USER_ERROR => array( 'title' => 'User Error', 'class' => 'error' @@ -100,9 +133,66 @@ class DebugView extends Object { } /** - * Render HTML header for development views + * @deprecated 4.0.0:5.0.0 Use renderHeader() instead */ public function writeHeader() { + Deprecation::notice('4.0', 'Use renderHeader() instead'); + echo $this->renderHeader(); + } + + /** + * @deprecated 4.0.0:5.0.0 Use renderInfo() instead + */ + public function writeInfo($title, $subtitle, $description=false) { + Deprecation::notice('4.0', 'Use renderInfo() instead'); + echo $this->renderInfo($title, $subtitle, $description); + } + + /** + * @deprecated 4.0.0:5.0.0 Use renderFooter() instead + */ + public function writeFooter() { + Deprecation::notice('4.0', 'Use renderFooter() instead'); + echo $this->renderFooter(); + } + + /** + * @deprecated 4.0.0:5.0.0 Use renderError() instead + */ + public function writeError($httpRequest, $errno, $errstr, $errfile, $errline) { + Deprecation::notice('4.0', 'Use renderError() instead'); + echo $this->renderError($httpRequest, $errno, $errstr, $errfile, $errline); + } + + /** + * @deprecated 4.0.0:5.0.0 Use renderSourceFragment() instead + */ + public function writeSourceFragment($lines, $errline) { + Deprecation::notice('4.0', 'Use renderSourceFragment() instead'); + echo $this->renderSourceFragment($lines, $errline); + } + + /** + * @deprecated 4.0.0:5.0.0 Use renderTrace() instead + */ + public function writeTrace($trace) { + Deprecation::notice('4.0', 'Use renderTrace() instead'); + echo $this->renderTrace($trace); + } + + /** + * @deprecated 4.0.0:5.0.0 Use renderVariable() instead + */ + public function writeVariable($val, $caller) { + Deprecation::notice('4.0', 'Use renderVariable() instead'); + echo $this->renderVariable($val, $caller); + } + + /** + * Render HTML header for development views + * @return string + */ + public function renderHeader() { $url = htmlentities( $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI'], ENT_COMPAT, @@ -115,41 +205,55 @@ class DebugView extends Object { 'css/debug.css' ); - echo '' . $url . ''; - echo ''; - echo ''; - echo ''; + $output = '' . $url . ''; + $output .= ''; + $output .= ''; + $output .= ''; + + return $output; } /** * Render the information header for the view * - * @param string $title - * @param string $title + * @param string $title The main title + * @param string $subtitle The subtitle + * @param string|false $description The description to show + * @return string */ - public function writeInfo($title, $subtitle, $description=false) { - echo '
'; - echo "

" . Convert::raw2xml($title) . "

"; - if($subtitle) echo "

" . Convert::raw2xml($subtitle) . "

"; + public function renderInfo($title, $subtitle, $description=false) { + $output = '
'; + $output .= "

" . Convert::raw2xml($title) . "

"; + if($subtitle) $output .= "

" . Convert::raw2xml($subtitle) . "

"; if ($description) { - echo "

$description

"; + $output .= "

$description

"; } else { - echo $this->Breadcrumbs(); + $output .= $this->Breadcrumbs(); } - echo '
'; + $output .= '
'; + + return $output; } /** * Render HTML footer for development views + * @return string */ - public function writeFooter() { - echo ""; + public function renderFooter() { + return ""; } /** - * Write information about the error to the screen + * Render an error. + * + * @param string $httpRequest the kind of request + * @param int $errno Codenumber of the error + * @param string $errstr The error message + * @param string $errfile The name of the soruce code file where the error occurred + * @param int $errline The line number on which the error occured + * @return string */ - public function writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext) { + public function renderError($httpRequest, $errno, $errstr, $errfile, $errline) { $errorType = isset(self::$error_types[$errno]) ? self::$error_types[$errno] : self::$unknown_error; $httpRequestEnt = htmlentities($httpRequest, ENT_COMPAT, 'UTF-8'); if (ini_get('html_errors')) { @@ -157,51 +261,66 @@ class DebugView extends Object { } else { $errstr = Convert::raw2xml($errstr); } - echo '
'; - echo "

[" . $errorType['title'] . '] ' . $errstr . "

"; - echo "

$httpRequestEnt

"; - echo "

Line $errline in $errfile

"; - echo '
'; + $output = '
'; + $output .= "

[" . $errorType['title'] . '] ' . $errstr . "

"; + $output .= "

$httpRequestEnt

"; + $output .= "

Line $errline in $errfile

"; + $output .= '
'; + + return $output; } /** - * Write a fragment of the a source file - * @param $lines An array of file lines; the keys should be the original line numbers + * Render a fragment of the a source file + * + * @param array $lines An array of file lines; the keys should be the original line numbers + * @param int errLine The line of the error + * @return string */ - public function writeSourceFragment($lines, $errline) { - echo '

Source

'; - echo '
';
+	public function renderSourceFragment($lines, $errline) {
+		$output = '

Source

'; + $output .= '
';
 		foreach($lines as $offset => $line) {
 			$line = htmlentities($line, ENT_COMPAT, 'UTF-8');
 			if ($offset == $errline) {
-				echo "$offset $line";
+				$output .= "$offset $line";
 			} else {
-				echo "$offset $line";
+				$output .= "$offset $line";
 			}
 		}
-		echo '
'; + $output .= '
'; + + return $output; } /** - * Write a backtrace + * Render a call track + * + * @param array $trace The debug_backtrace() array + * @return string */ - public function writeTrace($trace) { - echo '

Trace

'; - echo SS_Backtrace::get_rendered_backtrace($trace); - echo '
'; + public function renderTrace($trace) { + $output = '

Trace

'; + $output .= SS_Backtrace::get_rendered_backtrace($trace); + $output .= ''; + + return $output; } /** - * @param string $text + * Render an arbitrary paragraph. + * + * @param string $text The HTML-escaped text to render + * @return string */ - public function writeParagraph($text) { - echo '

' . $text . '

'; + public function renderParagraph($text) { + return '

' . $text . '

'; } /** * Formats the caller of a method * - * @param array $caller + * @param array $caller * @return string */ protected function formatCaller($caller) { @@ -215,14 +334,17 @@ class DebugView extends Object { /** * Outputs a variable in a user presentable way * - * @param object $val - * @param array $caller Caller information + * @param object $val + * @param array $caller Caller information + * @return string */ - public function writeVariable($val, $caller) { - echo '
';
-		echo "" . $this->formatCaller($caller). " - \n";
-		if (is_string($val)) print_r(wordwrap($val, self::config()->columns));
-		else print_r($val);
-		echo '
'; + public function renderVariable($val, $caller) { + $output = '
';
+		$output .= "" . $this->formatCaller($caller). " - \n";
+		if (is_string($val)) $output .= wordwrap($val, self::config()->columns);
+		else $output .= var_export($val, true);
+		$output .= '
'; + + return $output; } } diff --git a/dev/Log.php b/dev/Log.php deleted file mode 100644 index fe0c0ecaa..000000000 --- a/dev/Log.php +++ /dev/null @@ -1,185 +0,0 @@ - - * SS_Log::add_writer(new SS_LogEmailWriter('my@email.com'), SS_Log::ERR); - * - * - * Example usage of logging errors by file: - * - * SS_Log::add_writer(new SS_LogFileWriter('/var/log/silverstripe/errors.log'), SS_Log::ERR); - * - * - * Example usage of logging at warnings and errors by setting the priority to '<=': - * - * SS_Log::add_writer(new SS_LogEmailWriter('my@email.com'), SS_Log::WARN, '<='); - * - * - * Each writer object can be assigned a formatter. The formatter is - * responsible for formatting the message before giving it to the writer. - * {@link SS_LogErrorEmailFormatter} is such an example that formats errors - * into HTML for human readability in an email client. - * - * Formatters are added to writers like this: - * - * $logEmailWriter = new SS_LogEmailWriter('my@email.com'); - * $myEmailFormatter = new MyLogEmailFormatter(); - * $logEmailWriter->setFormatter($myEmailFormatter); - * - * - * @package framework - * @subpackage dev - */ -class SS_Log { - - const ERR = Zend_Log::ERR; - const WARN = Zend_Log::WARN; - const NOTICE = Zend_Log::NOTICE; - const INFO = Zend_Log::INFO; - const DEBUG = Zend_Log::DEBUG; - - /** - * Logger class to use. - * @see SS_Log::get_logger() - * @var string - */ - public static $logger_class = 'SS_ZendLog'; - - /** - * @see SS_Log::get_logger() - * @var object - */ - protected static $logger; - - /** - * @var array Logs additional context from PHP's superglobals. - * Caution: Depends on logger implementation (mainly targeted at {@link SS_LogEmailWriter}). - * @see http://framework.zend.com/manual/en/zend.log.overview.html#zend.log.overview.understanding-fields - */ - protected static $log_globals = array( - '_SERVER' => array( - 'HTTP_ACCEPT', - 'HTTP_ACCEPT_CHARSET', - 'HTTP_ACCEPT_ENCODING', - 'HTTP_ACCEPT_LANGUAGE', - 'HTTP_REFERRER', - 'HTTP_USER_AGENT', - 'HTTPS', - 'REMOTE_ADDR', - ), - ); - - /** - * Get the logger currently in use, or create a new one if it doesn't exist. - * - * @return object - */ - public static function get_logger() { - if(!static::$logger) { - // Create default logger - static::$logger = new static::$logger_class; - - // Add default context (shouldn't change until the actual log event happens) - foreach(static::$log_globals as $globalName => $keys) { - foreach($keys as $key) { - $val = isset($GLOBALS[$globalName][$key]) ? $GLOBALS[$globalName][$key] : null; - static::$logger->setEventItem(sprintf('$%s[\'%s\']', $globalName, $key), $val); - } - } - - } - return static::$logger; - } - - /** - * Get all writers in use by the logger. - * @return array Collection of Zend_Log_Writer_Abstract instances - */ - public static function get_writers() { - return static::get_logger()->getWriters(); - } - - /** - * Remove all writers currently in use. - */ - public static function clear_writers() { - static::get_logger()->clearWriters(); - } - - /** - * Remove a writer instance from the logger. - * @param object $writer Zend_Log_Writer_Abstract instance - */ - public static function remove_writer($writer) { - static::get_logger()->removeWriter($writer); - } - - /** - * Add a writer instance to the logger. - * @param object $writer Zend_Log_Writer_Abstract instance - * @param const $priority Priority. Possible values: SS_Log::ERR, SS_Log::WARN or SS_Log::NOTICE - * @param $comparison Priority comparison operator. Acts on the integer values of the error - * levels, where more serious errors are lower numbers. By default this is "=", which means only - * the given priority will be logged. Set to "<=" if you want to track errors of *at least* - * the given priority. - */ - public static function add_writer($writer, $priority = null, $comparison = '=') { - if($priority) $writer->addFilter(new Zend_Log_Filter_Priority($priority, $comparison)); - static::get_logger()->addWriter($writer); - } - - /** - * Dispatch a message by priority level. - * - * The message parameter can be either a string (a simple error - * message), or an array of variables. The latter is useful for passing - * along a list of debug information for the writer to handle, such as - * error code, error line, error context (backtrace). - * - * @param mixed $message Exception object or array of error context variables - * @param const $priority Priority. Possible values: SS_Log::ERR, SS_Log::WARN or SS_Log::NOTICE - * @param mixed $extras Extra information to log in event - */ - public static function log($message, $priority, $extras = null) { - if($message instanceof Exception) { - $message = array( - 'errno' => '', - 'errstr' => $message->getMessage(), - 'errfile' => $message->getFile(), - 'errline' => $message->getLine(), - 'errcontext' => $message->getTrace() - ); - } elseif(is_string($message)) { - $trace = SS_Backtrace::filtered_backtrace(); - $lastTrace = $trace[0]; - $message = array( - 'errno' => '', - 'errstr' => $message, - 'errfile' => isset($lastTrace['file']) ? $lastTrace['file'] : null, - 'errline' => isset($lastTrace['line']) ? $lastTrace['line'] : null, - 'errcontext' => $trace - ); - } - try { - static::get_logger()->log($message, $priority, $extras); - } catch(Exception $e) { - // @todo How do we handle exceptions thrown from Zend_Log? - // For example, an exception is thrown if no writers are added - } - } - -} diff --git a/dev/LogEmailWriter.php b/dev/LogEmailWriter.php deleted file mode 100644 index 6a4d81b17..000000000 --- a/dev/LogEmailWriter.php +++ /dev/null @@ -1,94 +0,0 @@ -emailAddress = $emailAddress; - $this->customSmtpServer = $customSmtpServer; - } - - public static function factory($emailAddress, $customSmtpServer = false) { - return new SS_LogEmailWriter($emailAddress, $customSmtpServer); - } - - /** - * @deprecated 4.0 Use the "SS_LogEmailWriter.send_from" config setting instead - */ - public static function set_send_from($address) { - Deprecation::notice('4.0', 'Use the "SS_LogEmailWriter.send_from" config setting instead'); - Config::inst()->update('SS_LogEmailWriter', 'send_from', $address); - } - - /** - * @deprecated 4.0 Use the "SS_LogEmailWriter.send_from" config setting instead - */ - public static function get_send_from() { - Deprecation::notice('4.0', 'Use the "SS_LogEmailWriter.send_from" config setting instead'); - return Config::inst()->get('SS_LogEmailWriter', 'send_from'); - } - - /** - * Send an email to the email address set in - * this writer. - */ - public function _write($event) { - // If no formatter set up, use the default - if(!$this->_formatter) { - $formatter = new SS_LogErrorEmailFormatter(); - $this->setFormatter($formatter); - } - - $formattedData = $this->_formatter->format($event); - $subject = $formattedData['subject']; - $data = $formattedData['data']; - $from = Config::inst()->get('SS_LogEmailWriter', 'send_from'); - - // override the SMTP server with a custom one if required - $originalSMTP = ini_get('SMTP'); - if($this->customSmtpServer) ini_set('SMTP', $this->customSmtpServer); - - // Use plain mail() implementation to avoid complexity of Mailer implementation. - // Only use built-in mailer when we're in test mode (to allow introspection) - $mailer = Email::mailer(); - if($mailer instanceof TestMailer) { - $mailer->sendHTML( - $this->emailAddress, - null, - $subject, - $data, - null, - "Content-type: text/html\nFrom: " . $from - ); - } else { - mail( - $this->emailAddress, - $subject, - $data, - "Content-type: text/html\nFrom: " . $from - ); - } - - // reset the SMTP server to the original - if($this->customSmtpServer) ini_set('SMTP', $originalSMTP); - } - -} diff --git a/dev/LogErrorEmailFormatter.php b/dev/LogErrorEmailFormatter.php deleted file mode 100644 index 299393e62..000000000 --- a/dev/LogErrorEmailFormatter.php +++ /dev/null @@ -1,80 +0,0 @@ -html, body, table {font-family: sans-serif; font-size: 12px;}'; - $data .= "
\n"; - $data .= "

[$errorType] "; - $data .= nl2br(htmlspecialchars($errstr))."
$errfile:$errline\n
\n
\n

\n"; - - // Render the provided backtrace - $data .= SS_Backtrace::get_rendered_backtrace($errcontext); - - // Compile extra data - $blacklist = array('message', 'timestamp', 'priority', 'priorityName'); - $extras = array_diff_key($event, array_combine($blacklist, $blacklist)); - if($extras) { - $data .= "

Details

\n"; - $data .= "\n"; - foreach($extras as $k => $v) { - if(is_array($v)) $v = var_export($v, true); - $data .= sprintf( - "\n", $k, $v); - } - $data .= "
%s
%s
\n"; - } - - $data .= "
\n"; - - $relfile = Director::makeRelative($errfile); - if($relfile && $relfile[0] == '/') $relfile = substr($relfile, 1); - - $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null; - $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null; - - $subject = "[$errorType] in $relfile:{$errline} (http://{$host}{$uri})"; - - return array( - 'subject' => $subject, - 'data' => $data - ); - } - -} diff --git a/dev/LogErrorFileFormatter.php b/dev/LogErrorFileFormatter.php deleted file mode 100644 index 5a64d8ca7..000000000 --- a/dev/LogErrorFileFormatter.php +++ /dev/null @@ -1,43 +0,0 @@ - at line : - * @package framework - * @subpackage dev - */ -class SS_LogErrorFileFormatter implements Zend_Log_Formatter_Interface { - - public function format($event) { - $errno = $event['message']['errno']; - $errstr = $event['message']['errstr']; - $errfile = $event['message']['errfile']; - $errline = $event['message']['errline']; - $errcontext = $event['message']['errcontext']; - - switch($event['priorityName']) { - case 'ERR': - $errtype = 'Error'; - break; - case 'WARN': - $errtype = 'Warning'; - break; - case 'NOTICE': - $errtype = 'Notice'; - break; - default: - $errtype = $event['priorityName']; - } - - $urlSuffix = ''; - $relfile = Director::makeRelative($errfile); - if(strlen($relfile) && $relfile[0] == '/') $relfile = substr($relfile, 1); - if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] && isset($_SERVER['REQUEST_URI'])) { - $urlSuffix = " (http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI])"; - } - - return '[' . date('d-M-Y H:i:s') . "] $errtype at $relfile line $errline: $errstr$urlSuffix" . PHP_EOL; - } - -} diff --git a/dev/LogFileWriter.php b/dev/LogFileWriter.php deleted file mode 100644 index b9df69df0..000000000 --- a/dev/LogFileWriter.php +++ /dev/null @@ -1,65 +0,0 @@ -path = $path; - $this->messageType = $messageType; - $this->extraHeaders = $extraHeaders; - } - - public static function factory($path, $messageType = 3, $extraHeaders = '') { - return new SS_LogFileWriter($path, $messageType, $extraHeaders); - } - - /** - * Write the log message to the file path set - * in this writer. - */ - public function _write($event) { - if(!$this->_formatter) { - $formatter = new SS_LogErrorFileFormatter(); - $this->setFormatter($formatter); - } - $message = $this->_formatter->format($event); - if(!file_exists(dirname($this->path))) mkdir(dirname($this->path), 0755, true); - error_log($message, $this->messageType, $this->path, $this->extraHeaders); - } - -} diff --git a/dev/SysLogWriter.php b/dev/SysLogWriter.php deleted file mode 100644 index aea45f7cb..000000000 --- a/dev/SysLogWriter.php +++ /dev/null @@ -1,51 +0,0 @@ -_formatter) $this->setFormatter(new SS_LogErrorFileFormatter()); - syslog($event['priority'], $this->_formatter->format($event)); - } - -} diff --git a/dev/ZendLog.php b/dev/ZendLog.php deleted file mode 100644 index fbcfd4451..000000000 --- a/dev/ZendLog.php +++ /dev/null @@ -1,43 +0,0 @@ -_writers; - } - - /** - * Remove a writer instance that exists in this logger. - * @param object Zend_Log_Writer_Abstract instance - */ - public function removeWriter($writer) { - foreach($this->_writers as $index => $existingWriter) { - if($existingWriter == $writer) { - unset($this->_writers[$index]); - } - } - } - - /** - * Clear all writers in this logger. - */ - public function clearWriters() { - $this->_writers = array(); - } - -} diff --git a/docs/en/02_Developer_Guides/07_Debugging/01_Error_Handling.md b/docs/en/02_Developer_Guides/07_Debugging/01_Error_Handling.md index 863116696..0d02daa38 100644 --- a/docs/en/02_Developer_Guides/07_Debugging/01_Error_Handling.md +++ b/docs/en/02_Developer_Guides/07_Debugging/01_Error_Handling.md @@ -1,26 +1,79 @@ -title: Error Handling -summary: Trap, fire and report user exceptions, warnings and errors. +title: Logging and Error Handling +summary: Trap, fire and report diagnostic logs, user exceptions, warnings and errors. -# Error Handling +# Logging and Error Handling -SilverStripe has its own error trapping and handling support. On development sites, SilverStripe will deal harshly with -any warnings or errors: a full call-stack is shown and execution stops for anything, giving you early warning of a -potential issue to handle. +SilverStripe uses Monolog for both error handling and logging. It comes with two default configurations: one for +development environments, and another for test or live environments. On development environments, SilverStripe will +deal harshly with any warnings or errors: a full call-stack is shown and execution stops for anything, giving you early +warning of a potential issue to handle. -## Triggering the error handler. +## Raising errors and logging diagnostic information. -You should use [user_error](http://www.php.net/user_error) to throw errors where appropriate. +For informational and debug logs, you can use the Logger directly. The Logger is a PSR-3 compatible LoggerInterface and +can be accessed via the `Injector`: :::php - if(true == false) { - user_error("I have an error problem", E_USER_ERROR); + Injector::inst()->get('Logger')->info('User has logged in: ID #' . Member::currentUserID()); + Injector::inst()->get('Logger')->debug('Query executed: ' . $sql); + +Although you can raise more important levels of alerts in this way, we recommend using PHP's native error systems for +these instead. + +For notice-level and warning-level issues, you should use [user_error](http://www.php.net/user_error) to throw errors +where appropriate. These will not halt execution but will send a message to the + + :::php + function delete() { + if($this->alreadyDelete) { + user_error("Delete called on already deleted object", E_USER_NOTICE); + return; + } + ... + } + + function getRelatedObject() { + if(!$this->RelatedObjectID) { + user_error("Can't find a related object", E_USER_WARNING); + return null; + } + ... } - if(0 / 0) { - user_error("This time I am warning you", E_USER_WARNING); +For errors that should halt execution, you should use Exceptions. Normally, Exceptions will halt the flow of executuion, +but they can be caught with a try/catch clause. + + :::php + throw new \LogicException("Query failed: " . $sql); + +### Accessing the logger via dependency injection. + +It can quite verbose to call `Injector::inst()->get('Logger')` all the time. More importantly, it also means that you're +coupling your code to global state, which is a bad design practise. A better approach is to use depedency injection to +pass the logger in for you. The [Injector](../extending/Injector) can help with this. The most straightforward is to +specify a `dependencies` config setting, like this: + + :::php + class MyController { + + private static $dependencies = array( + 'logger' => '%$Logger', + ); + + // This will be set automatically, as long as MyController is instantiated via Injector + public $logger; + + function init() { + $this->logger->debug("MyController::init() called"); + parent::init(); + } + } -## Error Levels +In other contexts, such as testing or batch processing, logger can be set to a different value by the code calling +MyController. + +### Error Levels * **E_USER_WARNING:** Err on the side of over-reporting warnings. Throwing warnings provides a means of ensuring that developers know: @@ -31,24 +84,115 @@ developers know: * **E_USER_ERROR:** Throwing one of these errors is going to take down the production site. So you should only throw E_USER_ERROR if it's going to be **dangerous** or **impossible** to continue with the request. +## Configuring error logging -## Filesystem Logs +You can configure your logging using Monolog handlers. The handlers should be provided int the `Logger.handlers` +configuration setting. Below we have a couple of common examples, but Monolog comes with [many different handlers](https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#handlers) +for you to try. -You can indicate a log file relative to the site root. +### Sending emails -**mysite/_config.php** +To send emails, you can use Monolog's [NativeMailerHandler](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/NativeMailerHandler.php#L74), like this: - :::php - if(!Director::isDev()) { - // log errors and warnings - SS_Log::add_writer(new SS_LogFileWriter('../silverstripe-errors-warnings.log'), SS_Log::WARN, '<='); - - // or just errors - SS_Log::add_writer(new SS_LogFileWriter('../silverstripe-errors.log'), SS_Log::ERR); - - // or notices (e.g. for Deprecation Notifications) - SS_Log::add_writer(new SS_LogFileWriter('../silverstripe-errors-notices.log'), SS_Log::NOTICE); - } + Injector: + Logger: + calls: + MailHandler: [ pushHandler, [ %$MailHandler ] ] + MailHandler: + class: Monolog\Handler\NativeMailerHandler + constructor: + - me@example.com + - There was an error on your test site + - me@example.com + - error + properties: + ContentType: text/html + Formatter: %$SilverStripe\Framework\Logging\DetailedErrorFormatter + +The first section 4 lines passes a new handler to `Logger::pushHandler()` from the named service `MailHandler`. The +next 10 lines define what the service is. + +The calls key, `MailHandler`, can be anything you like: its main purpose is to let other configuration disable it +(see below). + +### Logging to a file + +To log to a file, you can use Monolog's [StreamHandler](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/StreamHandler.php#L74), like this: + + Injector: + Logger: + calls: + LogFileHandler: [ pushHandler, [ %$LogFileHandler ] ] + LogFileHandler: + class: Monolog\Handler\StreamHandler + constructor: + - "../silverstripe.log" + - "info" + +The log file will be relative to the framework/ path, so "../silverstripe.log" will create a file in your project root. + +### Disabling the default handler + +You can disable a handler by removing its pushHandlers call from the calls option of the Logger service definition. +The handler key of the default handler is `DisplayErrorHandler`, so you can disable it like this: + + Injector: + Logger: + calls: + DisplayErrorHandler: %%remove%% + +### Setting a different configuration for dev + +In order to set different logging configuration on different environment types, we rely on the environment-specific +configuration features that the config system proviers. For example, here we have different configuration for dev and +non-dev. + + --- + Name: dev-errors + Only: + environment: dev + --- + Injector: + Logger: + calls: + - [ pushHandler, [ %$DisplayErrorHandler ]] + + DisplayErrorHandler: + class: SilverStripe\Framework\Logging\HTTPOutputHandler + constructor: + - "notice" + properties: + Formatter: %$SilverStripe\Framework\Logging\DetailedErrorFormatter + --- + Name: live-errors + Except: + environment: dev + --- + Injector: + Logger: + calls: + - [ pushHandler, [ %$LogFileHandler ]] + - [ pushHandler, [ %$DisplayErrorHandler ]] + + LogFileHander: + class: Monolog\Handler\StreamHandler + constructor: + - "../silverstripe.log" + - "notice" + properties: + Formatter: %$Monolog\Formatter\HtmlFormatter + ContentType: text/html + DisplayErrorHandler: + class: SilverStripe\Framework\Logging\HTTPOutputHandler + constructor: + - "error" + properties: + Formatter: %$FriendlyErrorFormatter + FriendlyErrorFormatter: + class: SilverStripe\Framework\Logging\DebugViewFriendlyErrorFormatter + properties: + Title: "There has been an error" + Body: "The website server has not been able to respond to your request"
In addition to SilverStripe-integrated logging, it is advisable to fall back to PHPs native logging functionality. A @@ -71,6 +215,48 @@ You can send both fatal errors and warnings in your code to a specified email-ad SS_Log::add_writer(new SS_LogEmailWriter('admin@domain.com'), SS_Log::ERR); } -## API Documentation -* [api:SS_Log] +## Replacing default implementations + +For most application, Monolog and its default error handler should be fine, as you can get a lot of flexibility simply +by changing that handlers that are used. However, some situations will call for replacing the default components with +others. + +### Replacing the logger + +Monolog comes by default with SilverStripe, but you may use another PSR-3 compliant logger, if you wish. To do this, +set the `Injector.Logger` configuration parameter, providing a new injector definition. For example: + + Injector: + ErrorHandler: + class: Logging\Logger + constructor: + - 'alternative-logger' + +If you do this, you will need to supply your own handlers, and the `Logger.handlers` configuration parameter will +be ignored. + +### Replacing the error handler + +The class `SilverStripe\Framework\Logging\MonologLoader` is responsible for loading performing Monolog-specific +configuration. It does a number of things: + + * Create a `Monolog\ErrorHandler` object. + * Register the registered service `Logger` against it, to start the error handler. + * If `Logger` has a `pushHandler()` method, pass every object defined by `ErrorHandler.handlers` into it, one at a time. + +This error handler is flexible enough to work with any PSR-3 logging implementation, but sometimes you will want to use +another. To replace this, you should registered a new service, `ErrorHandlerLoader`. For example: + + Injector: + ErrorHandlerLoader: + class: MyApp\CustomErrorHandlerLoader + +You should register something `Callable`, for example a class with an `__invoke()` method. + +## Differences from SilverStripe 3 + +In SilverStripe 3, logging was based on the Zend Log module. Customisations were added using `SS_Log::add_writer()`. +This function no longer works, and any Zend Log writers will need to be replaced with Monolog handlers. Fortunately, +a range of handlers are available, both in the core package and in add-ons. See the +[Monolog documentation](https://github.com/Seldaek/monolog/blob/master/doc/01-usage.md) for more information. diff --git a/i18n/i18n.php b/i18n/i18n.php index 171bc9e16..59358a09e 100644 --- a/i18n/i18n.php +++ b/i18n/i18n.php @@ -2114,12 +2114,12 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable { $placeholder = '{'.$variable.'}'; $returnValue = str_replace($placeholder, $injection, $returnValue, $count); if(!$count) { - SS_Log::log(sprintf( + Injector::inst()->get('Logger')->log('notice', sprintf( "Couldn't find placeholder '%s' in translation string '%s' (id: '%s')", $placeholder, $returnValue, $entity - ), SS_Log::NOTICE); + )); } } } diff --git a/logging/DebugViewFriendlyErrorFormatter.php b/logging/DebugViewFriendlyErrorFormatter.php new file mode 100644 index 000000000..9172fbc5d --- /dev/null +++ b/logging/DebugViewFriendlyErrorFormatter.php @@ -0,0 +1,90 @@ +statusCode; + } + + public function setStatusCode($statusCode) { + $this->statusCode = $statusCode; + } + + public function getTitle($title) { + return $this->friendlyErrorMessage; + } + + public function setTitle($title) { + $this->friendlyErrorMessage = $title; + } + + public function getBody($title) { + return $this->friendlyErrorDetail; + } + + public function setBody($body) { + $this->friendlyErrorDetail = $body; + } + + public function format(array $record) + { + + return $this->output(); + } + + public function formatBatch(array $records) { + return $this->output(); + } + + public function output() { + // TODO: Refactor into a content-type option + if(\Director::is_ajax()) { + return $this->friendlyErrorMessage; + + } else { + // TODO: Refactor this into CMS + if(class_exists('ErrorPage')){ + $errorFilePath = \ErrorPage::get_filepath_for_errorcode( + $this->statusCode, + class_exists('Translatable') ? \Translatable::get_current_locale() : null + ); + + if(file_exists($errorFilePath)) { + $content = file_get_contents($errorFilePath); + if(!headers_sent()) { + header('Content-Type: text/html'); + } + // $BaseURL is left dynamic in error-###.html, so that multi-domain sites don't get broken + return str_replace('$BaseURL', \Director::absoluteBaseURL(), $content); + } + } + + + $renderer = \Debug::create_debug_view(); + $output = $renderer->renderHeader(); + $output .= $renderer->renderInfo("Website Error", $this->friendlyErrorMessage, $this->friendlyErrorDetail); + + if(\Email::config()->admin_email) { + $mailto = \Email::obfuscate(\Email::config()->admin_email); + $output .= $renderer->renderParagraph('Contact an administrator: ' . $mailto . ''); + } + + $output .= $renderer->renderFooter(); + return $output; + } + } +} diff --git a/logging/DetailedErrorFormatter.php b/logging/DetailedErrorFormatter.php new file mode 100644 index 000000000..a13182e65 --- /dev/null +++ b/logging/DetailedErrorFormatter.php @@ -0,0 +1,118 @@ + $exception->getCode(), + 'message' => 'Uncaught ' . get_class($exception) . ': ' . $exception->getMessage(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'trace' => $exception->getTrace(), + ); + } else { + $context = $record['context']; + foreach(array('code','message','file','line') as $key) { + if(!isset($context[$key])) { + $context[$key] = isset($record[$key]) ? $record[$key] : null; + } + } + + $trace = debug_backtrace(); + + // Filter out monolog plumbing from the trace + // If the context file & line isn't found in the trace, then the trace is most likely + // call to the fatal error handler and is not useful, so exclude it entirely + $i = $this->findInTrace($trace, $context['file'], $context['line']); + if($i !== null) { + $context['trace'] = array_slice($trace, $i); + } else { + $context['trace'] = null; + } + } + + return $this->output( + $context['code'], + $context['message'], + $context['file'], + $context['line'], + $context['trace'] + ); + } + + public function formatBatch(array $records) { + return implode("\n", array_map(array($this, 'format'), $records)); + } + + /** + * Find a call on the given file & line in the trace + * @param array $trace The result of debug_backtrace() + * @param string $file The filename to look for + * @param string $line The line number to look for + * @return int|null The matching row number, if found, or null if not found + */ + protected function findInTrace(array $trace, $file, $line) { + foreach($trace as $i => $call) { + if(isset($call['file']) && isset($call['line']) && $call['file'] == $file && $call['line'] == $line) { + return $i; + } + } + return null; + } + + /** + * Render a developer facing error page, showing the stack trace and details + * of the code where the error occured. + * + * @param int $errno + * @param str $errstr + * @param str $errfile + * @param int $errline + * @param array $errcontext + */ + protected function output($errno, $errstr, $errfile, $errline, $errcontext) { + $reporter = \Debug::create_debug_view(); + + // Coupling alert: This relies on knowledge of how the director gets its URL, it could be improved. + $httpRequest = null; + if(isset($_SERVER['REQUEST_URI'])) { + $httpRequest = $_SERVER['REQUEST_URI']; + } elseif(isset($_REQUEST['url'])) { + $httpRequest = $_REQUEST['url']; + } + if(isset($_SERVER['REQUEST_METHOD'])) { + $httpRequest = $_SERVER['REQUEST_METHOD'] . ' ' . $httpRequest; + } + + $output = $reporter->renderHeader($httpRequest); + $output .= $reporter->renderError($httpRequest, $errno, $errstr, $errfile, $errline); + + if(file_exists($errfile)) { + $lines = file($errfile); + + // Make the array 1-based + array_unshift($lines, ""); + unset($lines[0]); + + $offset = $errline-10; + $lines = array_slice($lines, $offset, 16, true); + $output .= $reporter->renderSourceFragment($lines, $errline); + } + $output .= $reporter->renderTrace($errcontext); + $output .= $reporter->renderFooter(); + + return $output; + } +} diff --git a/logging/HTTPOutputHandler.php b/logging/HTTPOutputHandler.php new file mode 100644 index 000000000..6427ba451 --- /dev/null +++ b/logging/HTTPOutputHandler.php @@ -0,0 +1,73 @@ +contentType; + } + + /** + * Set the mime type to use when displaying this error. + * Default text/html + */ + public function setContentType($contentType) { + $this->contentType = $contentType; + } + + /** + * Get the HTTP status code to use when displaying this error. + */ + public function getStatusCode() { + return $this->statusCode; + } + + /** + * Set the HTTP status code to use when displaying this error. + * Default 500 + */ + public function setStatusCode($statusCode) { + $this->statusCode = $statusCode; + } + + protected function write(array $record) { + ini_set('display_errors', 0); + + // TODO: This coupling isn't ideal + // See https://github.com/silverstripe/silverstripe-framework/issues/4484 + if(\Controller::has_curr()) { + $response = \Controller::curr()->getResponse(); + } else { + $response = new SS_HTTPResponse(); + } + + // If headers have been sent then these won't be used, and may throw errors that we wont' want to see. + if(!headers_sent()) { + $response->setStatusCode($this->statusCode); + $response->addHeader("Content-Type", $this->contentType); + } else { + // To supress errors aboot errors + $response->setStatusCode(200); + } + + $response->setBody($record['formatted']); + $response->output(); + + return false === $this->bubble; + } +} diff --git a/logging/Log.php b/logging/Log.php new file mode 100644 index 000000000..42ace5530 --- /dev/null +++ b/logging/Log.php @@ -0,0 +1,89 @@ + + * SS_Log::add_writer(new SS_LogEmailWriter('my@email.com'), SS_Log::ERR); + * + * + * Example usage of logging errors by file: + * + * SS_Log::add_writer(new SS_LogFileWriter('/var/log/silverstripe/errors.log'), SS_Log::ERR); + * + * + * Example usage of logging at warnings and errors by setting the priority to '<=': + * + * SS_Log::add_writer(new SS_LogEmailWriter('my@email.com'), SS_Log::WARN, '<='); + * + * + * Each writer object can be assigned a formatter. The formatter is + * responsible for formatting the message before giving it to the writer. + * {@link SS_LogErrorEmailFormatter} is such an example that formats errors + * into HTML for human readability in an email client. + * + * Formatters are added to writers like this: + * + * $logEmailWriter = new SS_LogEmailWriter('my@email.com'); + * $myEmailFormatter = new MyLogEmailFormatter(); + * $logEmailWriter->setFormatter($myEmailFormatter); + * + * + * @package framework + * @subpackage dev + */ +class SS_Log +{ + + const ERR = 'error'; + const WARN = 'warning'; + const NOTICE = 'notice'; + const INFO = 'info'; + const DEBUG = 'debug'; + + /** + * Get the logger currently in use, or create a new one if it doesn't exist. + * + * @return Psr\Log\LoggerInterface + */ + public static function get_logger() { + Deprecation::notice('4.0', 'Use Injector::inst()->get(\'Logger\') instead'); + return Injector::inst()->get('Logger'); + } + + public static function add_writer($writer) { + throw new \InvalidArgumentException("SS_Log::add_writer() on longer works. Please use a Monolog Handler " + ."instead, and add list of handlers in the Logger.handlers configuration parameter."); + } + + /** + * Dispatch a message by priority level. + * + * The message parameter can be either a string (a simple error + * message), or an array of variables. The latter is useful for passing + * along a list of debug information for the writer to handle, such as + * error code, error line, error context (backtrace). + * + * @param mixed $message Exception object or array of error context variables + * @param const $priority Priority. Possible values: SS_Log::ERR, SS_Log::WARN or SS_Log::NOTICE + * @param mixed $extras Extra information to log in event + * + * @deprecated 4.0.0:5.0.0 Use Injector::inst()->get('Logger')->log($priority, $message) instead + */ + public static function log($message, $priority, $extras = null) { + Deprecation::notice('4.0', 'Use Injector::inst()->get(\'Logger\')->log($priority, $message) instead'); + + Injector::inst()->get('Logger')->log($priority, $message); + } +} diff --git a/logging/MonologErrorHandler.php b/logging/MonologErrorHandler.php new file mode 100644 index 000000000..c51e43298 --- /dev/null +++ b/logging/MonologErrorHandler.php @@ -0,0 +1,30 @@ +logger = $logger; + } + + function start() { + if(!$this->logger) { + throw new \InvalidArgumentException("No Logger property passed to MonologErrorHandler." + . "Is your Injector config correct?"); + } + + ErrorHandler::register($this->logger); + } +} diff --git a/tests/dev/LogTest.php b/tests/dev/LogTest.php deleted file mode 100644 index 4ab6d1ef1..000000000 --- a/tests/dev/LogTest.php +++ /dev/null @@ -1,138 +0,0 @@ -assertEquals(2, count($writers)); - } - - public function testRemoveWriter() { - $testEmailWriter = new SS_LogEmailWriter('test@test.com'); - $testFileWriter = new SS_LogFileWriter('../test.log'); - SS_Log::add_writer($testEmailWriter, SS_Log::ERR); - SS_Log::add_writer($testFileWriter, SS_Log::WARN); - - SS_Log::remove_writer($testEmailWriter); - $writers = SS_Log::get_writers(); - - $this->assertEquals(1, count($writers)); - - SS_Log::remove_writer($testFileWriter); - $writers = SS_Log::get_writers(); - $this->assertEquals(0, count($writers)); - } - - public function testEmailWriter() { - $testEmailWriter = new SS_LogEmailWriter('test@test.com'); - SS_Log::add_writer($testEmailWriter, SS_Log::ERR); - - SS_Log::log('Email test', SS_Log::ERR, array('my-string' => 'test', 'my-array' => array('one' => 1))); - $this->assertEmailSent('test@test.com'); - $email = $this->findEmail('test@test.com'); - $this->assertContains('[Error] Email test', $email['htmlContent']); - $parser = new CSSContentParser($email['htmlContent']); - $extras = $parser->getBySelector('table.extras'); - $extraRows = $extras[0]->tr; - $this->assertContains('my-string', $extraRows[count($extraRows)-2]->td[0]->asXML(), 'Contains extra data key'); - $this->assertContains('test', $extraRows[count($extraRows)-2]->td[1]->asXML(), 'Contains extra data value'); - $this->assertContains('my-array', $extraRows[count($extraRows)-1]->td[0]->asXML(), 'Contains extra data key'); - $this->assertContains( - "array('one'=>1,)", - str_replace(array("\r", "\n", " "), '', $extraRows[count($extraRows)-1]->td[1]->asXML()), - 'Serializes arrays correctly' - ); - } - - public function testEmailWriterDebugPriority() { - $testEmailWriter = new SS_LogEmailWriter('test@test.com'); - SS_Log::add_writer($testEmailWriter, SS_Log::DEBUG); - SS_Log::log('Test something', SS_Log::DEBUG, array('my-string' => 'test', 'my-array' => array('one' => 1))); - $this->assertEmailSent('test@test.com'); - $email = $this->findEmail('test@test.com'); - $this->assertContains('[DEBUG] Test something', $email['htmlContent']); - } - - public function testEmailWriterInfoPriority() { - $testEmailWriter = new SS_LogEmailWriter('test@test.com'); - SS_Log::add_writer($testEmailWriter, SS_Log::INFO); - SS_Log::log('Test something', SS_Log::INFO, array('my-string' => 'test', 'my-array' => array('one' => 1))); - $this->assertEmailSent('test@test.com'); - $email = $this->findEmail('test@test.com'); - $this->assertContains('[INFO] Test something', $email['htmlContent']); - } - - protected function exceptionGeneratorThrower() { - throw new Exception("thrown from SS_LogTest::testExceptionGeneratorTop"); - } - - protected function exceptionGenerator() { - $this->exceptionGeneratorThrower(); - } - - public function testEmailException() { - $testEmailWriter = new SS_LogEmailWriter('test@test.com'); - SS_Log::add_writer($testEmailWriter, SS_Log::ERR); - - // Trigger exception handling mechanism - try { - $this->exceptionGenerator(); - } catch(Exception $exception) { - // Mimics exceptionHandler, but without the exit(1) - SS_Log::log( - array( - 'errno' => E_USER_ERROR, - 'errstr' => ("Uncaught " . get_class($exception) . ": " . $exception->getMessage()), - 'errfile' => $exception->getFile(), - 'errline' => $exception->getLine(), - 'errcontext' => $exception->getTrace() - ), - SS_Log::ERR - ); - } - - // Ensure email is sent - $this->assertEmailSent('test@test.com'); - - // Begin parsing of email body - $email = $this->findEmail('test@test.com'); - $parser = new CSSContentParser($email['htmlContent']); - - // Check that the first three lines of the stacktrace are correct - $stacktrace = $parser->getByXpath('//body/div[1]/ul[1]'); - $this->assertContains('SS_LogTest->exceptionGeneratorThrower()', $stacktrace[0]->li[0]->asXML()); - $this->assertContains('SS_LogTest->exceptionGenerator()', $stacktrace[0]->li[1]->asXML()); - $this->assertContains('SS_LogTest->testEmailException()', $stacktrace[0]->li[2]->asXML()); - } - - public function testSubclassedLogger() { - $this->assertTrue(SS_Log::get_logger() !== SS_LogTest_NewLogger::get_logger()); - } - -} - -class SS_LogTest_NewLogger extends SS_Log { - protected static $logger; -} diff --git a/thirdparty/Zend/Log.php b/thirdparty/Zend/Log.php deleted file mode 100644 index 4d3a72065..000000000 --- a/thirdparty/Zend/Log.php +++ /dev/null @@ -1,624 +0,0 @@ -_priorities = array_flip($r->getConstants()); - - if ($writer !== null) { - $this->addWriter($writer); - } - } - - /** - * Factory to construct the logger and one or more writers - * based on the configuration array - * - * @param array|Zend_Config Array or instance of Zend_Config - * @return Zend_Log - * @throws Zend_Log_Exception - */ - static public function factory($config = array()) - { - if ($config instanceof Zend_Config) { - $config = $config->toArray(); - } - - if (!is_array($config) || empty($config)) { - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Configuration must be an array or instance of Zend_Config'); - } - - $log = new self; - - if (array_key_exists('timestampFormat', $config)) { - if (null != $config['timestampFormat'] && '' != $config['timestampFormat']) { - $log->setTimestampFormat($config['timestampFormat']); - } - unset($config['timestampFormat']); - } - - if (!is_array(current($config))) { - $log->addWriter(current($config)); - } else { - foreach($config as $writer) { - $log->addWriter($writer); - } - } - - return $log; - } - - - /** - * Construct a writer object based on a configuration array - * - * @param array $spec config array with writer spec - * @return Zend_Log_Writer_Abstract - * @throws Zend_Log_Exception - */ - protected function _constructWriterFromConfig($config) - { - $writer = $this->_constructFromConfig('writer', $config, $this->_defaultWriterNamespace); - - if (!$writer instanceof Zend_Log_Writer_Abstract) { - $writerName = is_object($writer) - ? get_class($writer) - : 'The specified writer'; - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception("{$writerName} does not extend Zend_Log_Writer_Abstract!"); - } - - if (isset($config['filterName'])) { - $filter = $this->_constructFilterFromConfig($config); - $writer->addFilter($filter); - } - - if (isset($config['formatterName'])) { - $formatter = $this->_constructFormatterFromConfig($config); - $writer->setFormatter($formatter); - } - - return $writer; - } - - /** - * Construct filter object from configuration array or Zend_Config object - * - * @param array|Zend_Config $config Zend_Config or Array - * @return Zend_Log_Filter_Interface - * @throws Zend_Log_Exception - */ - protected function _constructFilterFromConfig($config) - { - $filter = $this->_constructFromConfig('filter', $config, $this->_defaultFilterNamespace); - - if (!$filter instanceof Zend_Log_Filter_Interface) { - $filterName = is_object($filter) - ? get_class($filter) - : 'The specified filter'; - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception("{$filterName} does not implement Zend_Log_Filter_Interface"); - } - - return $filter; - } - - /** - * Construct formatter object from configuration array or Zend_Config object - * - * @param array|Zend_Config $config Zend_Config or Array - * @return Zend_Log_Formatter_Interface - * @throws Zend_Log_Exception - */ - protected function _constructFormatterFromConfig($config) - { - $formatter = $this->_constructFromConfig('formatter', $config, $this->_defaultFormatterNamespace); - - if (!$formatter instanceof Zend_Log_Formatter_Interface) { - $formatterName = is_object($formatter) - ? get_class($formatter) - : 'The specified formatter'; - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception($formatterName . ' does not implement Zend_Log_Formatter_Interface'); - } - - return $formatter; - } - - /** - * Construct a filter or writer from config - * - * @param string $type 'writer' of 'filter' - * @param mixed $config Zend_Config or Array - * @param string $namespace - * @return object - * @throws Zend_Log_Exception - */ - protected function _constructFromConfig($type, $config, $namespace) - { - if ($config instanceof Zend_Config) { - $config = $config->toArray(); - } - - if (!is_array($config) || empty($config)) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception( - 'Configuration must be an array or instance of Zend_Config' - ); - } - - $params = isset($config[ $type .'Params' ]) ? $config[ $type .'Params' ] : array(); - $className = $this->getClassName($config, $type, $namespace); - if (!class_exists($className)) { - require_once 'Zend/Loader.php'; - Zend_Loader::loadClass($className); - } - - $reflection = new ReflectionClass($className); - if (!$reflection->implementsInterface('Zend_Log_FactoryInterface')) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception( - $className . ' does not implement Zend_Log_FactoryInterface and can not be constructed from config.' - ); - } - - return call_user_func(array($className, 'factory'), $params); - } - - /** - * Get the writer or filter full classname - * - * @param array $config - * @param string $type filter|writer - * @param string $defaultNamespace - * @return string full classname - * @throws Zend_Log_Exception - */ - protected function getClassName($config, $type, $defaultNamespace) - { - if (!isset($config[ $type . 'Name' ])) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception("Specify {$type}Name in the configuration array"); - } - $className = $config[ $type . 'Name' ]; - - $namespace = $defaultNamespace; - if (isset($config[ $type . 'Namespace' ])) { - $namespace = $config[ $type . 'Namespace' ]; - } - - $fullClassName = $namespace . '_' . $className; - return $fullClassName; - } - - /** - * Packs message and priority into Event array - * - * @param string $message Message to log - * @param integer $priority Priority of message - * @return array Event array - */ - protected function _packEvent($message, $priority) - { - return array_merge(array( - 'timestamp' => date($this->_timestampFormat), - 'message' => $message, - 'priority' => $priority, - 'priorityName' => $this->_priorities[$priority] - ), - $this->_extras - ); - } - - /** - * Class destructor. Shutdown log writers - * - * @return void - */ - public function __destruct() - { - foreach($this->_writers as $writer) { - $writer->shutdown(); - } - } - - /** - * Undefined method handler allows a shortcut: - * $log->priorityName('message') - * instead of - * $log->log('message', Zend_Log::PRIORITY_NAME) - * - * @param string $method priority name - * @param string $params message to log - * @return void - * @throws Zend_Log_Exception - */ - public function __call($method, $params) - { - $priority = strtoupper($method); - if (($priority = array_search($priority, $this->_priorities)) !== false) { - switch (count($params)) { - case 0: - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Missing log message'); - case 1: - $message = array_shift($params); - $extras = null; - break; - default: - $message = array_shift($params); - $extras = array_shift($params); - break; - } - $this->log($message, $priority, $extras); - } else { - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Bad log priority'); - } - } - - /** - * Log a message at a priority - * - * @param string $message Message to log - * @param integer $priority Priority of message - * @param mixed $extras Extra information to log in event - * @return void - * @throws Zend_Log_Exception - */ - public function log($message, $priority, $extras = null) - { - // sanity checks - if (empty($this->_writers)) { - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('No writers were added'); - } - - if (! isset($this->_priorities[$priority])) { - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Bad log priority'); - } - - // pack into event required by filters and writers - $event = $this->_packEvent($message, $priority); - - // Check to see if any extra information was passed - if (!empty($extras)) { - $info = array(); - if (is_array($extras)) { - foreach ($extras as $key => $value) { - if (is_string($key)) { - $event[$key] = $value; - } else { - $info[] = $value; - } - } - } else { - $info = $extras; - } - if (!empty($info)) { - $event['info'] = $info; - } - } - - // abort if rejected by the global filters - foreach ($this->_filters as $filter) { - if (! $filter->accept($event)) { - return; - } - } - - // send to each writer - foreach ($this->_writers as $writer) { - $writer->write($event); - } - } - - /** - * Add a custom priority - * - * @param string $name Name of priority - * @param integer $priority Numeric priority - * @throws Zend_Log_Exception - */ - public function addPriority($name, $priority) - { - // Priority names must be uppercase for predictability. - $name = strtoupper($name); - - if (isset($this->_priorities[$priority]) - || false !== array_search($name, $this->_priorities)) { - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Existing priorities cannot be overwritten'); - } - - $this->_priorities[$priority] = $name; - return $this; - } - - /** - * Add a filter that will be applied before all log writers. - * Before a message will be received by any of the writers, it - * must be accepted by all filters added with this method. - * - * @param int|Zend_Config|array|Zend_Log_Filter_Interface $filter - * @return Zend_Log - * @throws Zend_Log_Exception - */ - public function addFilter($filter) - { - if (is_int($filter)) { - /** @see Zend_Log_Filter_Priority */ - require_once 'Zend/Log/Filter/Priority.php'; - $filter = new Zend_Log_Filter_Priority($filter); - - } elseif ($filter instanceof Zend_Config || is_array($filter)) { - $filter = $this->_constructFilterFromConfig($filter); - - } elseif(! $filter instanceof Zend_Log_Filter_Interface) { - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Invalid filter provided'); - } - - $this->_filters[] = $filter; - return $this; - } - - /** - * Add a writer. A writer is responsible for taking a log - * message and writing it out to storage. - * - * @param mixed $writer Zend_Log_Writer_Abstract or Config array - * @return Zend_Log - */ - public function addWriter($writer) - { - if (is_array($writer) || $writer instanceof Zend_Config) { - $writer = $this->_constructWriterFromConfig($writer); - } - - if (!$writer instanceof Zend_Log_Writer_Abstract) { - /** @see Zend_Log_Exception */ - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception( - 'Writer must be an instance of Zend_Log_Writer_Abstract' - . ' or you should pass a configuration array' - ); - } - - $this->_writers[] = $writer; - return $this; - } - - /** - * Set an extra item to pass to the log writers. - * - * @param string $name Name of the field - * @param string $value Value of the field - * @return Zend_Log - */ - public function setEventItem($name, $value) - { - $this->_extras = array_merge($this->_extras, array($name => $value)); - return $this; - } - - /** - * Register Logging system as an error handler to log php errors - * Note: it still calls the original error handler if set_error_handler is able to return it. - * - * Errors will be mapped as: - * E_NOTICE, E_USER_NOTICE => NOTICE - * E_WARNING, E_CORE_WARNING, E_USER_WARNING => WARN - * E_ERROR, E_USER_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR => ERR - * E_DEPRECATED, E_STRICT, E_USER_DEPRECATED => DEBUG - * (unknown/other) => INFO - * - * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler - * - * @return Zend_Log - */ - public function registerErrorHandler() - { - // Only register once. Avoids loop issues if it gets registered twice. - if ($this->_registeredErrorHandler) { - return $this; - } - - $this->_origErrorHandler = set_error_handler(array($this, 'errorHandler')); - - // Contruct a default map of phpErrors to Zend_Log priorities. - // Some of the errors are uncatchable, but are included for completeness - $this->_errorHandlerMap = array( - E_NOTICE => Zend_Log::NOTICE, - E_USER_NOTICE => Zend_Log::NOTICE, - E_WARNING => Zend_Log::WARN, - E_CORE_WARNING => Zend_Log::WARN, - E_USER_WARNING => Zend_Log::WARN, - E_ERROR => Zend_Log::ERR, - E_USER_ERROR => Zend_Log::ERR, - E_CORE_ERROR => Zend_Log::ERR, - E_RECOVERABLE_ERROR => Zend_Log::ERR, - E_STRICT => Zend_Log::DEBUG, - ); - // PHP 5.3.0+ - if (defined('E_DEPRECATED')) { - $this->_errorHandlerMap['E_DEPRECATED'] = Zend_Log::DEBUG; - } - if (defined('E_USER_DEPRECATED')) { - $this->_errorHandlerMap['E_USER_DEPRECATED'] = Zend_Log::DEBUG; - } - - $this->_registeredErrorHandler = true; - return $this; - } - - /** - * Error Handler will convert error into log message, and then call the original error handler - * - * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler - * @param int $errno - * @param string $errstr - * @param string $errfile - * @param int $errline - * @param array $errcontext - * @return boolean - */ - public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) - { - $errorLevel = error_reporting(); - - if ($errorLevel && $errno) { - if (isset($this->_errorHandlerMap[$errno])) { - $priority = $this->_errorHandlerMap[$errno]; - } else { - $priority = Zend_Log::INFO; - } - $this->log($errstr, $priority, array('errno'=>$errno, 'file'=>$errfile, 'line'=>$errline, 'context'=>$errcontext)); - } - - if ($this->_origErrorHandler !== null) { - return call_user_func($this->_origErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext); - } - return false; - } - - /** - * Set timestamp format for log entries. - * - * @param string $format - * @return Zend_Log - */ - public function setTimestampFormat($format) - { - $this->_timestampFormat = $format; - return $this; - } - - /** - * Get timestamp format used for log entries. - * - * @return string - */ - public function getTimestampFormat() - { - return $this->_timestampFormat; - } -} diff --git a/thirdparty/Zend/Log/Exception.php b/thirdparty/Zend/Log/Exception.php deleted file mode 100644 index 15c198c56..000000000 --- a/thirdparty/Zend/Log/Exception.php +++ /dev/null @@ -1,33 +0,0 @@ -toArray(); - } - - if (!is_array($config)) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Configuration must be an array or instance of Zend_Config'); - } - - return $config; - } -} diff --git a/thirdparty/Zend/Log/Filter/Interface.php b/thirdparty/Zend/Log/Filter/Interface.php deleted file mode 100644 index 801a62ba9..000000000 --- a/thirdparty/Zend/Log/Filter/Interface.php +++ /dev/null @@ -1,40 +0,0 @@ -_regexp = $regexp; - } - - /** - * Create a new instance of Zend_Log_Filter_Message - * - * @param array|Zend_Config $config - * @return Zend_Log_Filter_Message - */ - static public function factory($config) - { - $config = self::_parseConfig($config); - $config = array_merge(array( - 'regexp' => null - ), $config); - - return new self( - $config['regexp'] - ); - } - - /** - * Returns TRUE to accept the message, FALSE to block it. - * - * @param array $event event data - * @return boolean accepted? - */ - public function accept($event) - { - return preg_match($this->_regexp, $event['message']) > 0; - } -} diff --git a/thirdparty/Zend/Log/Filter/Priority.php b/thirdparty/Zend/Log/Filter/Priority.php deleted file mode 100644 index 56be6d8d2..000000000 --- a/thirdparty/Zend/Log/Filter/Priority.php +++ /dev/null @@ -1,101 +0,0 @@ -_priority = $priority; - $this->_operator = $operator === null ? '<=' : $operator; - } - - /** - * Create a new instance of Zend_Log_Filter_Priority - * - * @param array|Zend_Config $config - * @return Zend_Log_Filter_Priority - */ - static public function factory($config) - { - $config = self::_parseConfig($config); - $config = array_merge(array( - 'priority' => null, - 'operator' => null, - ), $config); - - // Add support for constants - if (!is_numeric($config['priority']) && isset($config['priority']) && defined($config['priority'])) { - $config['priority'] = constant($config['priority']); - } - - return new self( - (int) $config['priority'], - $config['operator'] - ); - } - - /** - * Returns TRUE to accept the message, FALSE to block it. - * - * @param array $event event data - * @return boolean accepted? - */ - public function accept($event) - { - return version_compare($event['priority'], $this->_priority, $this->_operator); - } -} diff --git a/thirdparty/Zend/Log/Filter/Suppress.php b/thirdparty/Zend/Log/Filter/Suppress.php deleted file mode 100644 index aa3008f4a..000000000 --- a/thirdparty/Zend/Log/Filter/Suppress.php +++ /dev/null @@ -1,77 +0,0 @@ -_accept = (! $suppress); - } - - /** - * Returns TRUE to accept the message, FALSE to block it. - * - * @param array $event event data - * @return boolean accepted? - */ - public function accept($event) - { - return $this->_accept; - } - - /** - * Create a new instance of Zend_Log_Filter_Suppress - * - * @param array|Zend_Config $config - * @return Zend_Log_Filter_Suppress - * @throws Zend_Log_Exception - */ - static public function factory($config) - { - return new self(); - } -} diff --git a/thirdparty/Zend/Log/Formatter/Abstract.php b/thirdparty/Zend/Log/Formatter/Abstract.php deleted file mode 100644 index dc53c751c..000000000 --- a/thirdparty/Zend/Log/Formatter/Abstract.php +++ /dev/null @@ -1,40 +0,0 @@ -_format = $format; - } - - /** - * Factory for Zend_Log_Formatter_Simple classe - * - * @param array|Zend_Config $options - * @return Zend_Log_Formatter_Simple - */ - public static function factory($options) - { - $format = null; - if (null !== $options) { - if ($options instanceof Zend_Config) { - $options = $options->toArray(); - } - - if (array_key_exists('format', $options)) { - $format = $options['format']; - } - } - - return new self($format); - } - - /** - * Formats data into a single line to be written by the writer. - * - * @param array $event event data - * @return string formatted line to write to the log - */ - public function format($event) - { - $output = $this->_format; - - foreach ($event as $name => $value) { - if ((is_object($value) && !method_exists($value,'__toString')) - || is_array($value) - ) { - $value = gettype($value); - } - - $output = str_replace("%$name%", $value, $output); - } - - return $output; - } -} diff --git a/thirdparty/Zend/Log/Formatter/Xml.php b/thirdparty/Zend/Log/Formatter/Xml.php deleted file mode 100644 index b04973d55..000000000 --- a/thirdparty/Zend/Log/Formatter/Xml.php +++ /dev/null @@ -1,165 +0,0 @@ -toArray(); - } elseif (!is_array($options)) { - $args = func_get_args(); - - $options = array( - 'rootElement' => array_shift($args) - ); - - if (count($args)) { - $options['elementMap'] = array_shift($args); - } - - if (count($args)) { - $options['encoding'] = array_shift($args); - } - } - - if (!array_key_exists('rootElement', $options)) { - $options['rootElement'] = 'logEntry'; - } - - if (!array_key_exists('encoding', $options)) { - $options['encoding'] = 'UTF-8'; - } - - $this->_rootElement = $options['rootElement']; - $this->setEncoding($options['encoding']); - - if (array_key_exists('elementMap', $options)) { - $this->_elementMap = $options['elementMap']; - } - } - - /** - * Factory for Zend_Log_Formatter_Xml classe - * - * @param array|Zend_Config $options - * @return Zend_Log_Formatter_Xml - */ - public static function factory($options) - { - return new self($options); - } - - /** - * Get encoding - * - * @return string - */ - public function getEncoding() - { - return $this->_encoding; - } - - /** - * Set encoding - * - * @param string $value - * @return Zend_Log_Formatter_Xml - */ - public function setEncoding($value) - { - $this->_encoding = (string) $value; - return $this; - } - - /** - * Formats data into a single line to be written by the writer. - * - * @param array $event event data - * @return string formatted line to write to the log - */ - public function format($event) - { - if ($this->_elementMap === null) { - $dataToInsert = $event; - } else { - $dataToInsert = array(); - foreach ($this->_elementMap as $elementName => $fieldKey) { - $dataToInsert[$elementName] = $event[$fieldKey]; - } - } - - $enc = $this->getEncoding(); - $dom = new DOMDocument('1.0', $enc); - $elt = $dom->appendChild(new DOMElement($this->_rootElement)); - - foreach ($dataToInsert as $key => $value) { - if (empty($value) - || is_scalar($value) - || (is_object($value) && method_exists($value,'__toString')) - ) { - if($key == "message") { - $value = htmlspecialchars($value, ENT_COMPAT, $enc); - } - $elt->appendChild(new DOMElement($key, (string)$value)); - } - } - - $xml = $dom->saveXML(); - $xml = preg_replace('/<\?xml version="1.0"( encoding="[^\"]*")?\?>\n/u', '', $xml); - - return $xml . PHP_EOL; - } -} diff --git a/thirdparty/Zend/Log/Writer/Abstract.php b/thirdparty/Zend/Log/Writer/Abstract.php deleted file mode 100644 index e9581c5e8..000000000 --- a/thirdparty/Zend/Log/Writer/Abstract.php +++ /dev/null @@ -1,138 +0,0 @@ -_filters[] = $filter; - return $this; - } - - /** - * Log a message to this writer. - * - * @param array $event log data event - * @return void - */ - public function write($event) - { - foreach ($this->_filters as $filter) { - if (! $filter->accept($event)) { - return; - } - } - - // exception occurs on error - $this->_write($event); - } - - /** - * Set a new formatter for this writer - * - * @param Zend_Log_Formatter_Interface $formatter - * @return Zend_Log_Writer_Abstract - */ - public function setFormatter(Zend_Log_Formatter_Interface $formatter) - { - $this->_formatter = $formatter; - return $this; - } - - /** - * Perform shutdown activites such as closing open resources - * - * @return void - */ - public function shutdown() - {} - - /** - * Write a message to the log. - * - * @param array $event log data event - * @return void - */ - abstract protected function _write($event); - - /** - * Validate and optionally convert the config to array - * - * @param array|Zend_Config $config Zend_Config or Array - * @return array - * @throws Zend_Log_Exception - */ - static protected function _parseConfig($config) - { - if ($config instanceof Zend_Config) { - $config = $config->toArray(); - } - - if (!is_array($config)) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception( - 'Configuration must be an array or instance of Zend_Config' - ); - } - - return $config; - } -} diff --git a/thirdparty/Zend/Log/Writer/Db.php b/thirdparty/Zend/Log/Writer/Db.php deleted file mode 100644 index 32039e4d1..000000000 --- a/thirdparty/Zend/Log/Writer/Db.php +++ /dev/null @@ -1,145 +0,0 @@ -_db = $db; - $this->_table = $table; - $this->_columnMap = $columnMap; - } - - /** - * Create a new instance of Zend_Log_Writer_Db - * - * @param array|Zend_Config $config - * @return Zend_Log_Writer_Db - */ - static public function factory($config) - { - $config = self::_parseConfig($config); - $config = array_merge(array( - 'db' => null, - 'table' => null, - 'columnMap' => null, - ), $config); - - if (isset($config['columnmap'])) { - $config['columnMap'] = $config['columnmap']; - } - - return new self( - $config['db'], - $config['table'], - $config['columnMap'] - ); - } - - /** - * Formatting is not possible on this writer - * - * @return void - * @throws Zend_Log_Exception - */ - public function setFormatter(Zend_Log_Formatter_Interface $formatter) - { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception(get_class($this) . ' does not support formatting'); - } - - /** - * Remove reference to database adapter - * - * @return void - */ - public function shutdown() - { - $this->_db = null; - } - - /** - * Write a message to the log. - * - * @param array $event event data - * @return void - * @throws Zend_Log_Exception - */ - protected function _write($event) - { - if ($this->_db === null) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Database adapter is null'); - } - - if ($this->_columnMap === null) { - $dataToInsert = $event; - } else { - $dataToInsert = array(); - foreach ($this->_columnMap as $columnName => $fieldKey) { - $dataToInsert[$columnName] = $event[$fieldKey]; - } - } - - $this->_db->insert($this->_table, $dataToInsert); - } -} diff --git a/thirdparty/Zend/Log/Writer/Firebug.php b/thirdparty/Zend/Log/Writer/Firebug.php deleted file mode 100644 index f8ae90375..000000000 --- a/thirdparty/Zend/Log/Writer/Firebug.php +++ /dev/null @@ -1,204 +0,0 @@ - Zend_Wildfire_Plugin_FirePhp::ERROR, - Zend_Log::ALERT => Zend_Wildfire_Plugin_FirePhp::ERROR, - Zend_Log::CRIT => Zend_Wildfire_Plugin_FirePhp::ERROR, - Zend_Log::ERR => Zend_Wildfire_Plugin_FirePhp::ERROR, - Zend_Log::WARN => Zend_Wildfire_Plugin_FirePhp::WARN, - Zend_Log::NOTICE => Zend_Wildfire_Plugin_FirePhp::INFO, - Zend_Log::INFO => Zend_Wildfire_Plugin_FirePhp::INFO, - Zend_Log::DEBUG => Zend_Wildfire_Plugin_FirePhp::LOG); - - /** - * The default logging style for un-mapped priorities - * - * @var string - */ - protected $_defaultPriorityStyle = Zend_Wildfire_Plugin_FirePhp::LOG; - - /** - * Flag indicating whether the log writer is enabled - * - * @var boolean - */ - protected $_enabled = true; - - /** - * Class constructor - * - * @return void - */ - public function __construct() - { - if (php_sapi_name() == 'cli') { - $this->setEnabled(false); - } - - $this->_formatter = new Zend_Log_Formatter_Firebug(); - } - - /** - * Create a new instance of Zend_Log_Writer_Firebug - * - * @param array|Zend_Config $config - * @return Zend_Log_Writer_Firebug - */ - static public function factory($config) - { - return new self(); - } - - /** - * Enable or disable the log writer. - * - * @param boolean $enabled Set to TRUE to enable the log writer - * @return boolean The previous value. - */ - public function setEnabled($enabled) - { - $previous = $this->_enabled; - $this->_enabled = $enabled; - return $previous; - } - - /** - * Determine if the log writer is enabled. - * - * @return boolean Returns TRUE if the log writer is enabled. - */ - public function getEnabled() - { - return $this->_enabled; - } - - /** - * Set the default display style for user-defined priorities - * - * @param string $style The default log display style - * @return string Returns previous default log display style - */ - public function setDefaultPriorityStyle($style) - { - $previous = $this->_defaultPriorityStyle; - $this->_defaultPriorityStyle = $style; - return $previous; - } - - /** - * Get the default display style for user-defined priorities - * - * @return string Returns the default log display style - */ - public function getDefaultPriorityStyle() - { - return $this->_defaultPriorityStyle; - } - - /** - * Set a display style for a logging priority - * - * @param int $priority The logging priority - * @param string $style The logging display style - * @return string|boolean The previous logging display style if defined or TRUE otherwise - */ - public function setPriorityStyle($priority, $style) - { - $previous = true; - if (array_key_exists($priority,$this->_priorityStyles)) { - $previous = $this->_priorityStyles[$priority]; - } - $this->_priorityStyles[$priority] = $style; - return $previous; - } - - /** - * Get a display style for a logging priority - * - * @param int $priority The logging priority - * @return string|boolean The logging display style if defined or FALSE otherwise - */ - public function getPriorityStyle($priority) - { - if (array_key_exists($priority,$this->_priorityStyles)) { - return $this->_priorityStyles[$priority]; - } - return false; - } - - /** - * Log a message to the Firebug Console. - * - * @param array $event The event data - * @return void - */ - protected function _write($event) - { - if (!$this->getEnabled()) { - return; - } - - if (array_key_exists($event['priority'],$this->_priorityStyles)) { - $type = $this->_priorityStyles[$event['priority']]; - } else { - $type = $this->_defaultPriorityStyle; - } - - $message = $this->_formatter->format($event); - - $label = isset($event['firebugLabel'])?$event['firebugLabel']:null; - - Zend_Wildfire_Plugin_FirePhp::getInstance()->send($message, - $label, - $type, - array('traceOffset'=>4, - 'fixZendLogOffsetIfApplicable'=>true)); - } -} diff --git a/thirdparty/Zend/Log/Writer/Mail.php b/thirdparty/Zend/Log/Writer/Mail.php deleted file mode 100644 index f2e1f97c9..000000000 --- a/thirdparty/Zend/Log/Writer/Mail.php +++ /dev/null @@ -1,430 +0,0 @@ - 'setFrom', - 'to' => 'addTo', - 'cc' => 'addCc', - 'bcc' => 'addBcc', - ); - - /** - * Class constructor. - * - * Constructs the mail writer; requires a Zend_Mail instance, and takes an - * optional Zend_Layout instance. If Zend_Layout is being used, - * $this->_layout->events will be set for use in the layout template. - * - * @param Zend_Mail $mail Mail instance - * @param Zend_Layout $layout Layout instance; optional - * @return void - */ - public function __construct(Zend_Mail $mail, Zend_Layout $layout = null) - { - $this->_mail = $mail; - if (null !== $layout) { - $this->setLayout($layout); - } - $this->_formatter = new Zend_Log_Formatter_Simple(); - } - - /** - * Create a new instance of Zend_Log_Writer_Mail - * - * @param array|Zend_Config $config - * @return Zend_Log_Writer_Mail - */ - static public function factory($config) - { - $config = self::_parseConfig($config); - $mail = self::_constructMailFromConfig($config); - $writer = new self($mail); - - if (isset($config['layout']) || isset($config['layoutOptions'])) { - $writer->setLayout($config); - } - if (isset($config['layoutFormatter'])) { - $layoutFormatter = new $config['layoutFormatter']; - $writer->setLayoutFormatter($layoutFormatter); - } - if (isset($config['subjectPrependText'])) { - $writer->setSubjectPrependText($config['subjectPrependText']); - } - - return $writer; - } - - /** - * Set the layout - * - * @param Zend_Layout|array $layout - * @return Zend_Log_Writer_Mail - * @throws Zend_Log_Exception - */ - public function setLayout($layout) - { - if (is_array($layout)) { - $layout = $this->_constructLayoutFromConfig($layout); - } - - if (!$layout instanceof Zend_Layout) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Mail must be an instance of Zend_Layout or an array'); - } - $this->_layout = $layout; - - return $this; - } - - /** - * Construct a Zend_Mail instance based on a configuration array - * - * @param array $config - * @return Zend_Mail - * @throws Zend_Log_Exception - */ - protected static function _constructMailFromConfig(array $config) - { - $mailClass = 'Zend_Mail'; - if (isset($config['mail'])) { - $mailClass = $config['mail']; - } - - if (!array_key_exists('charset', $config)) { - $config['charset'] = null; - } - $mail = new $mailClass($config['charset']); - if (!$mail instanceof Zend_Mail) { - throw new Zend_Log_Exception($mail . 'must extend Zend_Mail'); - } - - if (isset($config['subject'])) { - $mail->setSubject($config['subject']); - } - - $headerAddresses = array_intersect_key($config, self::$_methodMapHeaders); - if (count($headerAddresses)) { - foreach ($headerAddresses as $header => $address) { - $method = self::$_methodMapHeaders[$header]; - if (is_array($address) && isset($address['name']) - && !is_numeric($address['name']) - ) { - $params = array( - $address['email'], - $address['name'] - ); - } else if (is_array($address) && isset($address['email'])) { - $params = array($address['email']); - } else { - $params = array($address); - } - call_user_func_array(array($mail, $method), $params); - } - } - - return $mail; - } - - /** - * Construct a Zend_Layout instance based on a configuration array - * - * @param array $config - * @return Zend_Layout - * @throws Zend_Log_Exception - */ - protected function _constructLayoutFromConfig(array $config) - { - $config = array_merge(array( - 'layout' => 'Zend_Layout', - 'layoutOptions' => null - ), $config); - - $layoutClass = $config['layout']; - $layout = new $layoutClass($config['layoutOptions']); - if (!$layout instanceof Zend_Layout) { - throw new Zend_Log_Exception($layout . 'must extend Zend_Layout'); - } - - return $layout; - } - - /** - * Places event line into array of lines to be used as message body. - * - * Handles the formatting of both plaintext entries, as well as those - * rendered with Zend_Layout. - * - * @param array $event Event data - * @return void - */ - protected function _write($event) - { - // Track the number of entries per priority level. - if (!isset($this->_numEntriesPerPriority[$event['priorityName']])) { - $this->_numEntriesPerPriority[$event['priorityName']] = 1; - } else { - $this->_numEntriesPerPriority[$event['priorityName']]++; - } - - $formattedEvent = $this->_formatter->format($event); - - // All plaintext events are to use the standard formatter. - $this->_eventsToMail[] = $formattedEvent; - - // If we have a Zend_Layout instance, use a specific formatter for the - // layout if one exists. Otherwise, just use the event with its - // default format. - if ($this->_layout) { - if ($this->_layoutFormatter) { - $this->_layoutEventsToMail[] = - $this->_layoutFormatter->format($event); - } else { - $this->_layoutEventsToMail[] = $formattedEvent; - } - } - } - - /** - * Gets instance of Zend_Log_Formatter_Instance used for formatting a - * message using Zend_Layout, if applicable. - * - * @return Zend_Log_Formatter_Interface|null The formatter, or null. - */ - public function getLayoutFormatter() - { - return $this->_layoutFormatter; - } - - /** - * Sets a specific formatter for use with Zend_Layout events. - * - * Allows use of a second formatter on lines that will be rendered with - * Zend_Layout. In the event that Zend_Layout is not being used, this - * formatter cannot be set, so an exception will be thrown. - * - * @param Zend_Log_Formatter_Interface $formatter - * @return Zend_Log_Writer_Mail - * @throws Zend_Log_Exception - */ - public function setLayoutFormatter(Zend_Log_Formatter_Interface $formatter) - { - if (!$this->_layout) { - throw new Zend_Log_Exception( - 'cannot set formatter for layout; ' . - 'a Zend_Layout instance is not in use'); - } - - $this->_layoutFormatter = $formatter; - return $this; - } - - /** - * Allows caller to have the mail subject dynamically set to contain the - * entry counts per-priority level. - * - * Sets the text for use in the subject, with entry counts per-priority - * level appended to the end. Since a Zend_Mail subject can only be set - * once, this method cannot be used if the Zend_Mail object already has a - * subject set. - * - * @param string $subject Subject prepend text. - * @return Zend_Log_Writer_Mail - * @throws Zend_Log_Exception - */ - public function setSubjectPrependText($subject) - { - if ($this->_mail->getSubject()) { - throw new Zend_Log_Exception( - 'subject already set on mail; ' . - 'cannot set subject prepend text'); - } - - $this->_subjectPrependText = (string) $subject; - return $this; - } - - /** - * Sends mail to recipient(s) if log entries are present. Note that both - * plaintext and HTML portions of email are handled here. - * - * @return void - */ - public function shutdown() - { - // If there are events to mail, use them as message body. Otherwise, - // there is no mail to be sent. - if (empty($this->_eventsToMail)) { - return; - } - - if ($this->_subjectPrependText !== null) { - // Tack on the summary of entries per-priority to the subject - // line and set it on the Zend_Mail object. - $numEntries = $this->_getFormattedNumEntriesPerPriority(); - $this->_mail->setSubject( - "{$this->_subjectPrependText} ({$numEntries})"); - } - - - // Always provide events to mail as plaintext. - $this->_mail->setBodyText(implode('', $this->_eventsToMail)); - - // If a Zend_Layout instance is being used, set its "events" - // value to the lines formatted for use with the layout. - if ($this->_layout) { - // Set the required "messages" value for the layout. Here we - // are assuming that the layout is for use with HTML. - $this->_layout->events = - implode('', $this->_layoutEventsToMail); - - // If an exception occurs during rendering, convert it to a notice - // so we can avoid an exception thrown without a stack frame. - try { - $this->_mail->setBodyHtml($this->_layout->render()); - } catch (Exception $e) { - trigger_error( - "exception occurred when rendering layout; " . - "unable to set html body for message; " . - "message = {$e->getMessage()}; " . - "code = {$e->getCode()}; " . - "exception class = " . get_class($e), - E_USER_NOTICE); - } - } - - // Finally, send the mail. If an exception occurs, convert it into a - // warning-level message so we can avoid an exception thrown without a - // stack frame. - try { - $this->_mail->send(); - } catch (Exception $e) { - trigger_error( - "unable to send log entries via email; " . - "message = {$e->getMessage()}; " . - "code = {$e->getCode()}; " . - "exception class = " . get_class($e), - E_USER_WARNING); - } - } - - /** - * Gets a string of number of entries per-priority level that occurred, or - * an emptry string if none occurred. - * - * @return string - */ - protected function _getFormattedNumEntriesPerPriority() - { - $strings = array(); - - foreach ($this->_numEntriesPerPriority as $priority => $numEntries) { - $strings[] = "{$priority}={$numEntries}"; - } - - return implode(', ', $strings); - } -} diff --git a/thirdparty/Zend/Log/Writer/Mock.php b/thirdparty/Zend/Log/Writer/Mock.php deleted file mode 100644 index bbd0f71b9..000000000 --- a/thirdparty/Zend/Log/Writer/Mock.php +++ /dev/null @@ -1,81 +0,0 @@ -events[] = $event; - } - - /** - * Record shutdown - * - * @return void - */ - public function shutdown() - { - $this->shutdown = true; - } - - /** - * Create a new instance of Zend_Log_Writer_Mock - * - * @param array|Zend_Config $config - * @return Zend_Log_Writer_Mock - */ - static public function factory($config) - { - return new self(); - } -} diff --git a/thirdparty/Zend/Log/Writer/Null.php b/thirdparty/Zend/Log/Writer/Null.php deleted file mode 100644 index 5d5629615..000000000 --- a/thirdparty/Zend/Log/Writer/Null.php +++ /dev/null @@ -1,56 +0,0 @@ -_stream = $streamOrUrl; - } else { - if (is_array($streamOrUrl) && isset($streamOrUrl['stream'])) { - $streamOrUrl = $streamOrUrl['stream']; - } - - if (! $this->_stream = @fopen($streamOrUrl, $mode, false)) { - require_once 'Zend/Log/Exception.php'; - $msg = "\"$streamOrUrl\" cannot be opened with mode \"$mode\""; - throw new Zend_Log_Exception($msg); - } - } - - $this->_formatter = new Zend_Log_Formatter_Simple(); - } - - /** - * Create a new instance of Zend_Log_Writer_Stream - * - * @param array|Zend_Config $config - * @return Zend_Log_Writer_Stream - */ - static public function factory($config) - { - $config = self::_parseConfig($config); - $config = array_merge(array( - 'stream' => null, - 'mode' => null, - ), $config); - - $streamOrUrl = isset($config['url']) ? $config['url'] : $config['stream']; - - return new self( - $streamOrUrl, - $config['mode'] - ); - } - - /** - * Close the stream resource. - * - * @return void - */ - public function shutdown() - { - if (is_resource($this->_stream)) { - fclose($this->_stream); - } - } - - /** - * Write a message to the log. - * - * @param array $event event data - * @return void - * @throws Zend_Log_Exception - */ - protected function _write($event) - { - $line = $this->_formatter->format($event); - - if (false === @fwrite($this->_stream, $line)) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception("Unable to write to stream"); - } - } -} diff --git a/thirdparty/Zend/Log/Writer/Syslog.php b/thirdparty/Zend/Log/Writer/Syslog.php deleted file mode 100644 index a476c6001..000000000 --- a/thirdparty/Zend/Log/Writer/Syslog.php +++ /dev/null @@ -1,267 +0,0 @@ - LOG_EMERG, - Zend_Log::ALERT => LOG_ALERT, - Zend_Log::CRIT => LOG_CRIT, - Zend_Log::ERR => LOG_ERR, - Zend_Log::WARN => LOG_WARNING, - Zend_Log::NOTICE => LOG_NOTICE, - Zend_Log::INFO => LOG_INFO, - Zend_Log::DEBUG => LOG_DEBUG, - ); - - /** - * The default log priority - for unmapped custom priorities - * - * @var string - */ - protected $_defaultPriority = LOG_NOTICE; - - /** - * Last application name set by a syslog-writer instance - * - * @var string - */ - protected static $_lastApplication; - - /** - * Last facility name set by a syslog-writer instance - * - * @var string - */ - protected static $_lastFacility; - - /** - * Application name used by this syslog-writer instance - * - * @var string - */ - protected $_application = 'Zend_Log'; - - /** - * Facility used by this syslog-writer instance - * - * @var int - */ - protected $_facility = LOG_USER; - - /** - * Types of program available to logging of message - * - * @var array - */ - protected $_validFacilities = array(); - - /** - * Class constructor - * - * @param array $params Array of options; may include "application" and "facility" keys - * @return void - */ - public function __construct(array $params = array()) - { - if (isset($params['application'])) { - $this->_application = $params['application']; - } - - $runInitializeSyslog = true; - if (isset($params['facility'])) { - $this->setFacility($params['facility']); - $runInitializeSyslog = false; - } - - if ($runInitializeSyslog) { - $this->_initializeSyslog(); - } - } - - /** - * Create a new instance of Zend_Log_Writer_Syslog - * - * @param array|Zend_Config $config - * @return Zend_Log_Writer_Syslog - */ - static public function factory($config) - { - return new self(self::_parseConfig($config)); - } - - /** - * Initialize values facilities - * - * @return void - */ - protected function _initializeValidFacilities() - { - $constants = array( - 'LOG_AUTH', - 'LOG_AUTHPRIV', - 'LOG_CRON', - 'LOG_DAEMON', - 'LOG_KERN', - 'LOG_LOCAL0', - 'LOG_LOCAL1', - 'LOG_LOCAL2', - 'LOG_LOCAL3', - 'LOG_LOCAL4', - 'LOG_LOCAL5', - 'LOG_LOCAL6', - 'LOG_LOCAL7', - 'LOG_LPR', - 'LOG_MAIL', - 'LOG_NEWS', - 'LOG_SYSLOG', - 'LOG_USER', - 'LOG_UUCP' - ); - - foreach ($constants as $constant) { - if (defined($constant)) { - $this->_validFacilities[] = constant($constant); - } - } - } - - /** - * Initialize syslog / set application name and facility - * - * @return void - */ - protected function _initializeSyslog() - { - self::$_lastApplication = $this->_application; - self::$_lastFacility = $this->_facility; - openlog($this->_application, LOG_PID, $this->_facility); - } - - /** - * Set syslog facility - * - * @param int $facility Syslog facility - * @return Zend_Log_Writer_Syslog - * @throws Zend_Log_Exception for invalid log facility - */ - public function setFacility($facility) - { - if ($this->_facility === $facility) { - return $this; - } - - if (!count($this->_validFacilities)) { - $this->_initializeValidFacilities(); - } - - if (!in_array($facility, $this->_validFacilities)) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Invalid log facility provided; please see http://php.net/openlog for a list of valid facility values'); - } - - if ('WIN' == strtoupper(substr(PHP_OS, 0, 3)) - && ($facility !== LOG_USER) - ) { - require_once 'Zend/Log/Exception.php'; - throw new Zend_Log_Exception('Only LOG_USER is a valid log facility on Windows'); - } - - $this->_facility = $facility; - $this->_initializeSyslog(); - return $this; - } - - /** - * Set application name - * - * @param string $application Application name - * @return Zend_Log_Writer_Syslog - */ - public function setApplicationName($application) - { - if ($this->_application === $application) { - return $this; - } - $this->_application = $application; - $this->_initializeSyslog(); - return $this; - } - - /** - * Close syslog. - * - * @return void - */ - public function shutdown() - { - closelog(); - } - - /** - * Write a message to syslog. - * - * @param array $event event data - * @return void - */ - protected function _write($event) - { - if (array_key_exists($event['priority'], $this->_priorities)) { - $priority = $this->_priorities[$event['priority']]; - } else { - $priority = $this->_defaultPriority; - } - - if ($this->_application !== self::$_lastApplication - || $this->_facility !== self::$_lastFacility) - { - $this->_initializeSyslog(); - } - - $message = $event['message']; - if ($this->_formatter instanceof Zend_Log_Formatter_Interface) { - $message = $this->_formatter->format($event); - } - - syslog($priority, $message); - } -} diff --git a/thirdparty/Zend/Log/Writer/ZendMonitor.php b/thirdparty/Zend/Log/Writer/ZendMonitor.php deleted file mode 100644 index ad12526e1..000000000 --- a/thirdparty/Zend/Log/Writer/ZendMonitor.php +++ /dev/null @@ -1,131 +0,0 @@ -_isEnabled = false; - } - if (function_exists('zend_monitor_custom_event')) { - $this->_isZendServer = true; - } - } - - /** - * Create a new instance of Zend_Log_Writer_ZendMonitor - * - * @param array|Zend_Config $config - * @return Zend_Log_Writer_ZendMonitor - */ - static public function factory($config) - { - return new self(); - } - - /** - * Is logging to this writer enabled? - * - * If the Zend Monitor extension is not enabled, this log writer will - * fail silently. You can query this method to determine if the log - * writer is enabled. - * - * @return boolean - */ - public function isEnabled() - { - return $this->_isEnabled; - } - - /** - * Log a message to this writer. - * - * @param array $event log data event - * @return void - */ - public function write($event) - { - if (!$this->isEnabled()) { - return; - } - - parent::write($event); - } - - /** - * Write a message to the log. - * - * @param array $event log data event - * @return void - */ - protected function _write($event) - { - $priority = $event['priority']; - $message = $event['message']; - unset($event['priority'], $event['message']); - - if (!empty($event)) { - if ($this->_isZendServer) { - // On Zend Server; third argument should be the event - zend_monitor_custom_event($priority, $message, $event); - } else { - // On Zend Platform; third argument is severity -- either - // 0 or 1 -- and fourth is optional (event) - // Severity is either 0 (normal) or 1 (severe); classifying - // notice, info, and debug as "normal", and all others as - // "severe" - monitor_custom_event($priority, $message, ($priority > 4) ? 0 : 1, $event); - } - } else { - monitor_custom_event($priority, $message); - } - } -}