Question:
PHP gurus: file write and redirect - promised 10 pts easy?
Harvey Jones
2010-05-22 03:09:27 UTC
Hi guys,

I have a chat room, and when the user swears, the swear is replaced with with . That works well. But after 3 swears from the same user, I need PHP to do a filewrite of the client's IP to .htaccess and redirect him to another page, which simply states that he's been banned. What would I add to this code?

Here's the main part of the code (PHP):

$bad_words=file('badwords.txt');
$bad_words=explode(',',$bad_words[0]);
for($i=0;$i
if(count($bad_words)>1){
for($i=0;$i$ajx_line=str_replace($bad_words[$i],'',$ajx_line);}}
$ajx_line=neutral_escape($ajx_line,255,'str');

And I've uploaded the full code in .txt here (Y!A sometimes doesn't display code well): http://www.andrewtaylor.eu/chat/aroom.txt
Four answers:
anonymous
2010-05-22 03:46:35 UTC
The above poster is pretty indepth, but i'm not sure what question hes answering... you seem to want to know how to implement a feature more than the specifics of file writing. Personally i'd advise against dropping the IP into the .htaccess, for starters if anything goes wrong... eek.



I'd suggest just making a table in a mysql. All it needs is 3 fields, IP, BadCount, Date. Every time you censor a word increment the number in BadCount for that IP. If the number is above 3 then set the Date to however long you want the ban and then reset BadCount. Finally when the user logs in check the Date field for that IP to see if its after the current date, if it is then use a header redirect.



And if you do decide to go the file writing route, i'd look into file_put_contents(), its made for basic file operations and much easier to use then implementing the class above.
?
2010-05-22 03:17:35 UTC
Sometimes you may want to send headers, perhaps a redirection, to the user after the script has sent something else to the browser. This will normally cause an error, because the HTTP protocol says that you must send headers first and content after them. Output buffering functions can be used to buffer any data you output, and including other things, will let you send headers after other data has already been sent.







ob_start() and ob_end_flush() might already be familiar to you. In addition to their typical uses, the output buffering functions can be used to do various neat tricks, such as writing all output to a file.







The function ob_start() takes an optional parameter for a callback function. The callback function receives all output from PHP, so this is what we can use for our purprose.













Onwards to the code





Now that we have the basics covered, let's look at the code a bit.







Let's first look at a few functions to see how this will work, and then we will refactor it into a nice class. Let's start!







We need to open a file where the output will be written to





$ob_file = fopen('test.txt','w');





Very basic stuff. Next, let's create the callback function which will perform the actual writing to the file:













function ob_file_callback($buffer)

{

global $ob_file;

fwrite($ob_file,$buffer);

}









Look! There's a global, aren't they evil?



Don't worry about it, when we get to the next chapter, we will get rid of it in the class we will create.







Now we have all that we need together:





$ob_file = fopen('test.txt','w');



ob_start('ob_file_callback');



//Anything we output now will go to test.txt

echo 'Hi everyone!';

echo 'I bet you can\'t see this in your browser';



ob_end_flush();





Provided your script can write to test.txt, the above code will write the echo's to the file and not to the browser. Simple and easy.











This code we just made isn't very easy to re-use though: We used a global variable and we have to manually open the file and things like that. To make our code a little nicer, let's make it into a class!













Classified





Making this into a nice, easy to use class is pretty simple. I don't want to have to manually open a file, so I want the class to be able to open the target file for writing. It also obviously needs to handle the callback and such on its own so after I have written the class, I won't have to bother with the details anymore.







Let's just call the class OB_FileWriter. Note that I'm using PHP5 syntax, so if you're running PHP4, you need to change all variable declarations to var $variablename, change __construct into OB_FileWriter, remove __destruct and remove “public” from all function declarations.









class OB_FileWriter

{

private $_filename;

private $_fp = null;



public function __construct($filename)

{

$this->setFilename($filename);

}



public function __destruct()

{

if($this->_fp)

$this->end()

}



public function setFilename($filename)

{

$this->_filename = $filename;

}



public function getFilename()

{

return $this->_filename;

}



public function start()

{



}



public function end()

{



}



public function outputHandler($buffer)

{



}

}





So there's the basic skeleton for the class. Now let's add the code to the empty functions.









public function start()

{

$this->_fp = @fopen($this->_filename,'w');



if(!$this->_fp)

throw new Exception('Cannot open '.$this->_filename.' for writing!');



ob_start(array($this,'outputHandler'));

}





This function will be used for starting output buffering. It opens the file, checks if it was succesfull and starts buffering. Note that we used the @ symbol in front of the fopen command to suppress PHP error messages and instead just check if the $_fp variable was set and throw an exception if it failed.







ob_start takes the callback as the first parameter, but why is it an array? In PHP if you want to pass class' functions as callbacks, you need to pass an array containing the owner class and the class function name.











Next, the end function..





public function end()

{

@ob_end_flush();

if($this->_fp)

fclose($this->_fp);



$this->_fp = null;

}





First calling the ob_end_flush to end the buffering and flush the data to the file. Then if the file is still open as it should be, close it and set the variable to null to signify that the file is no longer open.











Finally, the outputHandler function





public function outputHandler($buffer)

{

fwrite($this->_fp,$buffer);

}





Simplest of them all! Just dump the contents of the buffer to the file.











Now we have a class for sending output to a file. Compare using this class to the approach in the earlier example:





$obfw = new OB_FileWriter('test.txt');

$obfw->start();



echo 'Hi everyone!';

echo 'I bet you can\'t see this in your browser';



$obfw->end();





Nice and easy!













Final improvements





The class can be used now, but there's one small thing to consider:







If we start the buffering and the code we run after that gives an error or throws an uncaught exception, what happens?







The message from the error will go straight to the file, which isn't necessarily what you want. At least I like to see my errors when they happen so that I can do something about it, so let's make it so that our class can optionally try to detect errors and exceptions and halt the buffering so that we can see them.











We can do this by registering our own error and exception handlers inside our class which will then stop the buffering and display the errors.







The class needs some new functions and a new variable for this, so let's add the variable first…





private $_errorHandlersRegistered = false;









and the functions…





private function _stopBuffering()

{



}



public function setHaltOnError($value)

{



}



public function exceptionHandler($exception)

{



}



public function errorHandler($errno, $errstr, $errfile, $errline)

{



}













The function names should be pretty self explanatory, so let's continue with adding the code in them:





public function setHaltOnError($value)

{

//Do nothing if the value is the same as old

if($value === $this->_errorHandlersRegistered)

return;



if($value === true)

{

set_exception_handler(array($this, 'exceptionHandler'));

set_error_handler(array($this, 'errorHandler'));

$this->_errorHandlersRegistered = true;

}

else

{

restore_exception_handler();

restore_error_handler();

$this->_errorHandlersRegistered = false;

}

}



So depending on the value you pass to this function, it will either register the class' error and exception handlers or restore the old handlers for them.







Now, let's add the code for the handlers:





public function exceptionHandler($exception)

{

$this->_stopBuffering();

echo 'Fatal error: Uncaught ', $exception;

}

Quite simple. Stop buffering and print a message with the exception details.public function errorHandler($errno, $errstr, $errfile, $errline)

{

$this->_stopBuffering();



$errorNumber = E_USER_ERROR;

switch($errno)

{

case E_NOTICE:

$errorNumber = E_USER_NOTICE;

break;

case E_WARNING:

$errorNumber = E_USER_WARNING;

break;

}



trigger_error($errstr.', File: '.$errfile.' line '.$errline, $errorNumber);

}

We can re-trigger errors and they will be handled by the default error handler, so that's what we do here.So what about _stopBuffering()? Let's move the code which stops the buffering from end() to this function and modify end a bit too…public function end()

{

$this->_stopBuffering();

$this->setHaltOnError(false);

}



private function _stopBuffering()

{

@ob_end_flush();



if($this->_fp)

fclose($fp);



$this->_fp = null;

}

We do this because we want to be able to stop the buffering without removing the error handlers. Also, when we end(), we want to return back to using the normal error handlers so we must add that to end()





In closingSo redirecting PHP output to a file is pretty simple to do. Just some things to remember, such as that error/exception thing. You might wonder why not just simply use ob_start(), ob_get_clean() and file_put_contents… With the class I described, everything outputted will be sent to the file when it's being written and if something else fails afterwards, the stuff which was sent earlier is still in the file. Writing the contents of the buffer to the file in the end of the script would mean that nothing is written if there's an error, so which way is better just depends on what your goal is.
anonymous
2016-11-30 12:11:27 UTC
all the unicorn and rainbow poems are making me think of of My Little Pony... in simple terms sayin' the jingle has been caught in my head for 2 rapidly days I even have the uncontrollable urge to braid the long hair of complete strangers whilst galloping down Mane(sic) street oh..and do your guy or woman homework slacker!! *whinnyin*
Elizabeth
2010-05-23 20:15:00 UTC
OMG!!!!!! You are so intelligent libby, gibby tibby, cibby or whatever it is. Gosh darn it, I think I'm suppose to be impressed??


This content was originally posted on Y! Answers, a Q&A website that shut down in 2021.
Loading...