I use this function in my views to position various content blocks.
function block(&$block = false) {
if ($block === false) return ob_end_clean();
return ob_start(function($buffer) use (&$block) { $block = $buffer; });
}
I never had problem with it until today. My blocks don't show up on this particular project.
Dev environment: PHP 5.3.3-7+squeeze17 with Suhosin-Patch (cli) (blocks show up)
Production environment where script fail (silently): PHP 5.4.4-14+deb7u5 (cli) (blocks don't show up)
PHP logs don't show anything.
Can you help me find what is going on?
EDIT:
A bit more information on how I use this function.
Say I have a basic view page.php
<h1><?= $title ?></h1>
<div class="content">
<?= $content ?>
</div>
<?php block($scripts) ?>
<script>
// javascript here
</script>
<?php block() ?>
Then in my layout file html.php
<html>
<head></head>
<body>
...
<?php if (isset($scripts)) print $scripts ?>
</body>
</html>
EDIT2:
I use this class for my views.
class view
{
public static $globals;
public function __construct($file, $layout = null) {
$this->file = $file;
if ($layout !== null) $this->layout = $layout;
}
public function __toString() {
extract((array)self::$globals);
extract((array)$this);
start:
ob_start();
include PATH_VIEWS . $file;
if (!isset($layout)) return ob_get_clean();
$view = ob_get_clean();
$file = $layout;
unset($layout);
goto start;
}
}
die(new view('page.php', 'html.php');
ob_end_clean() returns only the top most output buffer. If prod and dev have different settings for output_buffer (http://www.php.net/manual/en/outcontrol.configuration.php#ini.output-buffering) you will see different results. You can check this with phpinfo()
Since your output_buffer options match, could you change your include to a require, just in case there's some path difference between the servers that's causing the path to mess up.
Related
I want to write a new Plugin in wordpress. My classes:
the plugin PHP-file:
<?php
/*
plugin-header (working)
*/
// Exit if accessed directly
defined('ABSPATH' || exit());
// Include classes
include('foo.php');
include('boo.php');
//add Init Hook
add_action('admin_menu','bohoo_admin');
function bohoo_admin() {
add_options_page('bohoo', 'someTitle', 'manage_options', __FILE__, 'createView');
}
function createView() {
$foo = new foo();
$boo = new boo();
return $foo->createFooDiv() . $boo->createBooDiv();
}
?>
My foo.php:
<?php
class foo {
public function __construct() {
}
public function createFooDiv() {
return '<div><h2>Hi</h2></div>';
}
}
?>
My boo.php:
<?php
class boo {
public function __construct() {
}
public function createBooDiv() {
return '<div> test </div>';
}
}
?>
Now what I basically tried is: The HTML-code should be in two different files and these should be concatenated and displayed (of course).
The way I understood integrating plugins in WP:
With the add_options_page-method you specify where your plugin is shown and what code is displayed. For the code you use the last argument (in this case the createView-method. This works so far if my createView() in my plugin PHP-file looks like this:
function createView() {
//include HTML-Code directly
?>
<h1>Hello World</h1>
<?php
}
?>
What happens when I include the boo.php and foo.php files instead of including the HTML-Code directly is nothing (So nothing is displayed and there is on error aswell). I am not sure what I am doing wrong, I also tried playing around with the HTML-Code in the return-statements of BOO and FOO, but that did not help either. What am I doing wrong? Or is it simply not possible to do it that way?
You need to print/echo your html content. Change createView to this:
function createView() {
$foo = new foo();
$boo = new boo();
echo $foo->createFooDiv() . $boo->createBooDiv();
}
That's how it's done in the add_options docs page.
And basically when you use php closing and opening tags:
?>
<h1>Some html</h1>
<?php
It's equivalent to printing the content in between:
echo '<h1>Some html</h1>';
Im working on a website - just learning to improve my coding.
I have a routing system which works like this:
/config/routes.php:
$route->add('/' , function() {
require_once("/application/pages/index.php");
});
$route->add('/register', function() {
require_once("/application/pages/register.php");
});
$route->add('/login', function() {
require_once("/application/pages/login.php");
});
$route->add('/logout', function() {
require_once("/application/pages/logout.php");
});
$route->add('/panel', function() {
require_once('/application/pages/panel/index.php');
});
And in my index.php:
require_once('application/pages/header.php');
include('config/routes.php');
require_once('application/pages/footer.php');
Everything works fine but I need a different header.php and footer.php for when the user goes into the panel. file: /application/pages/panel/index.php
When I require_once a new header file in the panel/index.php then both the new and old header file is loaded. How can I unrequire the header and footer files in the /panel/index.php so I can require different ones? Any suggestions?
Note: Routing comes from an MVC design pattern, you should keep your controllers separate from your views.
Templates and Views could be kept separate, also. This meaning our directory set-up can look something like this:
- Templates
- header_one.php
- footer_one.php
- header_two.php
- footer_two.php
- Views
- index.php
- someOtherBody.php
Here is a simple, but unfinished (that is your challenge) example of an Object that could do what I am explaining:
class Page {
private $template_path = dirname(dirname(__FILE__)) . '/templates/';
private $view_path = dirname(dirname(__FILE__)) . '/views/';
protected $header;
protected $footer;
protected $body;
public function setHeader($file_name)
{
if(is_readable($this->template_path . $file_name))
{
$this->header = $this->template_path . $file_name;
return $this;
}
// add an exception
}
/* TODO: */
public function setFooter() {}
public function setBody() {}
/* Render page */
public function render()
{
$page = [$this->header,$this->body,$this->footer];
foreach($page as $file)
{
require_once($file);
}
}
}
The idea here is that we can set our page layout, using the above object, within the route method closure, then render / require all the files after the logic.
$route->add('/', function() {
$page = new Page();
$page->setHeader('header_one.php')
->setBody('index.php')
->setFooter('footer_one.php');
/* todo: add your logic here */
$page->render();
});
Each route can now have its own header, footer and body.
Hope this helped.
At your place, I will do something like that :
Use out buffer and check if the file is already required. I give you an quick example but adapt the code for you.
And check the function : http://php.net/manual/en/function.get-included-files.php
$route->add('/panel', function() {
include_once('YOUR_SPECIFIC_PATH/header.php');
require_once('/application/pages/panel/index.php');
include_once('YOUR_SPECIFIC_PATH_header/footer.php');
});
And :
ob_start();
include_once('config/routes.php');
$mainContent = ob_get_contents();
ob_end_clean();
include_once('application/pages/header.php');
echo $mainContent;
include_once('application/pages/footer.php');
I've not the time for help more sorry but I can explain later if you need
This solution requires you to have a header.php and footer.php in each folder where your sub-controllers (application/<module name>/index.php) are.
index.php only call your sub-controllers via routing:
// require not include, because "no routing" = "no web site" ;)
require_once('config/routes.php');
application/pages/index.php include appropriate header/footer w/ relative path
require_once('header.php');
// page code
require_once('footer.php');
application/register/index.php include appropriate header/footer w/ relative path
require_once('header.php');
// page code
require_once('footer.php');
etc
#KDOT , thanks you for your help but using your code I was getting an error that I could not fix:
Call to a member function setBody() on null
but thanks to your code, I managed to rewrite the class my way and now it works ;)
Thanks again #KDOT !
If anyone needs it:
class Page {
private $TEMPLATE_PATH = '/application/templates/';
private $VIEW_PATH = '/application/views/';
protected $header;
protected $footer;
protected $body;
public function __construct($header_file, $body_file, $footer_file) {
$this->header = $this->TEMPLATE_PATH . $header_file;
$this->body = $this->VIEW_PATH . $body_file;
$this->footer = $this->TEMPLATE_PATH . $footer_file;
}
public function render(){
$page = [$this->header, $this->body, $this->footer];
foreach($page as $file) {
require_once($file);
}
}
}
and:
$route->add('/', function() {
$page = new Page('header.php', 'home.php', 'footer.php');
$page->render();
});
I am trying to dynamically echo some predefined template ('template-file-for-output.php') filled with some data (from the $var array) on a specific place in a number of pages (as an example, the page 'page.php').
Basically my goal is to have a system, in which
I set up the logic (in the 'logic.php' file), the functions needed (in 'functions.php'), the template (in 'template-file-for-output.php')
with which my colleagues can create whatever page (just as in 'page.php' for the sake of the example) they want with their content and their HTML as they wish and only need to include the functions.php and logic.php files at the beginning of their file, and the echo statement to have the dynamic content where they want it to appear.
The problem I'm having is that when I test this example and try to achieve this in 'page.php', I always get the content before the custom HTML from the page. I suppose it has to do with output buffering and this include in the outputContent function, I tried other things but without success.
Here the contents of the files:
logic.php:
$submitOK = false;
if ($submitOK === true) {
/** No errors, output the error free content */
$output = outputContent($var);
} else {
/** Errors, output the content with errors */
$output = outputContent($var, $errors);
}
functions.php:
function outputContent($var, $errors = null)
{
extract($var);
ob_start();
include 'template-file-for-output.php';
$output = ob_get_contents();
ob_get_clean();
return $output;
}
template-file-for-output.php:
<p>Some content with tags and so on, filled in by some values of the $var array.</p>
<p>Example with the $example variable extracted from $var <?php echo $example; ?></p>
<p>Another variable also from $var <?php echo $anotherVariable; ?>
page.php:
<?php
include 'logic.php';
include 'functions.php';
?>
<!DOCTYPE html>
<html>
<head><title>A page of the site</title></head>
<body>
<p>Something interesting (hopefully).</p>
<?php echo $output; ?>
</body>
</html>
Change ob_get_contents to ob_get_clean, ob_get_contents gets the contents of the buffer but leaves it intact. Your Previous code got the buffer assigned it a variable then, flushed the buffer to output.
function outputContent($var, $errors = null)
{
extract($var);
ob_start();
include 'template-file-for-output.php';
$output = ob_get_clean();
return $output;
}
Setting aside the point that there are currently templating systems that have already solved this problem quite effectively ...
I would try not include the template file, but rather read the file with file_get_contents and then echo it out inside the output buffering section.
function outputContent($var, $errors = null)
{
extract($var);
ob_start();
echo file_get_contents('template-file-for-output.php');
$output = ob_get_clean();
return $output;
}
Question Updated
I am building an MVC framework, for my templates and views, I will have a main page template file and my views will be included into this template.
The only way I have seen to do this is to use output buffereing
ob_start();
include 'userProfile.php';
$content = ob_get_clean();
Is there any other way of doing this? I think output buffering is not the best on performance as it uses a lot of memory
Here is a sample controller, the $this->view->load('userProfile', $profileData);
is the part that will be loaded using output biffering so that it can be included into the main template below into the $content part
view class
public function load($view,$data = null) {
if($data) {
$this->data = $data;
extract($data);
} elseif($this->data != null) {
extract($this->data);
}
ob_start();
require(APP_PATH . "Views/$view.php");
$content = ob_get_clean();
}
controller
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
// load a Model
$this->loadModel('profile');
//GET data from a Model
$profileData = $this->profile_model->getProfile($userId);
// load view file
$this->view->load('userProfile', $profileData);
}
}
main site template
<html>
<head>
</head>
<body>
<?php echo $content; ?>
</body>
</html>
Using a template system is not necessarily tied to output buffering. There are a couple of things in the example code you give that should certainly not be taken for granted:
One:
flushblocks(); // what does this do??
And two:
$s = ob_get_clean();
Why does the code capture the template output into a variable? Is it necessary to do some processing on this before outputting it? If not, you could simply lose the output buffering calls and let the output be sent to the browser immediately.
Can I use a script to check if a JS file is loaded?
I have a function, which places a form on a page with javascript controls. I don't know where the user will use this form, and it might be loaded several times into a page. It strikes me as the best way to handle things if the form itself loads the script, so it doesn't load if not needed, but this leads me to need to check if the script is already loaded to avoid reloading and adding to page load times and bandwidth use.
No. The page isn't seen until the PHP script has flushed all its output, by which time it's too late to do anything. But most browsers are smart enough to only load an external resource once per page anyway.
You should have an asset management system in your PHP to see whats being included into the page.
Ultra simple example (derived from link):
<?php
class Page {
private static $head = array();
private static $js_assets = array();
private static $content = '';
static function add_head($tag) {
self::$head[] = $tag;
}
static function render_head() {
foreach (self::$head as $tag) echo $tag;
foreach (self::$js_assets as $js) echo '<script src="'.$js.'" type="text/javascript"></script>';
}
static function render_content() {
echo self::$content;
}
static function read_content($file) {
ob_start();
require $file;
self::$content = ob_get_clean();
}
static function render_layout($file) {
require $file;
}
static function add_js($js) {
if (!in_array($js, self::$js_assets)) {
self::$js_assets[] = $js;
}
}
}
Page::add_js('/javascripts/application.js');
Page::read_content('view.php');
Page::render_layout('layout.php');
?>
layout.php:
<html>
<head><?php Page::render_head(); ?></head>
<body>
<div id="header"></div>
<div id="content"><?php Page::render_content(); ?></div>
<div id="footer"></div>
</body>
</html>
view.php:
<?php Page::add_head('<title>Hello World!</title>'); ?>
<h1>Hello</h1>
<p>World</p>