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>
Related
So I have my views split up basically between three (3) files:
-- Header file
$this->load->view('templates/header', $data);
-- Main Body file
$this->load->view('login_view', $data);
-- Footer file
$this->load->view('templates/footer', $data);
Now I just recently started building, but I've noticed it's really annoying to retype the header and footer on every controller to tell it to load. Is there a way to automatically load the header and footer view on every request?
I found an article long time ago, but i can't seem to find it now, basically the author, (which i forgot) override the showing of output. this method of output will access your views regarding the given controller/method and will try to search in your views directory automatically.
Use at your own risk
Application/core/MY_COntroller.php
----------------------------------------------------------------------------
class MY_Controller Extends CI_Controller
{
protected $layout_view = 'layouts/application'; // default
protected $content_view =''; //data
protected $view_data = array(); //data to be passed
public function __construct()
{
parent::__construct();
}
public function _output($output)
{
if($this->content_view !== FALSE && empty($this->content_view)) $this->content_view = $this->router->class . '/' . $this->router->method;
$yield = file_exists(APPPATH . 'views/' . $this->content_view . EXT) ? $this->load->view($this->content_view, $this->view_data, TRUE) : FALSE ;
if($this->layout_view)
{
$html = $this->load->view($this->layout_view, array('yield' => $yield), TRUE);
echo $html;
}
}
}
Application/views/layouts/layout.php
----------------------------------------------------------------------------
<html>
<head>
<title>master layout</title>
</head>
<body>
<!-- this variable yeild is important-->
<div><?=$yield;?></div>
</body>
</html>
This is what i use to create my template. Basically you need a directory structure as follows.
+Views
|+layouts
||-layout.php
the layout.php will serve as your master template
How to use?
extend the controller
class User Extends MY_Controller
{
public function create_user()
{
//code here
}
public function delete_user()
{
//use a different master template
$this->layout_view = 'second_master_layout';
}
public function show_user()
{
//pass the data to the view page
$this->view_data['users'] = $users_from_db;
}
}
Just create directory in your views and name it with the controller name i.e user then inside it add a file you named your method i.e create_user
So now your Directory structure would be
+Views
| +layouts
| |-layout.php
| |-second_master_layout.php
| +user
| |-create_user.php
Just Edit the code to give you a dynamic header or footer
Here is the simple example which i always do with my CI project.
Pass the body part as a $main variable on controller's function
function test(){
$data['main']='pages/about_us'; // this is the view file which you want to load
$data['something']='some data';// your other data which you may need on view
$this->load->view('index',$data);
}
now on the view load the $main variable
<html lang="en">
<head>
</head>
<body>
<div id="container">
<?php $this->load->view('includes/header');?>
<div id="body">
<?$this->load->view($main);?>
</div>
<?php $this->load->view('includes/footer');?>
</div>
</body>
</html>
In this way you can always use index.php for your all the functions just value of $main will be different.
Happy codeing
Using MY_Controller:
class MY_Controller extends CI_Controller {
public $template_dir;
public $header;
public $footer;
public function __construct() {
parent::__construct();
$template_dir = 'templates'; // your template directory
$header = 'header';
$footer = 'footer';
$this->template_dir = $template_dir;
$this->header = $header;
$this->footer = $footer;
}
function load_views ($main, $data = [], $include_temps = true) {
if ($include_temps = true) {
$this->load->view('$this->template_dir.'/'.$this->header);
$this->load->view($main);
$this->load->view('$this->template_dir.'/'.$this->footer);
} else {
$this->load->view($main);
}
}
}
Then load it like: $this->load_views('login_view', $data);
You can do with library.
Create a new library file called template.php and write a function called load_template. In that function, use above code.
public function load_template($view_file_name,$data_array=array()) {
$ci = &get_instatnce();
$ci->load->view("header");
$ci->load->view($view_file_name,$data_array);
$ci->> load->view("footer");
}
You have to load this library in autoload file in config folder. so you don't want to load in all controller.
You can call
$this->template->load_template("index",$data_array);
If you want to pass date to view file, then you can send via $data_array
There is an article on this topic on ellislab forums. Please take a look. It may help you.
http://ellislab.com/forums/viewthread/86991/
Alternative way: Load your header and footer views inside the concern body view file. This way you can have batter control over files you want to include in case you have multiple headers and footer files for different purposes. Sample code shown below.
<html lang="en">
<head>
</head>
<body>
<div id="container">
<?php $this->load->view('header');?>
<div id="body">
body
</div>
<?php $this->load->view('footer');?>
</div>
</body>
</html>
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.
This is more of a style question. I have a template file header.php in which I define a PrintHeader() function.
Callers of this function can specify, via global variables, the title of the page and any Javascript scripts to include when printing the header (because surely not every page will have the same title or want to include the same scripts). I chose to use global variables rather than function arguments because the latter would require the interface to change when adding new arguments.
Is this considered "good" style, and is there a "better" way to do what I'm trying to do?
header.php (simplified)
<?php
function PrintHeader()
{
global $pageTitle, $scripts; // Set by the caller of this function
echo <<<HEADER
<html>
<head>
<title>$pageTitle</title>
HEADER;
if( !empty($scripts) )
{
foreach($scripts as $script)
{
echo " <script type=\"text/javascript\" src=\"$script.js\"></script>\n";
}
}
echo " </head>\n";
}
?>
index.php (simplified)
<?php
$pageTitle = 'Welcome';
$scripts = array('script1', 'script2');
require('header.php');
PrintHeader();
// Print the rest of the page
?>
is there a "better" way to do what I'm trying to do?
sure.
I see no point in defining and calling a function at all. as well as in using heredoc.
header.php (dramatically simplified):
<html>
<head>
<title><?=$pageTitle?></title>
<? if( !empty($scripts) ): ?>
<? foreach($scripts as $script): ?>
<script type="text/javascript" src="<?=$script?>.js"></script>
<? endforeach ?>
<? endif ?>
</head>
index.php:
<?php
$pageTitle = 'Welcome';
$scripts = array('script1', 'script2');
require('header.php');
?>
but still it's not the best way, as it seems you're not using a template where it most valuable - to output page contents itself.
So, I'd make it in three parts:
links.php (simplified):
<?
//include our settings, connect to database etc.
include dirname($_SERVER['DOCUMENT_ROOT']).'/cfg/settings.php';
//getting required data
$DATA = getdata("SELECT * FROM links");
$pagetitle = "Links to friend sites";
//etc
//and then call a template:
$tpl = "links.tpl.php";
include "main.tpl.php";
?>
where main.tpl.php is your main site template, including common parts, like header, footer, menu etc:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My site. <?=$pagetitle?></title>
</head>
<body>
<div id="page">
<? include $tpl ?>
</div>
</body>
</html>
and finally links.tpl.php is the actual page template:
<h2><?=$pagetitle?></h2>
<ul>
<? foreach($DATA as $row): ?>
<li><?=$row['name']?></li>
<? endforeach ?>
<ul>
notice native HTML syntax, which is highlighted, readable and centralized in one place instead of being split between numerous functions and files
The point is in having separate template for the every PHP page as well as main site template for them all. With such setup you'll get a lot of advantages such as custom error pages, multiple representations of the same data (say, HTML, JSON or XML) by switching only templates without changing the code and many more
The use of global variables is certainly not advisable, and I question the necessity of using heredoc as you have - not that there is anything inherently wrong with heredoc, just that you seem to have rather arbitrarily utilized it in this sample template.
It is not elegant to use a return-value of a function as the output of each template - this defeats one of the purposes of templates which is re-usability.
Take a look at smarty, if not to directly use it (after all, why re-invent the wheel), at least to get an idea of how a rendering class is used to shuttle in the variables that a template needs without resorting to messy globals.
Here's a very quick overview of a way to do templating:
You have a template class that you can assign data to and then render a template.
Template.php:
class Template
{
protected $data = array();
public function assign($key, $value)
{
$this->data[$key] = $value;
}
public function render($file)
{
extract($this->data);
require $file;
}
}
You then have your template, header.php:
<html>
<head>
<title><?php echo $pageTitle; ?></title>
....
In index.php, you then use the template class to assign data and render your template.
$tpl = new Template;
$tpl->assign('pageTitle', 'My page title!');
$tpl->render('header.php');
This is just a simple example to demonstrate the idea, and could give you a good starting point.
While "better" may be in the eye of the beholder, I would suggest having some sort of functions that set the page bits rather than exposing raw variables. For instance, instead of doing $pageTitle = 'Welcome'; you could have set_page_title('Welcome');.
For JavaScript you could have a function that adds to the current script set -- rather than possibly replacing it all -- such as add_javascript($code);. This will allow a developer to set all of these without having to keep track of what the variable name was, and also without needing to global it as well if they want to set it from within a function.
This is an alternative using output buffering.
p/example_page/index.php is one of your pages:
<?php
ob_start() ?>
<h1>Example</h1>
<p>This is the page content</p>
<?php $main = ob_get_clean();
ob_start() ?>
<script defer src="js/example_page/example.js"></script>
<?php $script = ob_get_clean();
$title = 'Example page';
include 'templates/base.php';
templates/base.php is your reusable layout:
<!doctype html>
<html>
<head>
<script defer src="js/main.js"></script>
<?php echo $script ?>
<title><?php echo $title ?> - Example website</title>
</head>
<body>
<header>
<nav aria-label="Main menu"></nav>
</header>
<main><?php
echo $main;
?></main>
<footer>Example footer</footer>
</body>
</html>
Global variables are generally considered bad, and should be avoided if possible.
Rather than listing every variable in the interface, as you said things could change, pass a single array to the PrintHeader() functions:
<?php
function PrintHeader($opts=array()) {
if(!isset($opts['title'])) $opts['title'] = 'Default Title';
echo <<<HEADER
<html>
<head>
<title>$opts['title']</title>
HEADER;
if(!empty($opts['scripts'])) {
foreach($opts['scripts'] as $script) {
echo " <script type=\"text/javascript\" src=\"$script.js\"></script>\n";
}
}
echo " </head>\n";
}
$opts = array('title'=>'Welcome',
'scripts'=>array('script1', 'script2'));
require('header.php');
PrintHeader($opts);
?>
This way, you can add new capabilities in the function without breaking old code.
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.
thank you for viewing.
My website includes the same header and footer for each page using PHP.
I wanted a style sheet that only applied specifically for a certain page, so put the style in using the appropriate tag.
...<body><style type="text/css"> /* what ever */ </style></body>...
The style sheet is processed correctly in all browsers I tested, however it is not validated correctly by W3C because it's located inside the body tag instead of the head.
My question is:
If I can't put the style sheet in the body tag, what is the best way to include it? I can reference the style sheet in the PHP header, but I'd rather not have another HTTP Request for such a small file. How would you do it? What is the least sloppy way to do it? Although the style tag shouldn't be in <body>, it is still processed correctly by browsers.
What about giving the body an id and then just including the page specific files in the general CSS but with the styles prefixed by the id selector? Something like this:
On page
<body id="pageSpecificId">......</body>
In CSS file:
#pageSpecificId p {
... paragraph specific styles ...
}
#pageSpecificId li {
... list item specific styles ...
}
The best way would be to use a MVC framework that buffers your view file, and allow tag to be dynamically added to the head before output.
Here is a ultra simple way of doing it:
index.php:
<?php
class Page {
private static $head = array();
private static $content = '';
static function add_head($tag) {
self::$head[] = $tag;
}
static function render_head() {
foreach (self::$head as $tag) echo $tag;
}
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;
}
}
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>