php buffering with ob_start: synchronous alternative - php

Hy community
I have some troubles with php and could not find a solution. I am currently developing a wordpress plugin, and what I would like to do is to manipulate some content. Using php buffering (ob_start) which works fine, but gives me some new troubles. What I am doing is the following (minimalized).
Let's assume that my webpage contains the following text:
Hy there, my name is A B, I am living in C with my dog D
What the php code is doing: replace a set of strings with the output of a subfunction. Of course, this is just a minimal example.
<?php
// -----------------------------------------
// The Function loading some content from a php file
function my_function() {
ob_start();
require_once("some_file.php");
$content = ob_get_contents();
ob_end_clean();
return($content); // Returns new content
}
// -----------------------------------------
// Content of the web page
$content = "Hy there, my name is A B, I am living in C with my dog D";
// Strings to replace (by the content returned by "my_function")
$matches = array("A","B","C","D");
// Looping over the different matches
foreach ( $matches as $match ) {
// Calling my_function in buffer mode
$content = str_replace($match,call_user_func("my_function"),$content);
} ?>
Well, what happens now is that the buffering is per definition async. As soon as the first my_function call is finished, the whole buffer will be cleaned and "A" is not replaced by what "A" should be replaced, but also contains parts of what "B" should be replaced with :). If there is only one thing to replace, that works excellent (only having one ob_start process).
Is there any other way to catch output of a include or require call, or to run ob_* in a synchronous way? Maybe there would be a way nicer way I have not found. Would be great to get a hint :).
Thanks in advance! Maybe I am completely on the wrong track, but that's how things are getting learned :).
Happy easter,
Reto

Because you use require_once.
But for require_once file has included ONCE!
You can use require or include for multiple including.
But this is bad way. Bad architecture.

Related

str_replace text with PHP

My users want to be able to have a contact form on their website. Understandable. But it's not really working out.
<?php
function ubbreplace($text){
$text = str_replace("[contact-form]",'<?php include("contactform.php"); ?>',$text);
return $text;
}
?>
The include is not including the contact form. Why is that?
The str_replace function that you're using is working as expected. If you don't see anything in your browser, view the source code and you'll see a <?php tag within your HTML code.
The output is the stuff that normally goes to your browser. A buffer is a cache of data. Output buffer is a cache of data that would have normally gone to your browser, but didn't because you buffered it instead.
To get your desired results, we need to grab the contents of the contactform.php file and replace [contact-form] with those contents. We can do this by capturing the output from the contactform.php into a variable and using that variable as the replacement.
<?php
function ubbreplace($text){
if(strpos($text, '[contact-form]') !== false) {
ob_start();
require 'contactform.php';
$replace = ob_get_contents();
ob_end_clean();
$text = str_replace('[contact-form]', $replace, $text);
}
return $text;
}
$content = ubbreplace('Hello world! [contact-form]');
echo $content;
?>
You are approaching the concept entirely from the wrong end, what you are doing is working with strings, and these strings will not be processed by PHP as functions, or includes or other core markup.
you can insert variables into a string but this happens at execution time, and the string will not then be re-executed (and also ignores the fact the include is not a variable at all but is a language construct).
So, what can you do about this? Well - rearrange your code with the logic that:
You have a string you want to find, and then act once it's been found.
So, to do this try this code logic (customise, obviously). You want to find the "flag" you have set and then replace it with a correct marker,
<?php
if( stripos($text,"[contact-form]") !== false){
include("contactform.php");
}
?>
The above will maybe not do exactly as you intend, because its behaviour depends heavily on what is inside the included PHP file.
You will maybe have to rearrange your include contents (you can return data from an include if you really need to, but I don't really recommend that).
As a small improvement I would also recommend using mb_stripos() function instead of the standard stripos();.
So to get a cleaner more usable result, set the contents of the include to a variable such as $contactForm = "HTML contact form data"; and then always run the include, but only output the contents if the flag is found:
include contains:
$contactForm = "Some HTML contact data";
parent file contains:
<?php
include("contactform.php");
if( stripos($text,"[contact-form]") !== false){
print $contactForm;
}
?>
Or what is very probably easier for you to implement is:
<?php
include("contactform.php");
function ubbreplace($text){
$text = str_replace("[contact-form]",$contactForm,$text);
return $text;
}
?>
Include in the PHP manual, Please note references to return values
Splash58's Answer (and Brogans Answer) of using output buffering is also a perfectly
good solution and saves a lot of the effort of quantifying included output into
varaibles in my answer, although my answer is primarily to explain the
purpose and the failings of your original question.
Given a completely open option I would choose to use Output buffering
to solve this issue but you do need to know what's going on, so if
output buffering is new to you definitely read up on it first.
function ubbreplace($text){
if (strpos($text, "[contact-form]") !== false) {
ob_start();
include("contactform.php");
$replace = ob_get_contents();
ob_end_clean();
$text = str_replace("[contact-form]", $replace, $text);
}
return $text;
}

Inserting output into page after document has been executed

In PHP have a situation where I need the page to be mostly executed, but have an item inserted into the output from that page.
I think output buffering may be of some help, but I can't work out how to implement it in my situation.
My code looks like this:
//this document is part of a global functions file
function pageHeader (){
//I'm using $GLOBALS here because it works, however I would really rather a better method if possible
$GLOBALS['error_handler'] = new ErrorHandler(); //ErrorHandler class sets a function for set_error_handler, which gets an array of errors from the executed page
require_once($_SERVER['DOCUMENT_ROOT'].'/sales/global/_header.php');
//I would like the unordered list from ->displayErrorNotice() to be displayed here, but if I do that the list is empty because the list was output before the rest of the document was executed
}
function pageFooter (){
$GLOBALS['error_handler'] ->displayErrorNotice(); //this function displays the errors as an html unordered list
include($_SERVER['DOCUMENT_ROOT']."/sales/global/_footer.php");
}
Most pages on the site include this document and use the pageHeader() and pageFooter() functions. What I am trying to achieve is to put an unordered list of the PHP generated errors into an HTML list just at a point after _header.php has been included. I can get the list to work as intended if I put it in the footer (after the document has been executed), but I don't want it there. I guess I could move it with JS, but I think there must be a PHP solution.
UPDATE
I'm wondering whether a callback function for ob_start() which searches the buffer by regex where to put the error list, and then inserts it will be the solution.
UPDATE 2 I have solved the problem, my answer is below. I will accept it in 2 days when I am allowed.
Worked it out finally. The key was to buffer the output, and search the buffer for a given snippet of html, and replace it with the unordered list.
My implementation is like this:
function outputBufferCallback($buffer){
return str_replace("<insert_errors>", $GLOBALS['error_handler']->returnErrorNotice(), $buffer);
}
function pageHeader (){
ob_start('outputBufferCallback');
//I'm using $GLOBALS here because it works, however I would really rather a better method if possible
$GLOBALS['error_handler'] = new ErrorHandler(); //ErrorHandler class sets a function for set_error_handler, which gets an array of errors from the executed page
require_once($_SERVER['DOCUMENT_ROOT'].'/sales/global/_header.php');
echo '<insert_errors>'; //this snippet is replaced by the ul at buffer flush
}
function pageFooter (){
include($_SERVER['DOCUMENT_ROOT']."/sales/global/_footer.php");
ob_end_flush();
}
If I'm getting this right, you're trying to insert some calculated code/errors between the header and footer. I'm guessing that the errors are being totalled/summed up at the very end of the page and would be completed after the page footer.
If this is true, I can't think of anyway to do this with pure php. It can only run through a page once, and cannot double back. What you can do is create an element after the footer and move it using javascript to the area where you want to display it. This would be the easiest way I would think. You can do this easily with jquery.
I can explain further if I am on the right track, but I'm not 100% sure what you're asking yet...
The jquery command you would use is .appendTo().

variable predefinition, moving variable to earlier point

Ok, here's the deal where I'm really stuck.
I use my cms with design patterns.
Where's the last final output(in design template's main file: 'design.php'):
<div>{CONTENT_HEADER}</div>
<div style='float:left;width:25%'>{CONTENT_LEFT}</div>
<div style='float:left;width:50%'>{CONTENT_CENTER}</div>
<div style='float:left;width:25%'>{CONTENT_RIGHT}</div>
<div>{CONTENT_FOOTER}</div>
Where is CONTENT_XXXX is generater thought site modules:
ob_start("gz_handler");
<....... LOAD ALL THE HEADER MODULES .......>
$output0 = ob_get_contents();
ob_end_clean();
ob_start("gz_handler");
<....... LOAD ALL THE LEFT SIDE MODULES .......>
$output1 = ob_get_contents();
ob_end_clean();
ob_start("gz_handler");
<....... LOAD ALL THE CENTER SIDE MODULES .......>
$output2 = ob_get_contents();
ob_end_clean();
ob_start("gz_handler");
<....... LOAD ALL THE RIGHT SIDE MODULES .......>
....
// That's I talk about, but this is just for static html, not variables
// Push to header finalizers to override default <title></title>
$header_finalizers_array['change_head_title_attr_to'] = $thread_data['topic_title'];
<....... END OF ALL THE RIGHT SIDE MODULES .......>
$output3 = ob_get_contents();
ob_end_clean();
ob_start("gz_handler");
<....... LOAD ALL THE FOOTER MODULES .......>
$output5 = ob_get_contents();
ob_end_clean();
define("CONTENT_HEADER", finalize_output($output0,$header_finalizers_array));
define("CONTENT_LEFT", finalize_output($output2,$left_finalizers_array));
define("CONTENT_CENTER", finalize_output($output3,$center_finalizers_array));
define("CONTENT_RIGHT", finalize_output($output4,$right_finalizers_array));
// No need finalizations, because there are no more parts, which call content changes
define("CONTENT_FOOTER", $output5);
All is ok in 95% of cases. But, there are some cases, where I need TO GET THE CONTENT OF VARIABLE(in left side modules) which ONLY will be defined in NEXT SIDE(ex. in right side modules)
With my code, I can define the site headers in 'header modules', but then if I want that, I'm able to change it in ex. right side, because I'm able add to array the replace data requirement, which will be executed after all the side content will be generater to variables but BEFORE printing in to client browser.
All this could be done only with 1 line in function:
function finalize_output($output="",$fin_array=array()) {
<...>
$output = preg_replace("#<title>.*</title>#i", "<title>".$fin_array['change_head_title_attr_to']."</title>", $output, 1);
<...>
}
Now about my problem.
How to do the same with VARIABLES:
IN LEFT SIDE modules I have a sentence (PROBLEM: $object ):
if(isset($object)) {
$best_product = sqlArray(sqlQuery("SELECT * FROM tableA WHERE b='$object'"));
<..All the rest code of BEST's product..>
}
IN RIGHT SIDE modules I have the sencence (PROBLEM: $object ):
<... Print LAST 10 PRODUCTS ***>
$res = sqlquery("SELECT * FROM tableA ORDER BY id DESC LIMIT 100);
while($data = sqlarray($res)) { <..PRINT PRODUCT INFO..> }
$new = rand(0,secured($_POST['user_input_new_products']));
for($i=1;$i<=$new;$i++) {
$price_diff_old_new1 = change_products_to_sql('fish_$i', 19.99);
$price_diff_old_new2 = push_product_to_sql('crab_$i', 16.99);
if($i==$new) {
$object = $price_diff_old_new1+$price_diff_old_new2;
}
}
THIS IS JUST EXAMPLE CODE OF WHAT I NEED TO DO(so don't go to details), but THE POINT IS THAT, that I need somehow to submit the variable to earlier point of source:
One of solutions would be " USE GOTO ", but is it variable will be remebered.
I mean:
echo "JOB IS STARTED";
LABEL HOME:
<INCLUDED FILE : procuts.php >
if(isset($object) && check_is_number($object)) {
$a = $object;
}
if(isset($break_me) && $break_me) {
GOTO FINAL;
}
<END OF INCLUDE>
...
<INCLUDED FILE : upload.php >
$object = 999;
$break_me = true;
GOTO HOME;
<END OF INCLUDE>
}
LABEL FINAL:
echo "JOB IS DONE";
AND, is there are any other way to do this WITHOUT GOTO(if GOTO solution is possible(?)) ?
I personally thought that is not possible to code it, but since Php 5.3.0 it is possible, I become likely to be coded. Misfortunelly I'm not sure the company will use Php 5.3.0 on their servers, so I hope there is another solutions.
Also I'm still not sure even the GOTO will help there.
I suggest rethinking your architecture.
No offense, but the code looks rather messy to me.
To point out a few things:
you said you use Design Patterns, but the only pattern seems to be a Template View. You don't seem to be using MVC, nor a Two-Step View, which would both be appropriate in your case.
is there any particular reason why you are not using OOP? It's not that procedural coding is a bad thing, but proper encapsulation of things that conceptually go together and separation of concerns will make your app much more maintainable.
your variable names are not meaningful. An $output0 really tells you nothing about what is inside. Why does $object hold a number? The same is true for your function names: push_to_sql() and change_to_sql() sound alike. I guess one means insert and one means update, but the semantics could be much clearer.
Why are you assigning the variables holding your content-blocks to constants? That step is completely superfluous as you can use the variables in your template view as well.
Now, I understand you used example code in your question's description, so bear with me if I addressed points not applicable to your production code. But still, the simple fact that your modules cannot be rearranged without affecting the other indicates a code smell. Modules should be self-contained.
But let's get to your issue now.
You say you need to submit the variable to an earlier point in the source. I do not understand the example code enough to figure out why. But if you have to keep your Big Ball of Mudcurrent architecture, you could refactor all the code that will be shared among modules into a separate function. Then preload all required data and inject it into the functions rendering the content-blocks. This way, you should be able to push them around as wanted.
If you don't have to keep your current architecture, do yourself a favor and have a look at the patterns linked above. Most PHP frameworks utilize them and you might want to consider migrating your app onto one. To further improve your code, have a look at these QA tools and check out these sites:
http://www.tuxradar.com/practicalphp
http://www.php.net/manual/en/language.oop5.patterns.php
http://www.ibm.com/developerworks/library/os-php-designptrns/
http://www.fluffycat.com/PHP-Design-Patterns/
http://sourcemaking.com/design%5Fpatterns

Implementing the View in MVC or MVP (in PHP)

I've experienced first hand the extent of the horror and foot-shooting that the ugliness of PHP can cause. I'm onto my next project (you may be wondering why I'm not just switching languages but that's not why I'm here) and I've decided to try doing it right, or at least better, this time.
I've got some models defined, and I've started on a main controller. I'm at a fork in my decisions about how to implement the view. So far, the main controller can be given lists of display functions to call, and then it can spew out the whole page with one call. It looks like:
function Parse_Body()
{
foreach ($this->body_calls as $command)
{
$call = $command['call'];
if (isset($command['args'])) $call($command['args']);
else $call();
}
}
My dilemma is this:
Would it be better to have all of my display functions return the HTML they generate, so that the main controller can just echo $page; or should the display files use raw HTML outside of PHP, which gets output as soon as it's read?
With the former, the main app controller can precisely control when things get output, without just relinquishing complete control to the whim of the displays. Not to mention, all those lists of display functions to call (above) can't really be executed from a display file unless they got passed along. With the latter method, I get the benefit of doing HTML in actual HTML, instead of doing huge PHP string blocks. Plus I can just include the file to run it, instead of calling a function. So I guess with that method, a file is like a function.
Any input or advice please?
Would it be better to have all of my
display functions return the HTML they
generate, so that the main controller
can just echo $page; or should the
display files use raw HTML outside of
PHP, which gets output as soon as it's
read?
One of the advantages of php is that the processing is similar to the output:
So:
<h1> <?= $myHeading; ?> </h1>
Is more clear than:
echo "<h1>$myHeading</h1>";
An even more than:
echo heading1($myHeading); //heading1() being an hypothethical user defined function.
Based on that I consider that it is better to in the view to have HTML and and just print the appropriate dynamic fields using php.
In order to get finner control over the output you can use: ob_start as gurunu recommended.
You could of course use any of the several php MVC frameworks out there.
My prefered one, now is: Solarphp
but Zend Framework and Cakephp could help you too.
And finally if you don't want to use any framework
You could still use a pretty slim templating engine: phpSavant.
That will save you a few headaches in the development of your view.
th
You can get the benefit of both, obtaining a string of HTML while also embedding HTML within PHP code, by using the output control functions:
From the PHP manual # http://www.php.net/manual/en/ref.outcontrol.php:
<?php
function callback($buffer)
{
// replace all the apples with oranges
return (str_replace("apples", "oranges", $buffer));
}
ob_start("callback");
?>
<html>
<body>
<p>It's like comparing apples to oranges.</p>
</body>
</html>
<?php
ob_end_flush();
?>
First buffer everything. then replace tags using a parser at end of script.
<?php
$page_buffer = '';
function p($s){
global $page_buffer;
$page_buffer .= $s;
}
$page_buffer = str_replace(
array('<$content$>','<$title$>'),
array($pagecontent,$pagetitle),
$page_buffer);
echo $page_buffer;
?>
Samstyle PHP Framework implements output buffering and View model this way
And did I mention about benefits of buffering your output in a variable before "echo-ing"? http://thephpcode.blogspot.com/2009/02/php-output-buffering.html

PHP: re-parse entire page before serving?

At the end of a page, if something occurs, it needs to be cleared, then the entire page needs to be re-parsed before serving to the client. I was going to echo out a javascript to refresh the page, but that will make them load the page and then reload it...I was wondering if there was a way to just tell the php engine to go back to the beginning and re-parse the entire page?
Thanks!
I will try to explain the problem more clearly but it is complicated and I am a terrible communicator. I on the page that lists products I am giving users the option to select fields to narrow the results. The system remembers this so they don't have to keep selected them. If they narrow a category like metal color and then go to a category that metal color is irrelevant like crystal figurines it will not show any results because none will match the metal color chosen. To generate the query to pull the products from the data-base is very complicated because different categories have different requirements to find the correct products. so once the query is generated I want to test it against mysql_num_rows() and if there is no results clear out the users choices and start over.
You're being a little vague, but if you're merely talking about reparsing the output, you could do that using output buffering.
I'm not entirely clear what the issue is, but couldn't you decide what is to be shown before creating the HTML, and then send the right thing the first time?
To generate the query to pull the products from the data-base is very complicated because different categories have different requirements to find the correct products. so once the query is generated I want to test it against mysql_num_rows() and if there is no results clear out the users choices and start over.
In that case, just put the query inside a function that returns the result, check the row count, and if it's zero clear the filters and call that function a second time.
Output buffering (ob_start and ob_clean), combined with separating the functionality at hand into a separate file and eval()'ing that should do the trick.
Oh, and recent PHP versions actually have a goto statement... although I'll always deny mentioning anything about it. :-)
I think you're going about it a little bit off.
What you should do to reparse the page is to redirect the user to the page again, using
header('Location: thepagefile.php');
however if you actually would like to reparse the file without creating a new page, you could also just include the file again:
include thepagefile.php
But you'd probably get the same result. If you want to actually parse the output of the page you'd do something like:
ob_start(); // this is at the very top of the code/page
// all the code goes here
$output = ob_get_clean();
eval($output); // WTF?
but that actually makes no sense, but I hope it helps.
I'd actually like to know what the real problem you're trying to solve really is.
I think your looking for something like this:
<?php
ob_start(); //we start output buffering, this means nothing is send to the browser
//We do some code stuff
$time = microtime();
echo "Hai \n"; //Note taht mixing logic and output in real life
echo $time; // is terribly practice
echo "\n bai"; //I do it here purely for the example
if(/*some condition */){
$anErrorHappened = true;
}
if($anEroorHappened === true){
//Load the output in a var if you need it
//Otherwise don't
$output = ob_get_clean();
//Do other code stuff
//I.E. send an error page
include('errorPage.html');
}
else{
ob_end_flush(); //Send everything the script has echo()'d, print()'ed and send to the browser in any other way (I.E. readfile(), header() etc.)
}
?>

Categories