Max Posted June 26, 2012 Share Posted June 26, 2012 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 More sharing options...
margaux Posted June 26, 2012 Share Posted June 26, 2012 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 More sharing options...
Antonio Conte Posted June 26, 2012 Share Posted June 26, 2012 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 More sharing options...
Max Posted June 27, 2012 Author Share Posted June 27, 2012 Thanks Margaux and Antonio - will make it a little learning project and will post it when done. Link to comment Share on other sites More sharing options...
Max Posted June 30, 2012 Author Share Posted June 30, 2012 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 More sharing options...
Antonio Conte Posted June 30, 2012 Share Posted June 30, 2012 He wrote the needed functions in the last post. Just look them up in the manual. Link to comment Share on other sites More sharing options...
Larry Posted July 1, 2012 Share Posted July 1, 2012 Don't I use the error_log() function within that error handler? If so, its arguments dictate where the error gets sent: in an email, written to a file, etc. Link to comment Share on other sites More sharing options...
Max Posted July 2, 2012 Author Share Posted July 2, 2012 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 More sharing options...
Antonio Conte Posted July 2, 2012 Share Posted July 2, 2012 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 More sharing options...
Max Posted July 3, 2012 Author Share Posted July 3, 2012 Hi Antonio Will have a look at that and re-work it. Sorry Antoinio - could you please expand on your last comment re: SQL injection....Thanks Link to comment Share on other sites More sharing options...
margaux Posted July 3, 2012 Share Posted July 3, 2012 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 2 Link to comment Share on other sites More sharing options...
Max Posted July 4, 2012 Author Share Posted July 4, 2012 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 More sharing options...
Max Posted July 5, 2012 Author Share Posted July 5, 2012 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 More sharing options...
Antonio Conte Posted July 6, 2012 Share Posted July 6, 2012 Just to clarify. Get is not always bad, but you must escape and validate it like you do with post. 1 Link to comment Share on other sites More sharing options...
Max Posted July 9, 2012 Author Share Posted July 9, 2012 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 More sharing options...
Larry Posted July 9, 2012 Share Posted July 9, 2012 Yeah, I don't discuss files and directories in this book. You don't actually have to do all that, though. You can just use the built-in error log system which will manage all this for you. Link to comment Share on other sites More sharing options...
Max Posted July 10, 2012 Author Share Posted July 10, 2012 Thanks, Larry - but it was good fun knowing that I could do it! (I don't get out much ). 1 Link to comment Share on other sites More sharing options...
Max Posted July 16, 2012 Author Share Posted July 16, 2012 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 More sharing options...
Larry Posted July 16, 2012 Share Posted July 16, 2012 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 More sharing options...
Recommended Posts