Yii framework: writing errors in apache log only - php

I'd like to write errors about r/w permissions of /protected/runtime directory into apache error log and do not throw any exception into interface. What i should do to resolve my 'problem'?
Thank you and sorry for my english!
the problem was resolved by using:
error_log($message);
and removing CException calls when script is trying to check runtime folder or run file_put_contents function

When debug is off yii is displaying this error using (by default) site/error view, just change it to not display message text and display something generic (ex if code == 500 then display "Internal Error")
If you want to have error in apache log I see only a bit unclean way, anyway just tested and it works. Set display_errors = Off and log_errors = On in php.ini and then change action that handles errors from yii to restore default handlers and rise error again - it should result in log record in apache log and blank screen in browser, ex:
public function actionError() {
if (($error=Yii::app()->errorHandler->error)) {
if (strpos($error['message'], 'runtime') !== false) {
restore_error_handler();
restore_exception_handler();
user_error("Problem with runtime: " . $error['message'], E_USER_ERROR);
exit();
}
if(Yii::app()->request->isAjaxRequest) {
echo $error['message'];
} else {
$this->render('error', $error);
}
}
}

Related

PHP Static Function Called Twice

I'm building a MVC PHP framework. I'm working on error logging. I'm testing a system to show and log (in a .txt file) errors thrown by a try, catch block. Since it's short, I've included the whole of index.php:
<?php
// *** SETUP THE DOCUMENT ***
// Declare strict_types.
declare(strict_types = 1);
// Include the autoload file.
require_once "classes/autoload.php";
// Create a new config variable to allow us to call the config file from anywhere.
new Config();
// Setup error reporting according to the config.
error_reporting(Config::$config['defaults']['errReporting']);
ini_set("display_errors", Config::$config['defaults']['ini_setErrors']);
// Errors::showCatch("Test");
try {
$test = new QueryExamples();
$test->getTexts();
} catch (Error $e) {
Errors::showCatch($e);
}
unset($test);
I've got an autoloader setup already. I'll include the code of that if anyone wants it. The next bit is a file called errors.php which is called by the try, catch and, depending on how the configuration file for the framework is setup, shows the message on the screen and/or sends it to a log file:
class Errors{
static function showCatch($errMessage){
// If manual errors are turned on in the config file.
if(Config::$config['defaults']['manErrors'] == 1){
// Show the error message.
echo "<br />{$errMessage}<br />";
}
// Log the error.
Logs::logManError($errMessage);
die();
}
}
How and where it is logged in the file is handled by another static function in another document:
class Logs{
static function logManError($errMessage) {
// If custom error logging is turned on in the config file:
if (Config::$config['defaults']['customErrorsLogging'] == 1) {
// Get the file path from the config file.
$manErrors_file = Config::$config['paths']['customErrors'];
// Format the error message.
$logTxt = "New Custom Error - [".date("d/m/Y - H:i:s")."]\n".$errMessage."\n\n";
file_put_contents($manErrors_file, $logTxt, FILE_APPEND);
}
}
}
The output in the log file:
New Custom Error - [23/03/2020 - 01:50:17]
Error: (Error message)
New Custom Error - [23/03/2020 - 01:50:17]
Error: (Error message)
I changed the error message to try and find where the errors were being called:
// Format the error message.
// $logTxt = "New Custom Error - [".date("d/m/Y - H:i:s")."]\n".$errMessage."\n\n";
$logTxt = debug_backtrace()["0"]['file']." ".debug_backtrace()['0']['line']."\n".debug_backtrace()['1']['file']." ".debug_backtrace()['1']['line']."\n\n";
The output in the log file:
F:\xampp\htdocs\FRED 0.0.0\classes\errors.php 21
F:\xampp\htdocs\FRED 0.0.0\index.php 22
F:\xampp\htdocs\FRED 0.0.0\classes\errors.php 21
F:\xampp\htdocs\FRED 0.0.0\index.php 22
errors.php:21 is where I called the the Logs:logManError() static function and index.php:22 is where I called the Errors:showCatch() function in the try, catch block.
I also tried adding a global variable to index.php to check how many times the showCatch() function was being called:
(index.php)
$GLOBALS['i'] = 0;
(errors.php)
$GLOBALS['i'] += 1;
// Log the error.
Logs::logManError($errMessage."<br />".$GLOBALS['i']);
die();
The output:
New Custom Error - [23/03/2020 - 01:50:17]
Error: (Error message)
1
New Custom Error - [23/03/2020 - 01:50:17]
Error: (Error message)
1
Okay. Here is the answer. After doing more research there appears to be nothing wrong with my code. It seems that it's something to do with the rewrite engine combined with a few other things. I've included a link to the page that gave me the solution.
https://www.sitepoint.com/community/t/duplicate-entries-with-file-put-contents-when-rewriteengine-on/16188/11

Why does fopen fail within a register shutdown function?

I am working on a site in a shared host. I don't have access to the PHP ini file, or the PHP ini_set function, and I can't use php_flag or php_value directives.
I'm using the set_error_handler function to log most errors, and trying to use the register_shutdown_function to log fatal ones. I've tried several of the solutions for catching fatal errors here: How do I catch a PHP Fatal Error
. I can successfully use the solutions there to either display the errors on screen or bring up a custom error page, and to log non-fatal errors, but I can't seem to log the fatal errors.
This is simplest version of my attempts:
function errorHandler($num, $str, $file, $line) {
$now = strftime("%B %d, %Y at %I:%M %p", time());
$text = "{$now} → error {$num} in {$file}, line {$line}: {$str}\n";
$errorlog = "my_path/my_log";
$new = file_exists($errorlog) ? false : true;
if($handle = fopen($errorlog, 'a')) {
fwrite($handle, $text);
fclose($handle);
if($new) { chmod($errorlog, 0755); }
} else {
print("Could not open log file for writing"); //I'm using this to test
}
}
function fatalHandler() {
$error = error_get_last();
if($error) {
errorHandler($error["type"], $error["message"], $error["file"], $error["line"]);
}
}
set_error_handler("errorHandler");
register_shutdown_function("fatalHandler");
When I test this with a non-fatal error, like echo $undefined_variable, it works and the error is logged properly. However if I test with a fatal error, like undefined_function(), it does not log anything and prints "could not open log file". If I test with both errors present, it logs the non-fatal one, but not the fatal one.
Your question has been identified as a possible duplicate of another question. If the answers there do not address your problem, please edit to explain in detail the parts of your question that are unique.
#Vic Seedoubleyew -Well, the most obvious thing is the warning addressed in the question you mentioned failed to open stream: ...etc has nothing to do with my situation. That warning never happened. The test message: "could not open log file for writing" is my own message that I wrote to myself, so I would have some evidence that the if statement was being processed. I made that message up. It's noted very clearly in my question that the message is of my own making. Anyone experiencing the problem I was will never get the message failed to open stream...etc, so they won't be helped by a question addressing a message they never got. I assume the point of all this is to actually help dummies like me.
The title: "Why does fopen fail within a register shutdown function" was not my title. That is someone else's edit. My question was why I couldn't write to a log within the register_shutdown_function. I didn't know whether fopen was failing or there was some other problem. I was trying to log something out of the function and it wasn't working. Removing the stated goal (logging out of the function) from the question, and adding someone else's assessment "why does fopen fail", actually makes it much less useful to anyone coming here for a solution to the situation I was experiencing. Had there already been a question present which addressed writing to a file within the shutdown function, I would have found it. "Why does fopen fail" is too specific to be useful in a search by someone who doesn't know why they can't write to a file in the function.
I wasn't going to say anything about the edit, but since you have asked me to explain, I'm explaining. I realize everyone probably gets points for edits, but take a minute to consider the effect your edit has on the usefulness of the question to someone who comes here with the situation I had.
Are you using a relative path to your custom error log? If so, this note on the register_shutdown_function page might be relevant:
Working directory of the script can change inside the shutdown function under some web servers, e.g. Apache.
In fact, the second comment says:
If you want to do something with files in function, that registered in register_shutdown_function(), use ABSOLUTE paths to files instead of relative. Because when script processing is complete current working directory chages to ServerRoot (see httpd.conf)
Other things I'll mention:
Your variable $error, and the logic if ($error) { ... is misleading. See, when you register a function using register_shutdown_function, you are telling PHP to invoke that function every single time your script finishes executing - regardless of whether or not there was an error. This means that fatalHandler is invoked even when your script finishes with a non-fatal error, or even no error at all!
Since you have an alternative method for dealing with non-fatal errors (using set_error_handler), you should specifically check for fatal errors in fatalHandler:
function fatalHandler() {
if( $error !== NULL && $error['type'] == E_ERROR) {
errorHandler($error["type"], $error["message"], $error["file"], $error["line"]);
header("HTTP/1.1 500 Internal Server Error");
exit();
}
}
See the list of PHP error levels here.
You may not be aware of this, but PHP has a built-in error-logging function. By default, it writes to the file specified for error_log in your php.ini.
If you are interested in more advanced logging, and generally taking your PHP-fu to the next level, I suggest looking into the Monolog package. It is more or less considered the universal standard for logging in professional PHP communities.
For this situations I just include file
that has content like that:
function ErrorHandler($errno, $errstr, $errfile, $errline) {
if(in_array($errno, [E_NOTICE, E_USER_NOTICE, E_WARNING])) { //, E_WARNING, E_CORE_WARNING, E_DEPRECATED, E_USER_DEPRECATED])) {
return;
}
$err_type='ERROR ';
switch ($errno)
{
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_PARSE:
$err_type='FATAL ';
break;
case E_USER_ERROR:
case E_RECOVERABLE_ERROR:
$err_type='ERROR ';
break;
case E_WARNING:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
case E_USER_WARNING:
$err_type='WARNING ';
break;
case E_NOTICE:
case E_USER_NOTICE:
$err_type='NOTICE ';
break;
case E_STRICT:
$err_type='STRICT ';
break;
}
$log_text = $err_type.$errfile.'('.$errline.')'.' : ('.$errno.') '.$errstr;
Logger::log('ErrorHandler', $log_text); // Logger is custom class that appends to log file or sends email, You can write same one
}
function ShutdownHandler() {
$last_error=error_get_last();
if(!empty($last_error)) {
if(in_array($last_error['type'], [E_NOTICE, E_USER_NOTICE])) { //, E_WARNING, E_CORE_WARNING, E_DEPRECATED, E_USER_DEPRECATED])) {
return;
}
$error = [];
foreach($last_error AS $key => $val) {
$error[] = $key.' : '.$val."\n";
}
Logger::log('ShutdownHandler', implode(', ', $error));
exit(-1);
}
}
function ExceptionHandler($exception) {
Logger::log('ExceptionHandler', $exception->getFile().'('.$exception->getLine().')'.' : '.$exception->getMessage());
}
// here I register handlers for different types of situations
set_error_handler("ErrorHandler", E_ALL);
set_exception_handler("ExceptionHandler");
register_shutdown_function("ShutdownHandler");
And my Logger class:
class Logger {
public static $logDir;
public static setLogDir($dir) {
self::$logDir = realpath($dir);
}
public static logFile($label = '') {
return
self::$logDir.'/'
.date('Ymd')
.(($label!='')?'.'.$label:'')
.'.log'; // example: /somepath/logs/20160906.ExceptionHandler.log
}
public static function log($label, $data) {
#file_put_contents(self::logFile($label), $data, FILE_APPEND);
}
}
Logger::setLogDir(__DIR__.'/logs'); // modify logs path as You wish
p.s. usually no need to have special logs, if You've full access to system You can enable error_log in php settings.

Handling PHP / mysql errors (including fatal) via custom error page

What is the best way to handle errors on front end websites? Similar to twitter, reddit, etc that have a nice 'something went wrong' page. I want to:
detect errors and be notified, even if its just to a log file
show a custom error page such as the fail whale or similar, so its
obvious something has gone wrong instead of showing nothing, or a horrible looking error.
Ideally anything that prevents a user from seeing the site, like fatal errors, custom exceptions, mysql being down, random 500 errors etc, should show the custom error page.
Fatal Errors
PHP : Custom error handler - handling parse & fatal errors - works ok, but if this happens at the end of a script, and it has already output something, this wont work
Non Fatal Errors
http://davidwalsh.name/custom-error-handling-php
MySQL Errors
Ideally need to cover everything from a simple query error, to the server not being there. Imagine its as simple as detecting the error in my database class and calling redirecting to / calling the error method
Server Errors
Is this something I should be doing in PHP OR Apache OR both?
Code at the moment
Currently I have the following from a mix of various other SO's, but fatal errors will just be reported after the rest of the page has loaded.
Anyone have any ideas for something that will work with all of the above?
<?
function xhandler($number,$string,$file,$line,$context)
{
//log to text file?
//log to xml file?
//store in database?
//whatever you want to do!
echo "<h1>here be dragons!</h1>";
echo "$number,$string,$file,$line,$context";
exit();
}
function fatal_handler() {
$errfile = "unknown file";
$errstr = "shutdown";
$errno = E_CORE_ERROR;
$errline = 0;
$error = error_get_last();
if( $error !== NULL) {
$errno = $error["type"];
$errfile = $error["file"];
$errline = $error["line"];
$errstr = $error["message"];
}
echo "<h1>here be dragons!</h1>";
print_r($error);
exit();
}
register_shutdown_function( "fatal_handler" );
set_error_handler('xhandler',E_ALL);
include('show_the_content');
function_that_doesnt_exist();
echo $unterminated_line_of_code
?>
You can't catch fatal errors. They are fatal, script execution stops immediately when they're encountered. These errors should never appear on production sites.
Notices and warnings should be suppressed from showing in HTML on production environment (live sites).
What these sites usually do is handle HTTP errors with PHP. You need to redirect these errors to a PHP script which will handle them. For example, in Apache you do it with the ErrorDocument directive. You can put these directives (one for each HTTP error code) in the server configuration or in the .htaccess file in the web site's document root.
In the PHP script, you can filter different HTTP errors with $_SERVER['REDIRECT_STATUS'], if you need to. When you receive a request with the desired HTTP error, you can handle it any way you wish.

register_shutdown_function() still outputs original error message

I am trying to replace the built in php shutdown_function with a custom one.
It works perfectly, however, it still outputs the original error (built in error) above my new error message.
<?php
function shutdown_output() {
$error = error_get_last();
if($error !== NULL) {
echo "ERROR";
exit();
} else {
echo "NO ERROR";
}
}
// Set the error reporting:
register_shutdown_function('shutdown_output');
// test.php does not exist, just here to get a critical error
require_once("test.php");
?>
Any ideas?
As already mentioned in comments, the use of register_shutdown_function is not going to override built-in error handling (the same way that set_error_handler doesn't either).
If you don't want to see the original message, disable the output of them in your php.ini using display_errors = 0. Or on the fly in your script using ini_set('display_errors', 0);.

register_shutdown_function : registered function cannot include files if terminating on failure?

Greetings,
I am writing some code inside a framework for PHP 5.3, and I am trying to catch all errors in a way that will allow me to gracefully crash on client side and add some log entry at the same time. To be sure to also catch parse errors, I am using register_shutdown_function to specifically catch parse errors.
Here is the function that I register
static function shutdown()
{
if(is_null($e = error_get_last()) === FALSE)
if($e["type"] == E_PARSE)
self::error($e["type"], $e["message"], $e["file"], $e["line"], array(self::$url));
}
The error method does two things :
It adds an error entry to a log file using fopen in append.
It execute an error display: it explicitely sets the HTTP code to 500, and display a custom format 500 error page. Some include (which I do within a wapper class, but is only an include for now) are required from there
For some reason, I can fopen my log file and append, but cannot do a simple include; it just silently dies from there.
Here is what the log outputs if I add a Log entry for each includes
static public function file($file)
{
if(class_exists("Logs"))
Logs::append("errors.log", $file . ":" . ((include $file) ? 1 : 0));
else
include $file;
}
// Inside the Logs class...
static public function append($file, $message)
{
if(!is_scalar($message))
$message = print_r($message, true);
$fh = fopen(Config::getPath("LOGS") . "/" . $file, 'a');
fwrite($fh, $message."\r\n");
fclose($fh);
}
Here what the log file gives me:
/Users/mt/Documents/workspace/core/language.php:1
...
/Users/mt/Documents/workspace/index.php:1
/Users/mt/Documents/workspace/core/view.php:1
[2010-01-31 08:16:31] Parsing Error (Code 4) ~ syntax error, unexpected T_VARIABLE {/Users/mt/Documents/workspace/controllers/controller1.php:13}
After the parse error is hitted, it does run the registered function, but as soon as it hits a new include file, it dies of a silent death... is there a way to circumvent this? Why would I be able to open a file for read/write, but not for inclusion?
Try to use Lagger
It would seem that it is related with either something in the config, either with some build specifics.
I ran the code initially on MacOSX, which would not run and fail as described, but it runs on a compiled version of PHP under Ubuntu.
Which is kinda fine for me, but pretty much makes me wonder why it still fails under OSX (XAMPP to be more precise).

Categories