output buffering not showing contents - php

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.

Related

PHP - file_put_contents writes content multiple times

I have an extremely oversimplified logger function:
<?php
class Logger {
public function __construct($logFile) {
$this->logFile = $logFile;
}
public function log($message) {
$message = date('c') . $message;
file_put_contents($this->logFile, $message, FILE_APPEND | LOCK_EX);
echo "it ran ";
}
}
Calling it like this
$logger = new Logger('log.txt');
$logger->log("message");
echo "called the method";
causes the message to be written to the file exactly 3 times, instead of 1.
The code is outside of any loop, which is confirmed by echo statements, which get printed only once.
Also, if I simply run file_put_contents() function on place where I'd call the log method, it works fine and writes the content just once. So it might have something to do with my class, but no clue what.
EDIT: #Tommy: here is the log file content:
2014-09-26T07:24:51-04:00message2014-09-26T07:24:54-04:00message2014-09-26T07:24:54-04:00message
EDIT 2: I tried using die() function after calling the method, and then it did write the message just once. So I kept moving the die() through the code, and it starts writing the message 3 times after this exact line:
if (isset($_POST['create_account'])) {
die;
Since there's a die below it, it shouldn't even matter what's in further code, right?
Wonder if it might be some sort of php bug, this is very stange. If I put the die() above this line, it will work fine and write the message just once.
There's a fairly good chance that your code does a redirect or reload somewhere. This causes a new request to start, which wipes away the original echo but does not remove the one written to file. As a result it looks like it was echo'd once and written thrice. But really, it was echo'd thrice as well, just the other copies have been removed.
If you want to see what's going on, print part of the stack-trace into the log-file along with the message. You can see exactly on which line the message is created and during which function call.
The main issue as per my experience is the index.php is being called twice. So, to fix:
change the file name
fix the index.php such that favicon.ico is missing!

Auto-execute a function after everything else has executed

Basically, I have a script that is included at the top of a page that does a bunch of things, the most important being an ob_start(). Then in the body of the page I have a variety of tags that will be replaced, such as {hello_word}. Then at the very end, I include another script that ends the output buffer, and makes the tag replacements with other code, then prints.
Is there any possible way to do this without having to include my second file at the end? Is there some simple way I can automatically execute a function or include a file at the very end?
You can register a function to be executed at the very end of script using register_shutdown_function
Any objects that you have remaining will be destroyed at the end of the script, and their destructors will be called (manual). You can put code that you want executed at the end in the destructor.
For example:
Class Waitforme {
function __destruct() {
echo "I'm here!";
}
}
$hello = new Waitforme();
This will do nothing until $hello is destroyed, at which time we'll see "I'm here!"
You can use the auto_append setting in php.ini, but you'll sacrifice portability. If you don't plan on distributing your application, this is a good option.

what is the role of ob_start() in here

session_start();
ob_start();
$hasDB = false;
$server = 'localhost';
$user = 'user';
$pass = 'pass';
$db = 'acl_test';
$link = mysql_connect($server,$user,$pass);
if (!is_resource($link)) {
$hasDB = false;
die("Could not connect to the MySQL server at localhost.");
} else {
$hasDB = true;
mysql_select_db($db);
}
a) what does ob_start() exactly do.? i got to understand it will turn output buffering on. with reference to the above code what will be the benefit if i use ob_start() while trying to establish the connection with the database. what output data it will buffer?
thank you..
Normally php sends all text not included in <?php ... ?>, all echos, all prints to the output. Which is send to the err... output: http server (which sends it to a client), console etc.
After ob_start this output is saved in output buffer, so you can later decide what to do with it.
It doesn't affect db connection. It deals with text (mostly) produced by php.
Some PHP programmers put ob_start() on the first line of all of their code*, and I'm pretty certain that's what going on here.
It means if they get halfway through outputting the page and decide there's an error, they can clear the buffer and output an error page instead. It also means you never get "couldn't sent headers, output already started" errors when trying to send HTTP headers.
There are a few legitimate reasons to do this, but I'd take it as a sign that they are mediocre programmers who don't want to structure their code in a coherent order - i.e. they should be working out if there are errors or headers to send before they start rendering the page. Don't copy this habit.
(* What makes this an easy habit to fall into is that if output buffering is still turned on when the script end is reached, the buffer is sent to the user, so they don't need to worry about a matching end statement)
first of all buffering is usefull for putting http-headers (header function) at any line of code. eg - session cookies. without ob_start you would not be able to add any http-header to response if you already sent some data, e.g. with echo or print functions
In this particular code ob_start seems useless. The benefit of output buffering is in either following code or does not exist. `In general, consult PHP manual about ob_start.

PHP header in a loop

Is it possible to "call" a PHP script in a loop like this ?
...
while (...)
{
...
header("Location:myscript.php");
...
}
...
Nope. header("Location: ...") is supposed to redirect the browser to a different page, so only one of the calls you make will take effect.
What do you want to do?
You can always include the script from another to execute it's logic:
include('myscript.php');
In principle, this shouldn't require refactoring any myscript.php code. Be forewarned - myscript.php and the containing script will share the same global namespace, which may introduce bugs. (For instance, if the container outputs HTML and myscript calls session_start() a warning will be generated).
What you propose should work fine, however not in the way you expect. The header() function simply sends information to the browser in a single batch before the script content (You modify the http headers). So when the script finishes execution the browser will go to the specified page, hence only the last call to header('Location... will have any effect and that effect will only happen when the php script has finished executing.
A good way to do what I think you want to do would be to encapsulate the functionality of 'myscript.php' into a function.
include 'myscript.php';
while (...)
{
...
myscriptFunction();
...
}
...
You can call header() in a loop, but with the location header, the browser will only follow one.
location:<url> tells the browser to go to the url specified. it is known as a 301 redirect. Why you would call it in a loop, I don't know.
No. Rather pass it as a request parameter, assuming you're trying to redirect to self. E.g.
<?php
$i = isset($_GET['i']) ? intval($_GET['i']) : 10; // Or whatever loop count you'd like to have.
if ($i-- > 0) {
header("Location:myscript.php?i=" . $i);
}
?>
I however highly question the sense/value of this :)
Update, you just want to include a PHP script/template in a loop? Then use include() instead.
while ( ... )
include('myscript.php');
}
If it contains global code, then it will get evaluated and executed that many times.

While loop combined with header() in PHP

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;

Categories