I am using PHP and simpletest for unit testing. My tests work fine until I try and set the cookie
try{
setcookie($name,$cookie,$cookie_expires );
}catch Exception($e){
blah
}
The exception is thrown because simpletest has already written out header information so I get the following:
Unexpected PHP error [Cannot modify header information - headers already sent by (output started at /tests/simpletest/reporter.php:43)] severity [E_WARNING] in [blah_code.php line 280]
I've seen vague explanations on catching this with $this->expectException(new Exception()); but no further documentation or examples that work. Could someone provide a working example or point me to documentation? To be clear. This is NOT my code producing the output but rather SimpleTest.
One way to get around this is by using output buffering.
You can turn it on globally in PHP's configuration (and possibly in .htaccess), or you can use ob_start() and its related functions (ob_get_clean(), ob_end_flush(), etc). For example:
ob_start();
// your SimpleTest here.
// your header/ cookie manipulation here.
And then:
ob_end_clean(); // Stop buffering and dump everything (don't echo).
ob_end_flush(); // Stop buffering and echo out the buffer.
ob_get_clean(); // Stop buffering and return everything as a string.
Or any of the other related functions. I believe PHP calls ob_flush() at the end of a file if you don't.
You get this error when you have output before (header functions) setcookie($name,$cookie,$cookie_expires );.
Make sure you don't have any echos or html or text or anything(NOT EVEN A SPACE) before <?php of setcookie($name,$cookie,$cookie_expires );.
Related
I hired someone to write an API for me in PHP and MySQL and now have to maintain it myself. I don't know php as well as other languages.
I noticed at the start of most of the php files they have:
ob_start();
I understand that this opens a new output buffer. The thing is that they never flush the buffer. The code had been working fine but I've had a lot of optimization issues, slow server responses, etc.
How is it that they don't have to flush the buffer but the response is still returning?
An example would be:
ob_start();
include "nusoap.php";
include "config.php";
require_once "class.Database.php";
$client = new nusoap_client($config['apiURL'].'/server.php',false, false, false, false, false, 600, 600);
... process the $_GET and build a $result ...
print_r($result);
Obviously the ... process ... is a wide open thing. But I'm not seeing anywhere in there that does any sort of flush or reading the ob contents. I've also searched all the includes and don't see one in there either.
I checked and implicit_flush is set to Off on this server. Since we did move this code from another server possibly on that server it was on. But still currently this API is working on this server.
The reason I said "not that I can find" when someone asked if there was an ob_get_contents() is because there are include files (including nusoap.php) that include other files and while I've grepped through them and tried to trace them, I might have missed something and am still searching. But so far it appears to my eye that no flush or get_contents is happening.
One possible answer is to say "NO, there has to either be an implicit_flush set in the php.ini file, or an implicit_flush() command somewhere, or another flush command somewhere, or getting the contents of the buffer somewhere - or the contents would never output." To me that is what the manual suggests. But sometimes there are loopholes and PHP seems to be a language of loopholes.
SOLVED
Indeed I did a simple test:
<?php
ob_start();
echo "Whats Up Doc!";
?>
and the output is seen in the browser.
PHP ob_start() function works by catching all output to buffer and then implicitly output this buffer on script end.
You may execute ob_end_clean() to discard (clean) buffer.
In Your example 'print_r($result);' will send output to buffer and then PHP interpreter will send buffer to client (http server/console).
PS. Function ob_implicit_flush() has different meaning. It just flush buffer on every output call (like print or echo), and do not have effect on script finish.
Does any body has any idea how to find where the leak is that provokes this "Content Encoding Error" with $config['compress_output'] = true in CodeIgniter?
I've trying to debug this error for days, but I can't seem to find where the leak is. LogLevel is at debug but I see no info in the log when this error happens.
So, any ideas how to debug this?
UPDATE
I really don't want to disable the compress_output feature, I just want to see how can I trace where the error is produced
UPDATE2
I've looked over and over again to see if there is any output in the controllers... and there is none, so some where else must be the error provoked. No models/database, just controllers, libraries, helpers and views
This issue is where the output buffering starts. The check for the config variable is in system/core/Output.php in _display(). It starts the gzipped buffering after a lot of code has already run. This leaves the potential for output to have occurred before it buffering starts.
With compress_output set to false it doesn't matter because nothing is encoded. With it set to true you end up with mixed content. Some output is encoded and some is not which causes the compression error.
There are two solutions:
1) You could leave compress_output set to false and add ob_start('ob_gzhandler'); to the top of your index.php file. This will ensure that all output is always gzipped, including errors.
2) The other solution is to add ob_flush(); before ob_start('ob_gzhandler'); in system/Output.php. This will gzip output when there are no errors and serve you unencoded content when there are errors.
I think 2 is the better solution and should be implemented by the CodeIgniter team. But if you don't want to muck with the system code (changes will go away when you upgrade) then 1 is the better solution for you.
This might be a long shot, but if you echo/print database output directly from your controller instead of sending it to the model you'll likely get error messages that have to do with output buffering. Are you echoing from your controller?
Putting the following line in config.php:
$config['compress_output'] = FALSE;
Solves it. The problem in my case was that I sent the post, but it did not recognize the FILLCATEGORIAS function. Inly by changing the $ config [ 'compress_output'] = FALSE; solved it.
THIS problem occurred when we send data using a POST request:
Failed to load resource: net::ERR_CONTENT_DECODING_FAILED
<script type="text/javascript">
$(document).ready(function() {
$("#idEmpresa").change(function() {
$("#idEmpresa option:selected").each(function() {
id = $('#idEmpresa').val();
$.post("<?php echo base_url();?>Admin/fillCategorias", {
idEmpresa : id
}, function(data) {
$("#idCategoria").html(data);
});
});
});
});
</script>
Any error in PHP will break compression.
To test this, in index.php change:
error_reporting(E_ALL);
to
error_reporting(E_ALL ^ E_NOTICE);
Do not echo/print to display output from controller.
Do not use "?>" at the end of controller file.
I hope it helps.
Update:
To check if the output is empty before starting buffering, you can open core/Output.php and add
ob_flush();
before
ob_start('ob_gzhandler');
If there is even a space or an empty line, compression will fail. (check page source from browser). This happens because with $config[‘compress_output’] = true , ob_start('ob_gzhandler') (line 379 in Output.php) is executed, wich will cause a "Cannot modify header information - Headers already sent ..." warning. This warning is the cause of compression failure.
So basically, any echo outside of Output class (json output included) will send headers to client, which will cause "Cannot modify header information - Headers already sent ..." warning, which will cause the "content encoding error".
I had the same problem. After searching, I found that my controller has ?> at end of file. So I removed it and it works perfectly. Here is a link for more detail.
More compliant solution:
$this->output->set_output($data);
Some CPanel lightspeed extension updates create this problem. You can try adding this code in your .htaccess file:
php_flag zlib.output_compression ON
Final update
Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D
I'm trying to achieve fire-and-forget like functionality in PHP.
From php.net
<?php
ignore_user_abort(true);
header("Content-Length: 4");
header("Connection: Close");
echo "abcd";
flush();
sleep(5);
echo "Text user should not see"; // because it should have terminated
?>
This works if I open the script with a browser. (shows "abcd").
But if I open it with file_get_contents or some stream library it will wait for ~5 seconds and show the second text as well.
I'm using PHP 5.2.11 / Apache 2.0
Update
I seems there is some confusion about what I'm trying to accomplish.
I don't want to hide output using output buffers (that's stupid). I want to have the client terminate before the server starts a possibly lengthy process (sleep(5)) and I don't want the client to wait for it (this is what fire-and-forget means, sort off).
The use of output buffers is merely a side effect. I've amended the sample code without the use of output buffers.
What I don't understand is: why does this script behave differently when accessing it from the browser vs. fetching it in PHP with file_get_contents("http://dev/test.php") or some stream library? What I've seen in testing is that for instance stream_get_contents will actually block for 5 seconds before it returns any output at all, the is quite the opposite of what I want.
Update2
Some more results:
The browser somehow responds to the flush(). I can't figure out how to replicate this behavior with streams in PHP, my streams keep blocking.
I've tried fread and found that it behaves similar to stream_get_contents.
Specifying a maxlength has no effect, it will still block for ~5 seconds.
Changing the blocking mode has no effect (other than generating a bunch more calls to stream_get_contents()). It will wait ~5 seconds before returning anything.
stream_set_read_buffer has no effect (tested on a PHP 5.3.5 sever)
The second portion of text is showing up because you're stopping output buffering with ob_end_flush() and ob_end_clean(). When that happens PHP outputs content as normal. Try something like the following:
<?php
ob_start(); // turn on output buffering
print "Text the user will see.";
ob_flush(); // send above output to the user and keep output buffering on
print "Text the user will never see";
ob_end_clean(); // empty the buffer and turn off output buffering. your script should end here.
?>
It's important for ob_end_clean() to appear at the end of the script. It empties the buffer and does not send its contents to the user, thus keeping everything after ob_flush() hidden.
How do you access the script using file_get_contents? How do you access it with your browser? If you access the script without "http://", of course it will never get executed. Use the same URL as in the browser.
Edit:
The browser will render the page even before the connection is closed. Even if you flush, I don't think the connection is closed. You can fire up Wireshark and check. stream_get_contents and file_get_contents will block until they have all the output. Even if you flushed, they can't be sure that there isn't more content. Since the content-length header didn't seem to make {file,stream}_get_contents return earlier, you probably need to implement your own buffering, ala. fopen, read, fclose.
Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D
I get unexplained "Headers already sent on line #..." error on those 2 lines that execute "echo ..." in the code below.
Simplified version of the case:
<?php
ob_start();
//Initializing FirePHP...
include_once(F_FS_PATH."lib/FirePHPCore/fb.php");
// <--- I've also tried to move the ob_start(), after the FirePHP init,
// <--- instead before it. But it made no difference.
?>
<html>
<div>A lots of HTML (and php) code goes here... Actually my entire page.
FirePHP is also used here many times by multiple invocations
of the function fb('debug text');</div>
</html>
<?php
$all_page_content=ob_get_clean();
if ($GLOBALS["marketing_enabled"])
echo marketingReplaceContent($all_page_content);
else
echo $all_page_content;
ob_flush(); flush();
//Do some other non-printing - but slow stuff.
do_the_silent_slow_stuff_Now();
// <--- presumably the php execution ends here.
?>
I cannot understand why FirePHP is trying to do something upon page completion after I print the buffer and flushed it? Or what is it trying? How can I cope with this problem? :(
Here's your problem:
Headers already sent on line #...
Thats exaclty what happens when you use FirePHP and echo something beforehand. This might even be a whitespace before the <?php tag. FirePHP sends all its content as a header, and headers can't be send after any output is made.
Since I'm sure that you call FirePHP in your do_the_silent_slow_stuff_Now(); method I recommend not to use buffering, flushing and FirePHP at once.
Either you resign on ob_start() and ob_flush() during the development phase or you call the ob_flush() method after all stuff is done.
Third possibility would be to seperate your development and live phase by doing something like $development = true; and make your own FirePHP function:
function my_fb($text) {
if(!$development)
fb($text);
}
and:
if($development) {
do_the_silent_slow_stuff_Now();
ob_flush(); flush();
}
else {
ob_flush(); flush();
do_the_silent_slow_stuff_Now();
}
Hope this helps!
When I call PHP's ob_flush() function on my localhost (via MAMP) I get the following error:
Notice: ob_flush() [ref.outcontrol]:
failed to flush buffer. No buffer to
flush.
The only solution I can find is to prefix it with #, but this doesn't seem like a real solution to me. What's causing the problem?
The error message would seem to imply that you're not actually using output buffering when you call ob_flush(). Have you called ob_start() previous to invoking ob_flush()?
If you want to use ob_flush you must first call ob_start() - desirably at the top of your page.
Not sure if it fits your needs but you might want to also try the plain old flush(), just guessing.