0

Is there a way to automatically include content before and after the actual output of a file?

Why? For example to use this to include everything up to the main content (dynamcally generated HTML, head, opening tags...) and after the file runs, automatically close everything up again.

I know of the ob_start approach, but I'm not sure if dynamically generated content is easy to include that way:

<?php
function bootstrap_page($content) {
    return "text before" . $content . "text after";
}

ob_start(bootstrap_page);
?>

But then, ob cannot be used to capture the output of an include within the callback, AFAIK. So that makes it hard to easily pre- and append something dynamically generated. I could use long strings in the callback function to get a static version working - but is there a way to do this more seamlessly?

In other words I'm basically trying to include a php file before and one after any (other) file I need and that - if possible reduced to a function call at the start of a given file.

The functionality I'm looking for would transform this:

<?php
bootstrap_this();
?>

<p>Lorem ipsum</p>

before.php:

<!DOCTYPE html>
<html>
  <?php include('head.php'); ?>
  <body>
    <?php if(somecondition) { ?>
    <h1>Hello, World!</h1>
    <?php } ?>

after.php:

  </body>
</html>

Into something like this:

<?php
include 'before.php';
?>
<p>Lorem ipsum</p>
<?php
include 'after.php';
?>

And in the end into:

<!DOCTYPE html>
<html>
  <?php include('head.php'); ?>
  <body>
    <?php if(somecondition) { ?>
    <h1>Hello, World!</h1>
    <?php } ?>
    <p>Lorem ipsum</p>
  </body>
</html>
rudib
  • 227
  • 1
  • 10
  • Use a framework or at least a templating engine. Why would you write raw PHP? – ryantxr May 07 '20 at 21:55
  • @ryantxr idk - just to suffer? I got away without one so far - but if there is not an easy way to achieve it, I may have to. – rudib May 07 '20 at 22:03
  • I really don't understand. You want to know how to include some value to prepend and append to a content string to a function that is not hard-coded as your example demonstrates ("text before") ("text after")? – bestprogrammerintheworld May 07 '20 at 22:05
  • @bestprogrammerintheworld maybe I wasn't clear enough - I'll try to update my question. I'm basically trying to include a php file before and one after any (other) file I need. The function shown does something like that, but with just strings. – rudib May 07 '20 at 22:09
  • Does this help? https://stackoverflow.com/questions/3332262/how-do-i-prepend-file-to-beginning (Maybe answer from Alex?) – bestprogrammerintheworld May 07 '20 at 22:14
  • @bestprogrammerintheworld no, because it's just the file content - it needs to be run. I've updated my question, maybe it's a bit clearer now what I'm trying to do. – rudib May 07 '20 at 22:19
  • 1
    I've added an answer but I'm not sure if I got it. – bestprogrammerintheworld May 07 '20 at 22:42
  • Use output buffers as you mentioned. I think it's the only way. I've updated my answer. – bestprogrammerintheworld May 07 '20 at 23:13
  • 1
    @bestprogrammerintheworld thanks for you effort. Yes, it seems like you can't sandwich the output out the current file easily. This would also have to be run from another file. – rudib May 07 '20 at 23:15
  • My last update2 might give you a hint/clue how you oould create what you want to create. This is generally not something you code yourself :-) – bestprogrammerintheworld May 07 '20 at 23:26
  • 1
    @bestprogrammerintheworld thank you! I does what I asked, but It makes my code more complicated than before, so I'll probably still have to opt for the not coding myself option and use some kind of framework. ;) – rudib May 07 '20 at 23:33
  • 1
    Thank you. I'm glad to help you ... to figure out you should not code this yourself ;-) – bestprogrammerintheworld May 07 '20 at 23:38
  • 1
    @bestprogrammerintheworld you might have given me an idea though. I'll try that and maybe I can provide an update. – rudib May 07 '20 at 23:39

4 Answers4

2

Isn't that what output buffering is for?

<?php
// Start Buffer
ob_start();

// Include before
include 'before.php'; 
?>

<p>Lorem ipsum</p>

<?php
// Include after
include 'after.php';

// Get buffered output
$page = ob_get_clean();

echo $page;
?>
davidgiesemann
  • 941
  • 6
  • 16
  • 1
    I think OP knew this, but IMO OP was looking for a way to make a function out of this where before, content and after is the included files. – bestprogrammerintheworld May 07 '20 at 23:16
  • 1
    In that case your answer is a possible approach. – davidgiesemann May 07 '20 at 23:17
  • @davidgiesemann yes, i knew this. I'm basically trying to sandwich the output of a php file between the output of two other ones (or even better: at a given point in one file) by calling a function at the start of the file to be sandwiched. Basically a poor mans template engine. – rudib May 07 '20 at 23:22
1

But then, ob cannot be used to capture the output of an include within the callback, AFAIK

AFAYK ? Would it be hard to test? As long as the include is after ob_start() and the code does not explicitly call ob_flush() before you choose to do so, then it will capture the output.

I'm basically trying to include a php file before and one after any (other) file I need

That implies some set sort of controlling script which calls the pre-oinclude, the main content and the post-include.

That would be OK if HTML (not true, I'll come back to that) did not have a defined root which should be explicitly declared. And you have the issue HTTP also has a structure which you risk subverting here - headers come before content. But leaving those aside for now, HTML requires a nested structure. All tags should be closed. Opening and closing tags in different files is messy and bad practice.

There are a whole lot technologies which provide the end result you appear to be looking for - ESI, templating and front-controller patterns all provide this in a much more structured way.

symcbean
  • 47,736
  • 6
  • 59
  • 94
  • No, in fact I did test it - I just wasn't sure if I did it right. But it lead to: `ob_start(): Cannot use output buffering in output buffering display handlers`. – rudib May 07 '20 at 22:29
  • https://stackoverflow.com/questions/33936067/cannot-use-output-buffering-in-output-buffering-display-handlers – symcbean May 07 '20 at 22:31
  • And: technically, it could be done in the same file - I just explained it like that for simplicity. – rudib May 07 '20 at 22:32
  • I already found that question. That's part of the reason I asked if there is another way to achieve this. If I understood correctly, it's impossible to use `ob` in the callback. – rudib May 07 '20 at 22:37
1

I'm not sure I see the usage of this or if I understood this correct, but if I understood it correctly you're looking for something like this:

<?php
function dynamice_include($before, $content, $after) {
    $dynamic_content = '';
    $dynamic_content .= include $before . '.php';
    $dynamic_content .= $content;
    $dynamic_content .= include $after . '.php';
    return $dynamic_content;
}

Usage:

$content = dynamice_include('before', 'Hello I am really cool','after');
echo $content;

In before.php and after.php a return would be required, e.g.

before.php
<?php
return "wow before";

after.php
<?php
return "wow after";

and the result would be:

wow beforeHello I am really coolwow after 

UPDATE:

It seems it more something like this you're looking for. output-buffers are the only way AFAIK to achieve this.

This code is not optimized at all... (I just show the concept here)

<?php
function dynamice_include($before, $content, $after) {
    $dynamic_content = '';

    ob_start();
    include $before . '.php';
    $dynamic_content .= ob_get_contents();
    ob_end_clean();  

    ob_start();
    include $content . '.php';
    $dynamic_content .= ob_get_contents();
    ob_end_clean();  

    ob_start();
    include $after . '.php';
    $dynamic_content .= ob_get_contents();    
    ob_end_clean();  

    return $dynamic_content;
}

$content = dynamice_include('before', 'dytest','after');
echo $content;

As other stated though - it's a lot of platforms, frameworks, template engines out there that could solve this issue. You will have do ob_start() and ob_clean within the current files content for this to work.

UPDATE2:

In this case I fetch current files output buffer as content.

<?php
function dynamice_include($before, $content, $after) {
    $dynamic_content = '';

    ob_start();
    include $before . '.php';
    $dynamic_content .= ob_get_contents();
    ob_end_clean();  

    $dynamic_content .= $content;

    ob_start();
    include $after . '.php';
    $dynamic_content .= ob_get_contents();    
    ob_end_clean();  

    return $dynamic_content;
}
ob_start();
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    feelings    
</body>
</html>
<?php
$content = ob_get_contents();
ob_end_clean();

$content = dynamice_include('before', $content, 'after');
echo $content;
?>
bestprogrammerintheworld
  • 5,417
  • 7
  • 43
  • 72
  • pretty much, except `$content` is the captured output of a file. Wait - It's possible to run an get the output in the form of a string with include? If that's the case, my question was useless... – rudib May 07 '20 at 22:45
  • Oh I see - no `before.php` and `after.php` contain html with some php and not just strings, so I can't really return it. – rudib May 07 '20 at 22:48
  • Just get a files content with file_get_contents(), e.g. $str = file_get_contents($file); – bestprogrammerintheworld May 07 '20 at 22:50
  • @but it's not a static html file - it contains some php code that inserts some information and decides which parts are rendered. I'll try to update my question with a simple example... – rudib May 07 '20 at 22:52
1

Thanks to the help of @bestprogrammerintheworld, I came up with this:

function use_template($before = 'pre', $after = 'post') {
    ob_start();
    include $before . '.php';
    $pre = ob_get_contents();
    ob_end_clean();

    ob_start();
    include $after . '.php';
    $post = ob_get_contents();
    ob_end_clean();

    $bootstrap_page = function ($content) use ($pre, $post) {
        return $pre . $content . $post;
    };

    ob_start($bootstrap_page);
}

If this function is called a the beginning of a php file, the outputs of before.php and after.php get stored and bound to the callback. Then, after all the main output is read, everything is pieced together. No code at the end of the file required.

Since ob cannot be run within the callback, bootstrap_page, it must be run beforehand to capture the other files first.

rudib
  • 227
  • 1
  • 10