q2a / question2answer

Question2Answer is a free and open source platform for Q&A sites, running on PHP/MySQL.
http://www.question2answer.org/
GNU General Public License v3.0
1.64k stars 629 forks source link

Headers already sent by (output started at qa-include/ajax/click-answer.php:66) in /qa-include/app/users.php on line 1054 #443

Closed q2apro closed 7 years ago

q2apro commented 8 years ago

Getting this error daily:

Cannot modify header information - headers already sent by (output started at /qa-include/ajax/click-answer.php:66) in /qa-include/app/users.php on line 1054

Where

 setcookie(...)  // line 1054 of users.php

and

echo "QA_AJAX_RESPONSE\n1\n";  // line 66 of click-answer.php

No solution yet, because I do not see the error source yet.

svivian commented 8 years ago

Sounds like it's related to #440

q2apro commented 8 years ago

Thought so too, however, click-answer.php does the $userid = qa_get_logged_in_userid(); already. https://github.com/q2a/question2answer/blob/dev/qa-include/ajax/click-answer.php#L36

The issue here is the setcookie, not the session, I guess.

q2apro commented 8 years ago

When reading the code, I can see that set_cookie() is within function qa_set_form_security_key() which gets requested by the ask page.

setcookie() reads/needs the $_COOKIE['qa_key'].

I assume that $_COOKIE['qa_key'] might not be set or a wrong string leading to the error? Is the ajax call to click-answer.php creating another cookie?

Update:

qa_set_form_security_key() is also called on each page load with page content, see qa-include/qa-page.php.

q2apro commented 8 years ago

For clarification: The click-answer.php gets triggered by ajax, using inline javascript return qa_answer_click(...) to select the best answer.

In qa-page.php I found a call to qa_set_form_security_key() in function qa_get_request_content.

I see that our click-answer.php calls qa_page_q_single_click_a() on line 57. This triggers qa_page_q_click_check_form_code() which then triggers qa_check_form_security_code. This reads @$_COOKIE['qa_key'].

Ah, I need to check this again after some sleep. Still not sure if I am on the correct path...

q2apro commented 8 years ago

Having thought about this issue again, it is still bugging me resp. my server:

  1. The answer click is AJAX handled.
  2. The server catches the AJAX request by ajax/click-answer.php and sets the echo "QA_AJAX_RESPONSE\n1\n"; to update the page content.
  3. At line 95 of click-answer.php the themeclass is called: $themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null); which can be found in app/format.php
  4. I now cannot find the spot where/if loading the theme class by AJAX does trigger the qa_set_form_security_key(); of qa-page.php again.

If this is the case, then qa_set_form_security_key(); would be called at the AJAX handling and tries to set a cookie. However, the cookie should already be there frontend. And this could lead to the php warning?!

svivian commented 7 years ago

I am not able to reproduce this. I don't get the warning when I trigger those AJAX calls (click-answer.php or asktitle.php).

Is there perhaps a plugin interfering with this? Can you try disabling each plugin and see if that solves the issue? Or it could be in the theme if you add some processing code to the theme.

svivian commented 7 years ago

I've spent a long while debugging this and finally found the situation where this occurs. In both click-answer.php and asktitle.php the page starts outputting content (e.g. line 68 in click-answer.php) but then later calls functions that modify headers.

But for me the warnings never occurred, as PHP buffers output by default. Adding a call to flush(); after the first output line reliably reproduces the warning. Your PR for asktitle.php fixed that issue, but click-answer.php is a bit more complex... took me ages to figure out how to reproduce it, turns out it only happens when an anonymous user triggers an "answer click" such as hiding their own answer.

Anyway... I think I have a solution that should solve the issue for all the AJAX calls, which I'll implement in the next few days.

q2apro commented 7 years ago

Fantastic! Great job @svivian !

svivian commented 7 years ago

OK should be solved now. @q2apro if you want a quick fix for your site, a simple way would be to add ob_start() near the bottom of qa-ajax.php just above the require line.

q2apro commented 7 years ago

I have implemented your fix in my production forum. Let's see if there are any errors left.

And again, great that you found the issue!

q2apro commented 7 years ago

Seems to be done. Thanks a thousand! And Merry Christmas!