How to use ob_start on a string? - php

I have a project where I am using OB_START to gather output from a PHP file. The problem is that sometimes I need the contents of the PHP file 20 - 30 times in 1 call.
I'd like to do something like get_file_contents({file}) then use that string for the OB_START() call. However, all the examples I've seen use an include() call to get the script each time.
Is there a way to load the script one time but use it several times in OB_START() calls?
ob_start();
include "file.php";
$output = ob_get_clean();
What I would like to do:
$script = get_file_contents(file);
$output = '';
begin loop;
ob_start();
{somehow make $script execute as code}
$output .= ob_get_clean();
end loop;

You can just use include over-and-over again.
$file = "SomeScript.php"
$output = '';
begin loop;
ob_start();
include $file
$output .= ob_get_clean();
end loop;
If you're dead-set on doing it the way you're describing, you can do the following, but it's weird:
$script = get_file_contents(file);
$output = '';
begin loop;
ob_start();
eval($script);
$output .= ob_get_clean();
end loop;
In my opinion, a much better solution would be to make the script a function that outputs it's results instead of printing them. So let's say you put the "script" into a function call foo(), Then you could do this:
$file = "SomeScript.php"
include $file
$output = '';
begin loop;
$output .= foo();
end loop;
But if you cannot change the contents of the script, then my first two examples should work for you.

Related

PHP File Handling (Download Counter) Reading file data as a number, writing it as that plus 1

I'm trying to make a download counter in a website for a video game in PHP, but for some reason, instead of incrementing the contents of the downloadcount.txt file by 1, it takes the number, increments it, and appends it to the end of the file. How could I just make it replace the file contents instead of appending it?
Here's the source:
<?php
ob_start();
$newURL = 'versions/v1.0.0aplha/Dungeon1UP.zip';
//header('Location: '.$newURL);
//increment download counter
$file = fopen("downloadcount.txt", "w+") or die("Unable to open file!");
$content = fread($file,filesize("downloadcount.txt"));
echo $content;
$output = (int) $content + 1;
//$output = 'test';
fwrite($file, $output);
fclose($file);
ob_end_flush();
?>
The number in the file is supposed to increase by one every time, but instead, it gives me numbers like this: 101110121011101310111012101110149.2233720368548E+189.2233720368548E+189.2233720368548E+18
As correctly pointed out in one of the comments, for your specific case you can use fseek ( $file, 0 ) right before writing, such as:
fseek ( $file, 0 );
fwrite($file, $output);
Or even simpler you can rewind($file) before writing, this will ensure that the next write happens at byte 0 - ie the start of the file.
The reason why the file gets appended it is because you're opening the file in append and truncate mode, that is "w+". You have to open it in readwrite mode in case you do not want to reset the contents, just "r+" on your fopen, such as:
fopen("downloadcount.txt", "r+")
Just make sure the file exists before writing!
Please see fopen modes here:
https://www.php.net/manual/en/function.fopen.php
And working code here:
https://bpaste.net/show/iasj
It will be much simpler to use file_get_contents/file_put_contents:
// update with more precise path to file:
$content = file_get_contents(__DIR__ . "/downloadcount.txt");
echo $content;
$output = (int) $content + 1;
// by default `file_put_contents` overwrites file content
file_put_contents(__DIR__ . "/downloadcount.txt", $output);
That appending should just be a typecasting problem, but I would not encourage you to handle counts the file way. In order to count the number of downloads for a file, it's better to make a database update of a row using transactions to handle concurrency properly, as doing it the file way could compromise accuracy.
You can get the content, check if the file has data. If not initialise to 0 and then just replace the content.
$fileContent = file_get_contents("downloadcount.txt");
$content = (!empty($fileContent) ? $fileContent : 0);
$content++;
file_put_contents('downloadcount.txt', $content);
Check $str or directly content inside the file

How to return an included file in PHP without using a return

I am trying to turn an existing website in an API, in order to do that I need to return some content as HTML inside a JSON. The problem I am having is that I can't find a way to make templating work.
I tried to do this
class TestController
{
public function get() {
$adapter = new Adapter();
$data = 'some data';
$html = include $_SERVER['DOCUMENT_ROOT'] . 'template.php';
$result = [
'html' => $html
];
return json_encode($result);
}
template.php:
<div>
<br/><?= $data ?><br/>
</div>
but it is returning the HTML alongside the JSON and of course html = 1 because include returns 1 on success, like this:
<div>
<br/>some data<br/>
</div>
{'html':1}
Is there a way for me to get the content from the included file without using a return statement inside the template?
Your problem is that using include essentially treats your template.php file as a single giant "echo" because there is no value returned inside template.php
You could change the template.php file to return a HTML string, and it would achieve what you want, but this is a nightmare to look at and manage.
return '<div>
<br/>' . $data . '<br/>
</div>';
An alternative, is to "capture" the output of the included file. Its a lot nicer on your template.php file and achieves the same thing. The output buffer functions essentially capture any information "echoed" - you need to be careful using this and the placement of your session_start however as it can catch you off guard depending on how your application is bootstrapped.
This is done by using three ob_ functions the main one being ob_get_contents which will be the contents of template.php in the example below.
http://php.net/manual/en/function.ob-get-contents.php
ob_start();
include $_SERVER['DOCUMENT_ROOT'] . 'template.php';
$html = ob_get_contents();
ob_end_clean();
This was your template files stay nice and clean, and the contents are all captured for returning in your json format.
you can use ob_start like this
ob_start();
include 'path_to_your_file.php';
$result= ob_get_contents();
ob_end_clean();

dynamically get results from exec

I have a php script that calls a go script. It gets results every 1-2 seconds, and print's them. Using php's exec and output, I only get the results when the program finishes. Is there a way I can check the output to see when it changes and output that while it's still running?
Something like this, but pausing the execution?:
$return_status = 0;
$output = [];
$old_output = ["SOMETHING ELSE"];
while ($return_status == 0) {
exec($my_program,$output,$return_status); #somehow pause this?
if $output != $old_output {
echo($output);
$old_output = $output;
}
}
Yes. Use the popen() function to get a file handle for the command's output, then read from it a line at a time.

PHP - Executing PHP from a string

This one is a bit of a weird one. Ive created a function designed to select a template and either include it or parse the %0, %1,%3 etc. variables. This is the current function:
if(!fopen($tf,"r")){
$this->template("error",array("404"));
}
$th = fopen($tf,"r");
$t = fread($th, filesize($tf) );
$i=0;
for($i;$i<count($params);$i++){
$i2 = '%' . $i;
$t = str_replace($i2,$params[$i],$t);
}
echo $t . "\n";
fclose($th);
Where $th is the relative directory to my template file. My issue is, I need to execute the PHP inside of these files whilst at the same tme being able to replace the string variables %0 %1 etc.
How could I go about attempting this?
Like I said in my comment I think a template engine like Smarty would probably serve you better but here's how I'd do it with output buffering rather than eval()
Something like this
ob_start();
include "your_template_file.php";
$contents = ob_get_contents(); // will contain the output of the PHP in the file
ob_end_clean();
// process your str_replace() variables out of $contents

Using print_r in ob_start

As mentioned in the manual it's not working. i tried var_dump it too suffers from the same problem.
ob_start()
$debugdata=print_r ($var,true)
This prints the result on screen than storing to a variable
The second parameter of print_r is $return which allows the output to be returned as a string rather than outputting it:
$debugData = print_r($var, true);
There is no need to use output buffering for this, and in fact it cannot be used. You will need to end the output buffering before this and then restart the buffering after your print_r call:
ob_start();
// stuff
$output = ob_end_clean();
$debugData = print_r($var, true);
ob_start();
// more stuff
$output .= ob_end_clean();
EDIT: Another option is to nest output buffers and have an inner buffer do the print_r work:
ob_start(); // your original start
// stuff
ob_start();
print_r($var);
$debugData = ob_get_clean();
// more stuff
$output = ob_get_clean(); // your original end
ob_start() starts outbut buffering. But you also need to end and retrieve the contents of the buffer as well.
Here are the functions you could use:
ob_get_clean() - puts the contents of the output buffer in a variable, ends and cleans the buffer.
ob_start();
print_r($foo);
$output = ob_get_clean();
ob_get_contents() - fetches the contents of the output buffer without closing or cleaning it.
ob_end_clean() - closes and cleans the buffer.
ob_start();
print_r($foo);
$output = ob_get_contents();
ob_end_clean();
There are a few other possibilities. Please make yourself familiar with the output buffering functions.
Also, a remark. You don't just assign the output of print_r to a variable. You just print stuff as if you were printing it on the screen. With output buffering on, all output will be buffered instead of being sent to stdout immediately. So, first you print_r, then retrieve the contents of the buffer.
[EDIT]
In the light of the conversation going on in the comments, I recommend having a look at the notes section of the print_r() manual. As #RomiHalasz and #cbuckley observe, due to print_r's internal output buffering, it cannot be used in conjunction with ob_start()
while the second parametre, return, is used, as the two will collide.
You have to EITHER use output buffering and plain print_r (with only the first parametre), or end the output buffering before you use print_r with the second parametre.
Either this:
ob_start();
print_r($foo);
$output = ob_get_clean();
or this:
ob_start();
// blah
$output = ob_get_clean();
$output .= print_r($foo,true);
ob_start();
// blah
$output .= ob_get_clean();

Categories