At the very top of my page/site, before <!doctype html> etc., I load my classes with spl_autoload_register().
One of the classes is site, and inside this class I have a static function:
<?php
/**
* A fast and easy way to include content to a page...
* "dir_pages_contents" is a defined constant with the path to the content files
*/
public static function include_content($content_name){
if(file_exists(dir_pages_contents.$content_name.'.cont.php')){
include dir_pages_contents.$content_name.'.cont.php';
} else {
/* echo an error message */
}
}
?>
I was hoping to do something like this i practice:
create a new document with some content and save it with the extension .cont.php into a folder specified for page contents.
Then; On the page where I want this content to be displayed - I do:
site::include_content('test_doc');
This almost works; The document, or content, is included and displayed.
But it seems like it is included where the class is - at the very top where the class is - because PHP-variables set outside of this document is not available within the document at all.
Here's a illustration of the setup:
test_doc.cont.php
<?=$hello?>
index.php
<!-- PHP-classes are included here -->
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
</head>
<body>
<?php $hello = 'Im outside...'; ?>
<!-- this includes the document, but the variable $hello is not -->
<?=site::include_content('test_doc')?>
<!-- this works perfectly -->
<?php include dir_pages_contents.'test_doc.cont.php; ?>
</body>
</html>
The inclusion of a separate file, or document, is done immediately when the include-statement is read by the script i guess? but not displayed until futher down the script where the function was called?
Any suggestion for another way to accomplish this?
I'm not looking for any MVC or other PHP-framework.
EDIT:
user1612290 pointed out to me that the include-statement within my include_content-function only uses the variable scope of my function - meaning that any variables decleared outside my include_content is not passed to the include directive unless I make them global.
It was also suggested that I could pass an array of named keys=>$variables into my function and use extract() on them to make them available.
This is what I came up with:
- added $arr
public static function include_content($content_name,$arr){
if(file_exists(dir_pages_contents.$content_name.'.cont.php')){
if (is_array($arr)){extract($arr);}
include dir_pages_contents.$content_name.'.cont.php';
} else {
/* echo an error message */
}
}
Now I'm able to do this:
<?=site::include_content('test_doc',array('hello'=>$hello))?>
Allthoug I'm not happy with this solution, I now have the variables accessable within the included document - so I'm closer than I was an hour ago :)
Any easier way?
Related
I'm looking for advice on the best practice for separating site content up into logical blocks. I want a header and footer that are constant throughout the site, so that if I have several pages of different content, they will all look as below — changes made to the header and footer then update automatically without me having to change each individual page.
<?php
include 'header.php';
?>
<body>
<p>page content here</p>
</body>
<?
include 'footer.php';
?>
The header.php would contain the opening <html>, <head> and static content, and the footer.php would contain any extra static content and the closing </html> tag. So, my question is: Is this a good approach? I'm worried that spreading the <html> tags across multiple files is bad practice. If so, what is the right way to approach this kind of design?
Nope, your approach is wrong.
Here are main faults in your design:
You're assuming that header.php would be called on the every page call. That's wrong.
You're assuming that header.php will always be static. That's wrong.
You forgot to create a template for the page itself.
The main rule everyone have to learn by heart:
Not a single character has to be sent into browser, until all data gets ready.
Why?
it's 2011 today. AJAX era. What if your code will have to send JSONed data instead of whole HTML page?
there is a thing called HTTP header. Sometimes we have to send them. And it's gets impossible if you already have your ornate HTML header sent.
it's for just 4-page site. Okay. Imagine you've got lucky and got a request for another 4-page site. You will have to change only templates and don't touch engine files. That's really great benefit.
Imagine you're going to make a custom <title> tag for your pages, based on the page content. Isn't it extremely common thing? But you can't make it without using templates.
So, you have to have one common site template containing header and footer and also dedicated templates for the every php script.
An example layout is going to be like this:
.1. page itself.
it outputs nothing but only gather required data and calls a template:
<?php
//include our settings, connect to database etc.
include dirname($_SERVER['DOCUMENT_ROOT']).'/cfg/settings.php';
//getting required data
$DATA=dbgetarr("SELECT * FROM links");
$pagetitle = "Links to friend sites";
//etc
//and then call a template:
$tpl = "links.tpl.php";
include "template.php";
?>
.2. template.php which is your main site template,
consists of your header and footer:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My site. <?=$pagetitle?></title>
</head>
<body>
<div id="page">
<?php include $tpl ?>
</div>
</body>
</html>
.3. and finally links.tpl.php is the actual page template:
<h2><?=$pagetitle?></h2>
<ul>
<?php foreach($DATA as $row): ?>
<li><?=$row['name']?></li>
<?php endforeach ?>
<ul>
easy, clean and maintainable.
In building off of Your Common Sense's answer, there's not a good reason to have 2 files for every page. You can easily combine your template (YCS called this .tpl.php) and your actual page into one file.
First, start off with a class that you can expand as your template needs expand:
<?php
#lib/PageTemplate.php
class PageTemplate {
public $PageTitle;
public $ContentHead;
public $ContentBody;
}
Then, make your layout:
<?php
# layout.php
require_once('lib/PageTemplate.php');
?>
<!DOCTYPE HTML>
<html>
<head>
<title><?php if(isset($TPL->PageTitle)) { echo $TPL->PageTitle; } ?></title>
<?php if(isset($TPL->ContentHead)) { include $TPL->ContentHead; } ?>
</head>
<body>
<div id="content">
<?php if(isset($TPL->ContentBody)) { include $TPL->ContentBody; } ?>
</div>
</body>
</html>
And finally, add your page with the body content:
<?php
#Hello.php
require_once('lib/PageTemplate.php');
# trick to execute 1st time, but not 2nd so you don't have an inf loop
if (!isset($TPL)) {
$TPL = new PageTemplate();
$TPL->PageTitle = "My Title";
$TPL->ContentBody = __FILE__;
include "layout.php";
exit;
}
?>
<p><?php echo "Hello!"; ?></p>
This is a basic approach but, yeah, it does work :) I sure would bother with a lot of templating and OOP but you are definitely on the right path
As i can't comment anymore, then i will answer here ;) If he need a custom title then he needs some more advanced functions. So, as i told, this is a basic approach. But in the end, if he really have a static header/footer, and really use them everywhere, well, yes, this is a good way to go.
So ofc you could bother with some advanced headers with parameters you could feed on each page. You could go on a whole MVC stuff. In the end just tell him to use a pre-made framework and stop bothering. How could he learn if you don't let him do some trial and error ?
index.php -- includes header, footer, and content based on REQUEST variable.
header.php -- header content
footer.php -- footer content
content1.php, content2.php, etc.
index.php:
<?php
include ('header.php');
// VERY IMPORTANT - do not use the GET variable directly like this
// make sure to filter it through a white-list
include(basename($_GET['page']).'.php');
include ('footer.php');
?>
if you want the URL to go www.domain.com/pagename where the page you're trying to load into index.php is "pagename", use HTACCESS and do some URL Rewriting: http://corz.org/serv/tricks/htaccess2.php
i have just learned about include() function in php, which enables to include whole document into another document. i was wondering how to do the same, if i would like to include not a whole document, but only a snippet of code, from one document into another one.
You can do it with 2 approach:
You can go with #blckbird idea and put your code in a new file and just include it.
You can create a file containing a method foo(), with your snip code. include the file and just call that foo().
Exmple:
// helper.php - contain your snip/reusable code
<?php
function startPage($title){
print '
<!DOCTYPE html>
<html>
<head>
<title>'.$title.'</title>
</head>
<body>';
}
function endPage(){
print '
</body>
</html>';'
}
?>
Now your main file include helper.php and call the method you want.
// main.php
<?php
include("helper.php");
startPage("My Title");
// do your stuff/coding here
endPage();
?>
hope it helps a bit.
ok i have an index.php as so:
<?php
require 'php/stdlib.php';
$site->page->render();
foreach($page as $var => $value) {
echo $var ." is ". $value." <br/>";
}
?>
the obj creation for site and page is in the stdlib file and is obviously working cuz
the -for each- loop prints out:
name is welcome
headers is inc/index_h.php
footers is inc/index_f.php
contents is inc/welcome.php
It show that the object is created.
I also did a var dump with proper results
here is site---page---render:
public function render_page(){
$this->page->render();
}
here is page---render:
public function render(){
include $this->headers;
include $this->contents;
include $this->footers;
}
however the result of the script is the following:
Undefined variable:
and also
Trying to get property of non-object:
both errors point to my $page object that i used in the include file for the page header:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><?php echo $page->name; ?></title>
<script src="/scripts/jquery.js"></script>
</head>
<body>
The errors actually print out in the html title tag not on the screen meaning i have to use View Source on my browser to see it
How do i get the $page object to be visible when using an include
Im usually pretty good about finding answers myself but this thing has me stumped for two days now.(I have learned alot about many other things while searching for answer tho so I guess not all is lost) If anyone could help me I would greatly appreciate it.
Probably should have added that the page and site object are instantiated in stdlib.php with the following
$site = new csite();
site_ini($site);
$page = new cpage("welcome");
$site->setPage($page);
When you use PHP's build-in include function, the contents of the included file are executed in the same scope as the call to include. Therefore, you really want to call $this in the included files, as their code is executed as-though it were actually written inside the render() method, and you will note that no $page variable was declared in that method.
On the other-hand, it may make more syntactic sense to set $page = $this prior to the first include.
I up-voted for you because, while the title does not suggest it, you are actually highlighting an important aspect of the PHP include and require functions: the way they pass scope.
For more information about this topic, have a close read of the PHP.net documentation on the function:
http://www.php.net/manual/en/function.include.php
Edit: To clarify why one example works, and another does not, it is because $page, from the include, has the same scope as your foreach, but is not declared inside the render.
Do not pull it in as a global, however. As syntactically you want to render the page you call render on, so: $this
So I'm basically trying to pass the <link> and <script> tags into my original header file from a modules view file (which is displayed in the body of the page). How can I pass variables that include these references?
Currently I have just put extra <head></head> tags into my module's view, but it just feels messy and dodgy to do so, as that means the head tags are used up the top of the page, and also mid-way down.
Edit: Didn't realise that stack overflow edited out my tags that are crucial to this question! Sorry guys!
It sounds like you are really in need of a Template setup for CodeIgniter. Here are links to some of my favorites:
http://williamsconcepts.com/ci/codeigniter/libraries/template/reference.html
http://philsturgeon.co.uk/demos/codeigniter-template/user_guide/
And my personal favorite for simplicity:
http://maestric.com/doc/php/codeigniter_template
EDIT:
Per #Sneaksta's question, here is how I add css scripts to my template:
In my master template I have this code:
<?php if (!empty($cssFiles)): ?>
<?php foreach ($cssFiles as $styleSheet): ?>
<link rel="stylesheet" media="screen" href="<?= base_url(); ?>styles/<?= $styleSheet; ?>" />
<?php endforeach; ?>
<?php endif; ?>
Then in my controllers that might need to load different CSS files per function I do this:
$cssFiles = array('style1.css', 'style2.css', 'style3.css');
$this->template->set('cssFiles', $cssFiles);
Sneaksta,
I think I understand what you are asking about, but I am not 100% sure because you don't have any code examples posted.
So I will give you an example of how you can have a "telescoping" View that allows you to modularly load different style tags inside the head tags.
As Damien Pirsy mentioned, Views are buffered, which means that CI makes a special output buffer and will concatenate a series of View objects together, and then output the final buffer content as a finished web page.
My example below is built on this sort of chain of thinking:
End User
(calls) --> Page Controller, which then:
(calls & passes params) --> Base View
(calls multiple fragment views) --> Fragment View +
--> Fragment View +
--> Fragment View = Final Cumulative Page --> (sent back as output ) --> End User
First make the "base View", which we will call "base.php" for reference sake:
<!doctype html>
<html>
<head>
<!-- Base View -->
<?php
//This "if" + "isset()" statement is important,
// because if you forget to pass the variable in the controller
// CI will throw an error.
// Also important: the name of variable ($style) MUST match the
// the name of the associative element in the controller! (See
// remarks below on handling this in the controller)
if(isset($style))
{
//Loading views is like a telescoping effect:
//A view may load other views into itself recursively
$this->load->view($style);
}
else
{
//This echo statement will show a comment in
// source code if an error occurred during loading a view
echo "<!-- Style not found -->");
}
?>
</head>
<body>
<!-- Page Content Here -->
</body>
</html>
Next you create the Style View (note: the following code fragment would be in a separate file all by itself) which we will call "style1.php", and must located with your other views in order for CI to find it, e.g. inside the "application/views" folder. This lets you swap out an inline style block declared in the header by just changing which style view is loaded:
<style type="text/css">
/*Style 1:*/
/*Just do something simple and obvious, i.e. turn text red*/
body { color: red; }
</style>
Next you create the alternate Style View (note: the following code fragment would be in a separate file all by itself) which we will call "style2.php", and must located with your other views in order for CI to find it, e.g. inside the "application/views" folder. This lets you swap out an inline style block declared in the header by just changing which style view is loaded:
<style type="text/css">
/*Style 2:*/
/*Just do something simple and obvious, i.e. turn text blue*/
body { color: blue; }
</style>
Now inside of our controller "example.php" we tell base.php to load the style1.php file into its header. We do this by passing the file name as a parameter when we load the base.php view, by passing the file name as an element of an associative array, code igniter will parse that parameter array and create a variable with the same name as the associative element, and make that variable available to you inside the base.php view:
<?php
class Example extends CI_Controller
{
//Constructor
function __construct(){ parent::__construct(); }
//Base View request handler
function baseview()
{
//Note: Make an array, be certain to name the element
// the same as what will be expected inside base.php
$params = array("style" => "style1.php");
//Switching to load a different style is now easy
// just comment out the line above, and uncomment the line below:
//$params = array("style" => "style2.php");
//Pass the parameters array into the CI load view method:
$this->load->view("base.php", $params);
}
}
?>
The cumulative result should be the modular ability to switch the style tags inside the page header, by just specifying which "style view" to load (you could even build a model that retrieves which "style views" to load from a database table). Obviously this approach has certain processing overhead constraints inside of a web browser, as you are constructing actual inline HTML source code, rather than linking to a CSS file through a link tag. This means that the browser will not cache the css content for each page load, but will have to download it on each subsequent request.
I'm relatively new at PHP and came across a slight problem.
I have a php page called info.php and use an included php file called components.php to pull functions that have html code in them that is then used in page.php (and other pages.) I put the title in a variable called $title and then reference that in my components.php, but for some reason the components.php doesn't recognize that as a title. Here's the code, and thanks for all help ( I know my description of the problem is hard. Let me know if you need any more info)
page.php
<?php
include("components.php");
$title = "This is my page Title!";
echo writeHeader();
?>
components.php
<?php
function writeHeader()
{
echo <<<HED
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>
HED;
echo $title;
echo <<<HED
</title>
HED;
}
?>
Good solution: Pass $title as a parameter to he function.
Less good solution: Declare $title as
global $title
within the function to make it belong to the global scope.
Without declaring it as global, it is a fresh new variable.
To summarize the comments:
Never use global! Anything a function/method depends on, should be passed as a parameter.
Never access $_GLOBALS! Same reason.
Disable register_globals! No GET-parameters should be automatically injected into your application.
Enable the highest error level! Write your application in such a way, that no error or warning gets printed.
Unfortunately, my question regarding PHP newbie practices is closed. But it provides some helpful hints.
In case you like it, click the re-open link.
A variable has a scope. When you declare it, unless it's global you can't use it in another scope. To do that you have to pass it to the other scope using parameters
Your code should be like that :
page.php
<?php
include("components.php");
$title = "This is my page Title!";
echo writeHeader($title);
?>
components.php
<?php
function writeHeader($title)
{
echo <<<HED
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>
HED;
echo $title;
echo <<<HED
</title>
HED;
}
?>
In your code example, you call the echo function at two places :
In your page.php file : This one does nothing
In your component.php : This one prints the text
The best practice is to group the prints into a single component called the "view".
If your view is the file called component.php then you should remove the first echo.