Jump to content
Larry Ullman's Book Forums

Recommended Posts

Hi Larry

 

In your book you create a my_error_handler which sends error messages to an e-mail address (and it works very well). Just one question - would it be possible in php to write the error message to a file and save it on the server?

 

e.g.

 

error0001.txt

error0002.txt

error0003.txt

 

etc. etc

Link to comment
Share on other sites

I'm not Larry but I have done this a few times, so hope you don't mind if I add a reply. There are a number of php functions that allow you to manipulate a file and they are well documented on the web. Start with fopen, fwrite and fclose.

Link to comment
Share on other sites

Why not use a database instead? Sound very boring to open several files or find the correct file.

 

An alternative would be creating one file for each day instead of each error. That way, you'll have it more organized. Should be pretty easy to create. Throw in a few date/time functions to what margaux said, and you are good to go.

 

Edit: And by the way? Doesn't errors get logged in files by default?

Link to comment
Share on other sites

Hi Margaux

Have been looking through Larry's books and can't seem to find much on writing to files. Uploading images and files, yes, but actually inserting text into a .txt file no. Would appreciate any link you could send me.

Link to comment
Share on other sites

O.K. - will have to review that chapter again.

 

I have put together the bare bones of a config.php script that will save error messages to MySql instead of sending an e-mail.

This is purely an intellectual exercise and I would appreciate any criticism - constructive or otherwise. Just accept that I'm an amateur!

 

This is the configuration file - feel free to copy:.....

 

--------------------------------------------------------------------------

 

<?php

/* -----------------------------config.php---------------------------------------

Written by: Max Kite

This is purely an exercise but feel free to use it if you wish.

This program catches errors and saves them either to text or MySql. It is a version of my_error_handler from page 494 of Larry's book but with the function sending error messages to SQL as opposed to e-mail.

SQL:...The program needs to see if the error has happened before because we don't want the same error registered a million times on the database. If the error is new, it will be saved on SQL for a programmer to retrieve at will.

This program will run in background and will not be seen by users, hence no HTML used.

 

*/

require ('log6.php'); //Get database connection

date_default_timezone_set ('Europe/Madrid'); //Set local date (Spain)

function my_error_handler ($e_number, $e_message, $e_file, $e_line, $e_vars) //See page 494 of book for explanation.

{

require ('log6.php'); // log6.php is the dbc connection script and needs to be inside the function to connect to My Sql.

$file = $e_file; // a bit tidier var.

$file = str_replace('\\', '/', $file); //MySql can't save backslashes in utf-8 so need to change all to forward slash.

$line = $e_line; //a bit tidier;

$message = "<p>Error in the script " . $file . " on line " . $e_line; //Build the error message - see book

$message .= "<br /><br />" . $e_message;

$message2 = print_r ($e_vars, 1); //Keep the variable listing separate from message (unlike the book)

$errornumber = $e_number;

$errorexists = 0; //This is the switch which assigns a value depending on whether the error has been previously saved. 0 = not saved 1=saved

/*Run an enquiry on errors table of db to pass through every record*/

$checkerrors1 = "SELECT * FROM errors"; // Build MySQL query - retieve all files from errors table.

$checkerrors2 = mysqli_query($dbc, $checkerrors1); //Make query

while ($checkerrors3 = mysqli_fetch_array($checkerrors2)) //Use while loop to fetch all records in the errors table array

{

//Pass through the db looking for any matching error - firs tidying up variable names

$errorFile = $checkerrors3['File']; //Get the name of the errant file

$errorLine = $checkerrors3['Line']; //Get line number

$errortype = $checkerrors3['ErrorLevel']; //Get error level

 

//Now see if the record selected matches the criteria for our error.....

if (($errorFile == $file) && ($errorLine == $line) && ($errortype == $errornumber)) // If the error is already on the db then assign 1 to switch ($errorexists)

{

$errorexists = 1;

}

} //End while

if ($errorexists == 0) // Now save error if it has not been found and therefore $errorexists = 0

{

//Write the query to append the error details to the db.......

$saveerror1 = "INSERT INTO errors (ErrorLevel, Error, File, Line, Variables) VALUES ('$errornumber', '$message', '$file', '$line', '$message2')";

$saveerror2 = @mysqli_query ($dbc, $saveerror1); //run query

}

} //Close function my_error_handler

set_error_handler ('my_error_handler'); //tell php to use my_error_handler

 

?>

 

 

-------------------------------------------------------------

 

 

On top, I have written two scripts to read the db. They are missing html headers etc. but do the job.......

 

 

----------------------------------------------------------------

 

<?php

/* Bare bones script to list errors db

--------------------seeerrors.php---------------

*/

require_once ('log6.php'); // Get db connection script

if (isset($_GET['Ref2'])) //If seeerrors2.php or seeerrors.php have returned a GET with a ref number/All, change 'viewed' in db to 1 to preclude the script from showing the errors already seen.

{

$done = $_GET['Ref2']; //Nicer variable name

$done1 = "UPDATE errors SET Viewed = '1' WHERE Ref = $done"; // Put a 1 in viewed column so that it won't be seen again unless specifically requested.

$done2 = mysqli_query ($dbc, $done1); //Run the query to change the viewed column to '1'

 

//Now we use $done to see if the user wants to see all errors including "deleted" ones. If user has clicked on "See all", then "All" will be in "$done"

if ($done == 'All')

{

$geterror1 = "SELECT * FROM errors"; //Write query to list all unviewed errors

$choice = '<a href = "seeerrors.php">See new</a>';

}

else

{

$geterror1 = "SELECT * FROM errors WHERE viewed = 0"; //Write query to list all errors

$choice = '<a href = "seeerrors.php?Ref2=All">See all errors</a>';

} //end if

}

else

{

$geterror1 = "SELECT * FROM errors WHERE viewed = 0"; //Write query to list all errors

$choice = '<a href = "seeerrors.php?Ref2=All">See all errors</a>';

} //end if

 

echo "ERROR LISTING <br /><br />";

?>

<table width = "500px"><tr><td>DATE</td><td>ERROR LEVEL</td><td>FILE</td></tr>

<?php

 

$geterror2 = mysqli_query ($dbc, $geterror1); //Do the query

while ($geterror3 = mysqli_fetch_array($geterror2)) //Pass through $geterror3 array to list records

{ // Do the listing:.....

echo '<tr><td><a href = "seeerrors2.php?Ref=' . $geterror3['Ref'] . '">' . $geterror3['Date'] . '</a></td><td align = "center">' . $geterror3['ErrorLevel'] . '</td><td>' . $geterror3['File'] . '</td></tr>';

}

echo '</table><br /><br />'; // Close table

echo $choice;

?>

 

 

---------------------------------------------------

 

Then individual errors can be viewed using seeerrors2.php..........

---------------------------------------------------

 

<?php

echo "ERROR REPORT<br /><br /><br />";

require_once ('log6.php'); //get connction script

?>

<table width = "1000px" border = "0">

<?php

$errorref = $_GET['Ref']; //Get the error reference No from seeerrors.php

$geterror1 = "SELECT * FROM errors WHERE Ref = $errorref"; // Make the query

$geterror2 = mysqli_query ($dbc, $geterror1);

while ($geterror3 = mysqli_fetch_array($geterror2))

{

echo '<tr><td>Ref Nº</td><td>' . $geterror3['Ref'] . '</td></tr>';

echo '<tr><td>Date</td><td>' . $geterror3['Date'] . '</td></tr>';

echo '<tr><td>Error level</td><td>' . $geterror3['ErrorLevel'] . '</td></tr>';

echo '<tr><td valign = "top">Error</td><td>' . $geterror3['Error'] . '</td></tr>';

echo '<tr><td>File</td><td>' . $geterror3['File'] . '</td></tr>';

echo '<tr><td>Variables</td><td>' . $geterror3['Variables'] . '</td></tr>';

}

 

echo '</table><br /><br />';

echo '<p><a href = "seeerrors.php?Ref2=' . $errorref . '">Done</a></p>';

 

 

?>

 

 

-----------------------------------------------------

 

Here's the SQL to set up the db:......

 

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

SET time_zone = "+00:00";

 

CREATE TABLE IF NOT EXISTS `errors` (

`Ref` int(100) NOT NULL AUTO_INCREMENT,

`Date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

`ErrorLevel` int(6) unsigned NOT NULL,

`Error` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,

`File` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,

`Line` int(6) unsigned NOT NULL,

`Variables` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,

`Viewed` tinyint(3) unsigned NOT NULL,

PRIMARY KEY (`Ref`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2774 ;

 

 

 

 

Still working on a script to save to file.

Link to comment
Share on other sites

Just a thought. You only save errors to DB if they are not previously saved, right? Why fetch ALL rows and check each one agains the current error? Why not just find a way to check if that particular error is already saved in the DB by a simple query? If no match is found, then you save it.

 

You would of course have to use enough info in your query to make sure it's a good check, but that would be a LOT faster than checking each row. I wrote some pseudo code just so you can kind of follow my thought.

 

$query = "SELECT * FROM errors WHERE File = '{$file}' AND Line = '{$line}' AND ErrorLevel = '($errorLevel}';

// Run query etc....

if ( mysqli_num_rows($result) > 0 )
{
   // Save error to DB... It's unique
}
// No need for an else. Error already saved... (Maybe update a DateTime field or something)

 

Also... Watch out for SQL injection. These lines are dangerous for obvious reasons.

 

$done = $_GET['Ref2']; //Nicer variable name
$done1 = "UPDATE errors SET Viewed = '1' WHERE Ref = $done";

 

I don't know if this is to be had in an admin area/etc, but you should never trust input regardless. Bad thing happen. :)

Link to comment
Share on other sites

To prevent anyone inserting dicey code into your MySQL statements, you should probably validate your variable in some way - e.g. check its type with the filter extension and if appropriate, is it within a specific range. Use the mysqli_real_escape_string() function which escapes any special characters in your query. Or you can use a prepared statement which sends the query and specific values separately to MySQL

$view = 1;
$done = filter_var($_GET['Ref2'], FILTER_VALIDATE_INT, array('min_range' => 1)); //ensure variable is an integer > 0
$done1 = "UPDATE errors SET Viewed = ? WHERE Ref = ?"; // insert placeholders to your query
$stmt = mysqli_prepare($dbc, $done1); // send the statement to MySQL and assign result to a variable
mysqli_stmt_bind_param($stmt, 'ii', $view, $done); // bind variables to the placeholders, the middle parameter tells sql what type of variables to expect, in this instance two integers
mysqli_stmt_execute($done1); // execute the sql statement
check the result
mysqli_stmt_close($stmt);// clears $stmt variable

  • Upvote 2
Link to comment
Share on other sites

Hi Antonio

Yes - it clicked as soon as I left the internet café - I should probably have used sessions instead. However, in my defence, this suite of programs would normally be saved in an inaccessible directory, accessible only form a main passworded program, and only used by the programmer. A very good point, though - I will be VERY wary of using GET in future.

Link to comment
Share on other sites

O.K. - This is version 2 of the config.sys file as suggested by Antonio Conte. Seems to function O.K.

 

--------------------------------------------------------------------

 

<?php

/* -----------------------------config.php---------------------------------------

Written by: Max Kite

This is purely an exercise but feel free to use it if you wish.

This program catches errors and saves them either to text or MySql. It is a version of my_error_handler from page 494 of Larry's book but with the function sending error messages to SQL as opposed to e-mail.

SQL:...The program needs to see if the error has happened before because we don't want the same error registered a million times on the database. If the error is new, it will be saved on SQL for a programmer to retrieve at will.

This program will run in background and will not be seen by users, hence no HTML used.

 

This is the second version using mysqli_num_rows as suggested by Antonio Conte.

 

*/

require ('log6.php'); //Get database connection

date_default_timezone_set ('Europe/Madrid'); //Set local date (Spain)

function my_error_handler ($e_number, $e_message, $e_file, $e_line, $e_vars) //See page 494 of book for explanation.

{

require ('log6.php'); // log6.php is the dbc connection script and needs to be inside the function to connect to My Sql.

$file = $e_file; // a bit tidier var.

$file = str_replace('\\', '/', $file); //MySql can't save backslashes in utf-8 so need to change all to forward slash.

$line = $e_line; //a bit tidier;

$message = "<p>Error in the script " . $file . " on line " . $e_line; //Build the error message - see book

$message .= "<br /><br />" . $e_message;

$message2 = print_r ($e_vars, 1); //Keep the variable listing separate from message (unlike the book)

$errornumber = $e_number;

$errorexists = 0; //This is the switch which assigns a value depending on whether the error has been previously saved. 0 = not saved 1=saved

//We only need to know if ONE record on the DB is the same as the current error to know that we don't want to save it.......

$checkerrors1 = "SELECT * FROM errors WHERE File = '$file' AND Line = '$line' AND ErrorLevel = '$errornumber'"; // Build MySQL query - retieve all files from errors table that match three criteria on the error message.

$checkerrors2 = mysqli_query($dbc, $checkerrors1); //Make query

 

$r = mysqli_num_rows($checkerrors2); // Find out how many rows were returned

if($r == 0) //If no rows were returned (i.e. the error isn't recorded anywhere on the db) then save the error

{

//Write the query to append the error details to the db.......

$saveerror1 = "INSERT INTO errors (ErrorLevel, Error, File, Line, Variables) VALUES ('$errornumber', '$message', '$file', '$line', '$message2')";

$saveerror2 = @mysqli_query ($dbc, $saveerror1); //run query

}

} //Close function my_error_handler

set_error_handler ('my_error_handler'); //tell php to use my_error_handler

?>

 

-------------------------------------------------------------------------

Link to comment
Share on other sites

Have managed to pick up some new php thanks to Margaux. I emphasise that this is just an exercise. Sorry Larry - couldn't find anything regarding fopen

in PHP 6 AND MYSQL 5 but I reckon 620 pages is enough for one textbook!!!

As always, all comments appreciated.

 

<?php

/*

This is the other way discussed in this forum to save error messages without sending them to

an e-mail address. It is just a fun exercise but I hope it may be useful to someone. The premise is that the programmer can

go into the website through, e.g. FileZilla and look at all errors by reading the text files.

Each error has its own file which is named with the reverse date/time of the error.

There are some refinements that could be made to this script for use in the real world - one is that it might be better to put all of the errors from a certain day into the same .txt file

and the other is that if we are going to keep one .txt file for each error, then we should read the previous files and not save the error if it has already been recorded previously. This script

doesn't do that.

 

---------------configforerrors.php-----------------------

*/

date_default_timezone_set ('Europe/Madrid'); //Set local date (Spain)

function my_error_handler ($e_number, $e_message, $e_file, $e_line, $e_vars) //See page 494 of book for explanation.

{

$file = $e_file; // a bit tidier var.

$line = $e_line; //a bit tidier;

$message = "<p>Error " . $e_number . " in the script " . $file . " on line " . $e_line; //Build the error message - see book

$message .= "\n\r" . $e_message; //Stick in a carriage return then the actual message

$message2 = print_r ($e_vars, 1); //Variable listing to be appended later (don't know why)

$errordate = date('ymdHis'); //Build the reverse date so that files will be listed in date order

$newfilename = "errorlog" . $errordate . ".txt"; //Build the filename which will read e.g. "errorlog120708114420.txt"

/*

We don't want to save the same file name twice so need to know if it exists and then append a number to it

e.g. errorlog120708114420-4.txt

*/

$counter = 0; //Set counter to zero

 

if(file_exists($newfilename)) //If the file we were about to write is already in the directory

{

while(file_exists($newfilename)) //Set a loop to see if file exists, and if so to increment the extension Nº

{

$counter = $counter + 1;

$newfilename = "errorlog" . $errordate . "-" . $counter . ".txt"; //Build the filename plus extension - if exists then loop through while again

}

}

$file = fopen ($newfilename, "a+"); //This opens the file with the new filename

$text = $message . "\n\r" . $message2; //Build the error message by adding the variable listing

fwrite($file, $text); //Write to the new file

fclose($file); //Close the new file

} //End of function

set_error_handler ('my_error_handler'); //tell php to use my_error_handler

$test = $test1; //Force an error to test the script

?>

Link to comment
Share on other sites

One last question to do this topic to death - and I know it should never happen to a good programmer - but am I right in saying that there is no way of stopping parse error messages (that actually stop the script) appearing on the client's screen?

Link to comment
Share on other sites

No, there is a way: disable display_errors. Of course the result will be a blank screen.

 

The most professional solution is to only develop and test scripts on a development server, and only upload them once they're working. If you never edit a live script, you'll never create a parse error.

Link to comment
Share on other sites

 Share

×
×
  • Create New...