I've written a script to geocode some points which has a structure basically like this:
//get an unupdated record
$arr_record;
while(count($arr_record) > 0)
{
//strings are derived from $arr_record
geocode($string1);
geocode($string2);
geocode($string3);
array_pop($arr_record);
}
function geocode($string) {
//if successful
update($coords)
}
function update($coords) {
//update the database
header('Location:http://localhost/thisfile.php')
}
The trouble is that even when the geocode is successful and the database is updated, and teh header resent, the script still goes back into the while loop without reloading the page and starting again on a new record.
Is this normal behaviour for PHP? How do I avoid it behaving like this?
After header() use die(); to terminate the script and output.
How do I avoid it behaving like this?
Put exit() after header().
another effective way is not to send headers directly in a loop. which is not proper (i couldn't find in php.net manual but i remember it was discussed before in phpusenet).
it may act unexpected in different php versions. & different apache ver. installations.
php as cgi will make problems too.
you can assign it to return as string then you can send header later...
function update($coords) {
//update the database
if(statement to understand update is ok){
return 'Location:http://localhost/thisfile.php';
} else {
return false;
}
}
if($updateresult=update($cords)!=false){ header($updateresult); }
but if i were you... i would try to work ob_start() ob_get_contents() ob_end()
because those are the excellent way to control what will be sent to browser. normal mimetypes or headers... whatever. it's better way while working with headers & html output at the same time.
ob_start(); /* output will be captured now */
echo time(); /* echo test */
?>
print something more...
<?php /* tag test */
/* do some stuff here that makes output. */
$content=ob_get_contents();
ob_end_clean();
/* now everything as output with echo, print or phptags.
are now stored into $content variable
then you can echo it to browser later
*/
echo "This text will be printed before the previous code";
echo $content;
Related
There's a lot of code in each file, too much to post, so I'm giving you a general idea of what's happening in each file.
index.php
[html dropdown menu code etc.]
scripts.js
[AJAX detects user selection from dropdown, grabs fetch.php which pulls database to generate html code for secondary dropdown selections to put in index.php]
fetch.php
[Generates secondary dropdown code based on user selection and query of the database]
I need to see what exactly is being queried to debug, so I'd like to echo the sql select statement:
$query = "SELECT * FROM databasename WHERE.."
That is in fetch.php when user makes a selection from index.php - How do I do this?
When I deal with AJAX, that I return as JSON, one trick I use is to take advantage of output buffering. You can't just echo or output anything you want because it will mess up the JSON data so for an example,
ob_start(); //turn on buffering at beginning of script.
.... other code ...
print_r($somevar);
.... other code ...
$debug = ob_get_clean(); //put output in a var
$data['debug'] = $debug;
header('Content-Type: application/json');
echo json_encode($data); //echo JSON data.
What this does, is wrap any output from you script into your JSON data so that it's format is not messed up.
Then on the javascript side you can use console.log
$.post(url, input, function(data){
if(data.debug) console.log(data.debug);
});
If you are not used to debugging with console.log(), you can usually hit F12 and open the debugger in most browsers. Then in there the output will be sent to the "console". IE9 had a bit of an issue with console.log() if I recall, but I don't want to go to far off track.
NOTE: Just make sure to not leave this stuff in the code when you move it to production, its very simple to just comment this line out,
//$data['debug'] = $debug;
And then your debug information wont be exposed in production. There are other ways to automatically do this, but it depends on if you do development local then publish to the server. For example you can switch it on the $_SERVER['SERVER_ADDR']; which will be ::1 or 127.0.0.1 when it's local. This has a few drawbacks, mainly the server address is not available from the Command Line Interface (CLI). So typically I will tie it into a global constant that says what "mode" the site is in (included in the common entry point, typically index.php).
if(!defined('ENV_DEVELOPMENT')) define('ENV_DEVELOPMENT','DEVELOPMENT');
if(!defined('ENV_PRODUCTION')) define('ENV_PRODUCTION','PRODUCTION');
if(!defined('ENVIRONMENT')) define('ENVIRONMENT',ENV_DEVELOPMENT);
//site is in Development mode, uncomment for production
//if(!defined('ENVIRONMENT')) define('ENVIRONMENT',ENV_DEVELOPMENT);
Then it is a simple matter to check it:
if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;
If you know how to use error reporting you can even tie into that using
if(ini_get('display_errors') == 1) $data['debug'] = $debug;
Which will only show the debug when display errors is on.
Hope that helps.
UPDATE
Because I mentioned it in the comments, here is an example of it wrapped in a class (this is a simplified version, so I didn't test this)
class LibAjax{
public static function respond($callback, $options=0, $depth=32){
$result = ['userdata' => [
'debug' => false,
'error' => false
]];
ob_start();
try{
if(!is_callable($callback)){
//I have better exception in mine, this is just more portable
throw new Exception('Callback is not callable');
}
$callback($result);
}catch(\Exception $e){
//example 'Exception[code:401]'
$result['userdata']['error'] = get_class($e).'[code:'.$e->getCode().']';
//if(ENVIRONMENT == ENV_DEVELOPMENT){
//prevents leaking data in production
$result['userdata']['error'] .= ' '.$e->getMessage();
$result['userdata']['error'] .= PHP_EOL.$e->getTraceAsString();
//}
}
$debug = '';
for($i=0; $i < ob_get_level(); $i++){
//clear any nested output buffers
$debug .= ob_get_clean();
}
//if(ENVIRONMENT == ENV_DEVELPMENT){
//prevents leaking data in production
$result['userdata']['debug'] = $debug;
//}
header('Content-Type: application/json');
echo self::jsonEncode($result, $options, $depth);
}
public static function jsonEncode($result, $options=0, $depth=32){
$json = json_encode($result, $options, $depth);
if(JSON_ERROR_NONE !== json_last_error()){
//debug is not passed in this case, because you cannot be sure that, that was not what caused the error. Such as non-valid UTF-8 in the debug string, depth limit, etc...
$json = json_encode(['userdata' => [
'debug' => false,
'error' => json_last_error_msg()
]],$options);
}
return $json;
}
}
Then when you make a AJAX response you just wrap it like this (note $result is pass by reference, this way we don't have to do return, and in the case of an exception we update $result in "real time" instead of on completion)
LibAjax::respond( function(&$result){
$result['data'] = 'foo';
});
If you need to pass additional data into the closure don't forget you can use the use statement, like this.
$otherdata = 'bar';
LibAjax::respond( function(&$result) use($otherdata){
$result['data'][] = 'foo';
$result['data'][] = $otherdata;
});
Sandbox
This handles catching any output and puts it in debug, if the environment is correct (commented out). Please pleas make sure to implement some kind of protection so that the output is not sent to clients on production, I cant stress that enough. It also catches any exceptions puts it in error. And it also handles the header and encoding.
One big benefit to this is consistent structure to your JSON, you will know (on the client side) that if if(data.userdata.error) then you have an exception on the back end. It gives you one place to tweak your headers, JSON encoding etc...
One note in PHP7 you'll have to or should add the Throwable interface (instead of Exception). If you want to catch Error and Exception classes Or do two catch blocks.
Let's just say I do a lot of AJAX and got sick of re-writing this all the time, my actual class is more extensive then this, but that's the gist of it.
Cheers.
UPDATE1
One thing I had to do for things to display was to parse the data variable before I console.log() it
This is typically because you are not passing the correct header back to the browser. If you send (just before calling json_encode)
header('Content-Type: application/json');
This just lets the browser know what type of data it is getting back. One thing most people forget is that on the web all responses are done in text. Even images or file download and web pages. It's all just text, what makes that text into something special is the Content-Type that the browser thinks it is.
One thing to note about header is you cannot output anything before sending the headers. However this plays well with the code I posted because that code will capture all the output and send it after the header is sent.
I updated the original code to have the header, I had it in the more complex class one I posted later. But if you add that in it should get rid of the need to manually parse the JSON.
One last thing I should mention I do is check if I got JSON back or text, you could still get text in the event that some error occurs before the output buffering is started.
There are 2 ways to do this.
If Data is a string that needs to be parsed
$.post(url, {}, function(data){
if( typeof data == 'string'){
try{
data = $.parseJSON(data);
}catch(err){
data = {userdata : {error : data}};
}
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Or if you have the header and its always JSON, then its a bit simpler
$.post(url, {}, function(data){
if( typeof data == 'string'){
data = {userdata : {error : data}};
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Hope that helps!
UPDATE2
Because this topic comes up a lot, I put a modified version of the above code on my GitHub you can find it here.
https://github.com/ArtisticPhoenix/MISC/blob/master/AjaxWrapper/AjaxWrapper.php
Echo the contents and do a die() or exit; afterwards... then in the Network tab of your browser, start it recording, run the Ajax request (it'll fail) but check the resource/name and then view the Response, and it'll show you what was echo'd in the script
Taken from: Request Monitoring in Chrome
Chrome currently has a solution built in.
Use CTRL+SHIFT+I (or navigate to Current Page Control > Developer > Developer Tools.
In the newer versions of Chrome, click the Wrench icon > Tools > Developer Tools.) to enable the Developer Tools.
From within the developer tools click on the Network button. If it isn't already, enable it for the session or always.
Click the "XHR" sub-button.
Initiate an AJAX call.
You will see items begin to show up in the left column under "Resources".
Click the resource and there are 2 tabs showing the headers and return content.
Other browsers also have a Network tab, but you will need to use what I commented to get the string value of the query.
ArtisticPhoenix solution above is delightful.
I am fairly new to PHP, even more so to the ob_ functions, so help me understand this, as the manual is somehow does not provide a very simple example or reference.
I am assuming that "output buffering" is what delays and holds php from sending headers until full content is sent, and that may be why the header() function does not issue an error if ob_start() is declared above. If so, my question is how do I "buffer" only some contents instead of just mentioning ob_start() at the top of my script, which is greatly slowing down my application?
Example.
<?php
namespace App\Controller;
class Home extends Controller{
public function showHomePage()
{
$students = $pdo->query('SELECT id FROM students');
$view->showContent($students); // includes content.php
}
}
//content.php
<p> showing stundent by id </p>
<?php
showContent()
{
if(!$students){
header('Location: /404');
}else{
//show students
}
}
}
Now you can see in the above example that, as soon as content.php is loaded, it will issue header already sent sent error (if $students evaluates to false/null ) so, to hide this error, I placed ob_start() inside my howHomePage method as seen here
public function showHomePage()
{
$students = $pdo->query('SELECT id FROM students');
ob_start();
$view->showContent($students); // includes content.php
}
Now, with the above approach, I get no header errors, but I would like to close that buffer as soon as the showContent() method is executed. In other means, I don't want the ob_start() to apply only for that following function. I tried to do something like this
public function showHomePage()
{
$students = $pdo->query('SELECT id FROM students');
ob_start();
$view->showContent($students); // includes content.php
on_end_flush();
}
but now, the contents showContent() are not being shown
<p> showing stundent by id </p>
<?php
showContent()
{
if(!$students){
header('Location: /404');
}else{
//show students
}
}
}
This is a terrible way to code. You've already got your output baked in, which, as you've noted, prevents you from changing the header(). This is a major driver behind MVC, which holds that you need to segment your code and separate your view(HTML) from your controller(PHP). In this case, you've put a function inline with your HTML.
There's a couple of ways to work around this without having to resort to output buffering
Do the check on $students earlier in the page (like when you get/build the data set) and issue the 404 there.
Move your HTML into a separate template file (maybe check out Smarty to help with that) and then do your drawing there.
I had the same issue and solved it by adding:
ob_implicit_flush(true);
to the beginning of the php file. This outputs everything right away and you can take your other flush commands out.
http://php.net/manual/en/function.ob-implicit-flush.php
ob_implicit_flush() will turn implicit flushing on or off. Implicit
flushing will result in a flush operation after every output call, so
that explicit calls to flush() will no longer be needed.
I have a simple php page, that is doing some operations for an mysql result set:
$i = $d = $n = 0;
$sql="SELECT record, field1,field2,field3 from table where condition='xxxx'" ;
result=$db->send_sql($sql);
while($row=mysql_fetch_array($result, MYSQL_ASSOC))
{
//about 500 results
//now performing some actions on these results via function call
$i++;
if($row['field1'] == 'xxx')
{
$d++;
echo $row['record'].": ";
echo do_something(field1,field2); //function will echo some text
echo "<br>";
}
else
{
$n++;
echo $row['record'].": nothing to do <br>";
}
}
echo "<hr>Total: ".$i." - Updates: ".$d." - Nothing done: ".$n;
All works, as it should but the Output/echos are only shown, once all operations have been done and the while statement has been worked through.
It might be a silly question, but is there a way to show the "echos/output" "on the fly" - so the user get's the "something is happening" experience?
echo sends the output toward the user immediately, but that output might be held up by a couple of layers in between. If you want the feel you describe, use AJAX. Otherwise, you can try some of these approaches:
Turn output buffering off
flush() immediately after sending your HTML head element
then flush() periodically, with some str_repeat() to kickstart/prevent timeouts
See also this and this conversation on performance/best practices and this conversation on output buffering.
PHP is run on the server and displays output to the client when it is finished. I think you will need Javascript to do this, but I could be wrong.
You can try to use flush() function to send data to browser after each loop execution.
I have a function, 'redirect_to()' written on php script that is called after a successful update to a page on my custom CMS. It works fine on the localhost, but when I try it on my actual live domain I get the following error message:
Warning: Cannot modify header information - headers already sent by (output started at /hermes/bosweb/web119/b1192/ipg.typaldosnetcom/edit_listing.php:7) in /hermes/bosweb/web119/b1192/ipg.typaldosnetcom/includes/functions.php on line 20
Here is the code for the redirect_to() function:
function redirect_to ($location = NULL) {
if ($location != NULL) {
header("Location: {$location}");
exit;
}
}
I've made sure to call the function before I output any HTML, so I'm not sure what the problem really is.
My question: Why am I receiving this error?
It's not lying. You've output something before getting to this point. Check the locations mentioned in the error messages.
Show us the first 25 lines of each of the files mentioned.
you already sent your output to the page before you set the header. first you need to set the headers and then can the output come.
It can even be a whitespace.
It means something was already outputted on the suggested line. Try going there and see what it does.
Try pasting the surrounding code on that position for a better clarification if you can't find the problem yourself.
One common cause is to have a line after a php file you're including...
Simple solution: remove the closing php tag "?>" from all files as it's not needed..
You can test if you have a character before the opening php-script tag by removing any closing php-script tag. This way you are sure there isn't any character left (it's not needed).
Use output buffering:
<?php
ob_start();
// Test buffered output.
echo 'hello world';
function redirect_to ($location = NULL) {
if ($location != NULL) {
header('Location: ' . $location);
exit;
}
}
// rest of php file here
ob_end_flush();
?>
Docs: ob_start() and ob_end_flush()
Is there anyway I can check when file_get_contents has finished loading the file, so I can load another file, will it automatically finish loading the one file before going onto the next one?
Loading a file with file_get_contents() will block operation of your script until PHP is finished reading it in completely. It must, because you couldn't assign the $content = otherwise.
PHP is single threaded - all functions happen one after the other. There is a php_threading PECL extension if you did want to try loading files asynchronously, but I haven't tried it myself so I can't say if it would work or not.
simple example that will loop through and get google.co.uk#q=* 5 times and output if it got it or not, pretty useless but kinda answers your question that a check can be done to see if file_get_contents was successful before doing the next one, obviously google could be changed to something else. but wouldn't be very practical. plus output buffering dont output within functions.
<?php
function _flush (){
echo(str_repeat("\n\n",256));
if (ob_get_length()){
#ob_flush();
#flush();
#ob_end_flush();
}
#ob_start();
}
function get_file($loc){
return file_get_contents($loc);
}
for($i=0;$i<=5;$i++){
$content[$i] = #get_file("http://www.google.co.uk/#q=".$i);
if($content[$i]===FALSE){
echo'Error getting google ('.$i.')<br>';
return;
}else{
echo'Got google ('.$i.')<br>';
}
ob_flush();
_flush();
}
?>