Currently I am doing it like this:
function load_template($script, $args){
extract($args);
require __DIR__ . '/templates/' . $script;
}
In my controller code:
// if home page was requested
load_template('home.php', array(
'title' => get_title(),
'content' => get_content(),
...
));
The template is just a PHP script like
<!DOCTYPE html>
<html>
<head>
<title> <?php echo $title; ?> </titlee>
...
I was wondering if it's possible to lazy load these variables somehow, so I don't actually run get_title() or get_content() until the template specifically requests the variable.
Could this be possible, without creating a template parser thingy? I'd really like to stick with simple .php scripts and html as templates.
In short, what I'm asking is if it's possible to auto-assign a value to a variable only when it's first requested.
$var = func(); // this should not run
if($var){ // now the code above should run:)
echo $var; // <- the value that was just assigned (don't run func() again)
}
In my opinion, if you do not wish to change your template to extract the variables, you can create for example an array that would know which variables each template needs.
You may consider a function (let's name it caller) into which you pass all parameters and the template name. The caller could choose which variables are required. This idea is like a factory class in oop.
I think there is no other way, but...
While inserting a template and use unexistent variable, a warning will be shown. You can make PHP to throw exceptions in warnings and then in try ... catch block parse it. I think it's too complicated and not worth effort.
EDIT
The third idea is to create objects instead of arrays. The object would keep all your $args variable. In your template just change <?php echo $title; ?> to <?php echo $argument_object->getTitle(); ?>, and code the getTitle() method. The getTitle(), as a method not a function, would then be run only on request.
Related
I'm new to Object-Oriented PHP so I'm not entirely sure what my question really is, but here's the jam:
I created a WordPress plugin that adds a bunch of metaboxes on a certain custom post type created by another plugin. In my theme I need to get all the custom meta, but it's quite a lot that needs to be gotten, and usually with certain conditionals and so forth. So, I created a bunch of helper functions to simplify it. Problem is the names of all these functions are kind of a pain, so I put them inside a class in my plugin to simplify things, and my issue is that I'm having trouble combining them. Whether that's possible or not, hopefully you can help with...
So most of the helper functions (now class methods) look something like this:
public function metabox_wrapper( $code ) {
$info = get_post_meta( ID, 'blah', true);
if ( $info ) {
code( $info );
}
}
Then in my theme file I use:
global $Other_Plugins_Global // Created by the other plugin.
$class = new Class( $Other_Plugins_Global );
$class->metabox_wrapper( function( $info ) {
?>
<div>
<?php echo $info; ?>
</div>
<?php
});
The above is currently working - it displays the blah meta from the current post if the blah metadata exists. The other plugin's global variable contains the custom post type info (and therefore the meta), so I pass that through the class instance, use a method in the class to manage all the conditionals and other complicated stuff, and then use the method in the theme file, so that the conditional stuff is pre-arranged and able to be used in the same way elsewhere in the theme.
However, here's the rub: There are situations where I want something like
if (
Do this
if (
Also do this
)
)
But this (and everything like it that I've tried) does not work:
global $Other_Plugins_Global
$class = new Class( $Other_Plugins_Global );
$class->metabox_wrapper( function( $info ) {
?>
<div>
<?php echo $info; ?>
</div>
<?php
$class->other_metabox_wrapper( function( $stuff ) {
?>
<div>
<?php echo $stuff; ?>
</div>
<?php
});
});
So my question is... is there a way I can use these wrapper functions inside of each other? I've tried setting them as static and using :: in the theme, I've tried some fancy variable gymnastics that didn't work and would've been ugly af if it had, I almost raised a demon trying to call the global variable from inside the method... so at this point I'm not even sure what to Google.
Obviously for this particular example I could just use if(){}, but the conditional logic often has multiple conditions and other semi-complicated stuff, so it'd be helpful to keep that separate and able to be used in multiple places around the theme.
I will say that if this is a dumb question and you know of a completely different and more elegant way to accomplish what I'm describing, then I'm all ears.
Many thanks for the help.
The problem lies not in the realm of OOP, but rather in the realm of functions and function scopes. You see, when you create an anonymous function it has its own scope -- the scope of variables that are accessible. In most languages, anonymous function encloses the scope it was created in, creating a closure -- meaning it can access the copies of all variables from the parent scope. In PHP it is not the case, so in order to access $class variable from within the anonymous function you should use use: $class->metabox_wrapper(function($info) use ($class) {.
Another approach would be to bind the context to anonymous function with Closure::bind():
public function metabox_wrapper($code) {
$info = get_post_meta(ID, 'blah', true);
if ($info) {
$code = Closure::bind($code, $this);
$code($info);
}
}
Then you can use $this inside the anonymous function:
$class->metabox_wrapper(function($info) {
?>
<div>
<?php echo $info; ?>
</div>
<?php
$this->other_metabox_wrapper(function($stuff) {
?>
<div>
<?php echo $stuff; ?>
</div>
<?php
});
});
But to tell you the truth I agree with #CrisanLucian. Mixing the code with HTML in such a manner is not the best way to program.
First of all you have to read some Coding Standards articles
1. https://www.tutorialspoint.com/php/php_coding_standard.htm
Try to make separation between PHP code and HTML; Using Twig instead PHP make life much easy
https://aristath.github.io/blog/using-twig-in-wordpress
When you have a more help (hooks) functions better to group them into a class, or create a Helper dir structure and create a file for each scope. (e.g: Trim strings , Resize Images create different files)
After creating Helpers try to not put HTML inside; A helper suppose to return a value ( boolean ).
e.g showCampaign() => will return True;
HTML page/ TWIG page
{{ if(showCampaign()) }}
//HTML CODE IS HERE
{{ endif }}
When you have too many if/else statements is need refactor check the link above or another example: https://carlalexander.ca/mastering-php-conditionals/
happy codding!
It's really convenient how MVC patterns allow you to define view a and then load variables into it via the controller. For the sake of argument let us take CodeIgniter as an example:
Controller:
Class Example extends CI_controller(){
function show_page(){
$data = array('msg'=>'Hello World');
echo $this->load->view('hello',$data);
}
}
View (hello.php):
<h1><?php echo $msg; ?></h1>
I have taken over an old project written years ago where there are redundant html code everywhere. It has no pattern whatsoever just straight up poorly structured code.
I wanted to create a class that has a function that will fetch all HTML code from a file in one folder, feed variables to them and show the result. Like so:
Folder structure:
View_folder
- hello.php
Class
- view_class.php`
Main:
<?php
$data['msg'] = 'Hello World!';
echo $view_class->get_view('hello.php',$data);
?>
Is it possible to achieve this? Can someone give an example function on how to do this. Thanks.
Sure thing, that's what the frameworks are doing. Here's a really basic overview of how I'd expect that to work:
function view($template, $data){
extract($data); // this pulls all of the first-level array keys out as their own separate variables
ob_start(); // this turns on **output buffering** which is the method we'll use to "capture" the contents of the view (there are surely other ways)
require $template; // you should prepend some sort of fixed path to this where all of your templates will reside
echo ob_get_flush(); // turns off output buffering, returning the buffered content as a string which we then send to the browser.
}
This has probably been asked before, but I couldn't find the answer to my question with some preliminary searches, so here it is:
A very simple example of my current method of injecting PHP variables into HTML is as follows:
HTML (file.php):
<tag><?php echo $variable; ?></tag>
PHP:
$variable = "value";
ob_start();
include "file.php";
$results = ob_get_clean();
This achieves the correct result just fine, but it annoys me every time that I have to copy and paste those three lines to get variables injected into my HTML files. There's a chance I might want to change the details of this injection functionality at a later date, and it's currently scattered in a couple hundred locations throughout my code.
In a perfect world, I would move those three lines to a separate function that takes the file path as an input, and returns the "compiled" result. However, the function would then no longer have access to the calling scope, and I would have to pass the variables in as another input. The only thing I'm able to think of for doing that is:
function injectVariables($filePath, array $variables)
{
//Built-in PHP function, extracts array key => value pairs into variable $key = value pairs in the current scope
extract($variables);
ob_start();
include $filePath;
return ob_get_clean();
}
That certainly gets the job done, and it will be what I implement if there aren't any better solutions, but it seems less than ideal to me. It involves creating an array every time this runs, just to loop through it and extract all the variables, which just feels a bit wrong to me.
Does anyone know of any other methods that might work?
Not really sure what you are asking, but here is my answer
I don't know the structure of your code, but I hope you are using a MVC approach (or at least something which deals with classes) so that you can do the following:
In your main controller, you create a class variable like viewData which will be an array. And you put in it everything you want
$this->viewData['username'] = $username;
$this->viewData['myArray'] = array('foo' => 'bar');
$this->viewData['menuSubview'] = 'path_to_menu_subview.php';
Then, you create a render function.
public function render()
{
extract($this->viewData);
ob_start();
include("myfile.php");
return ob_get_clean();
}
And in myfile.php (with the HTML) you can simply do what you used so far
<div id="menu"><?php include($menuSubview);?></div>
<p><?=$username;?></p>
<p><?=$myArray['foo'];?></p>
The whole code can be something like this.
class Something {
protected $viewData;
protected $viewFile;
public function logic()
{
$this->userProfile();
echo $this->render();
}
public function userProfile()
{
$this->viewData['username'] = 'John The Ripper';
$this->viewFile = 'myFile.php';
}
public function render()
{
extract($this->viewData);
ob_start();
include($this->viewFile);
return ob_get_clean();
}
}
Here's a simplified class that stores data and allows for recursive rendering of files that all have access to the save data form the initial instance.
class View {
// view data
protected $_data = [];
/**
* Set view data
*/
public function __set( $key, $value )
{
$this->_data[ $key ] = $value;
}
/**
* Get view data
*/
public function __get( $key )
{
if( array_key_exists( $key, $this->_data ) )
{
return $this->_data[ $key ];
}
return null;
}
/**
* Render view file
*/
public function render( $file )
{
if( is_file( $file ) )
{
$view = $this;
ob_start();
include( $file );
return ob_get_clean();
}
return 'File not found: '.$file;
}
}
Just use the variable $view inside your included files to access the data in the class, or render() another partial file that can do the same thing, and so on.
// Bootstrap a View instance and add some data
$view = new View;
$view->dataOne = 'valueOne';
$view->dataTwo = 'valueTwo';
// Render main template
echo $view->render( 'template.php' );
Inside template.php
<header>
<?= $view->render( 'header.php' ) ?>
</header>
<h1><?= $view->dataOne ?></h1>
<p><?= $view->dataTwo ?></p>
Nothing is wrong with your injectVariables() function, it is in fact a good idea.
You shouldn't have performance impact anyway (If this is your main concern your are doing premature optimization !)
Your view (HTML) shouldn't know about the internal of your application. (You split responsability - this is a huge subject that I won't talk deep)
You know if something end-up into $variables it has been build/formatted or is revelent to be display for $filePath.
In complex system you may end up with a variable with a pdf generator, why would the HTML want that? The only purpose of the HTML is to DISPLAY HTML data.
Some variables from your logic will end-up in $variables almost every time such as session informations (Who is currently logged), for your case this is normal.
In the perfect world if you put a class into $variables it should be designed only for purpose of your HTML even if it is almost the same object as your logic.
As an exemple I designed a Session class in my logic. It does have method such as Logout() and EnsureIsAlive() as well as field such as FullName. FullName is going to be use by my HTML because I want to display it in the header of my page.
I should have a Session class for the HTML that only contains a FullName field because the HTML has only one job and is it to display data. Login() and EnsureIsAlive() are specific for the logic and aren't related at all with displaying data.
In reallity time is always a constraint and because you are writing all by yurself from scratch you may end-up just sending the logic Session class into $variables.
Design note : I'm a C# programmer and we always use class over array to pass parameters as a good pratice and it does affect the exemple about how to pass FullName to your HTML. In your case, instead of creating a class dedicated for the HTML you could also create a simple array :
$variables['Session'] = array('FullName' => $mySession->fullName).
That way you still avoid your HTML to have access to the unrelated method specific for your logic. I'm not sure if it is a good pratice in php...
Keep context variables clean
If you do call injectVariables() more than once (for different $PathFile) you may not want the same variables.
One injectionVariables() could be for a widget, another for a complete complete page.
Why would a TimeWidget need your totalInvoice variable?
You also have the ability to prepare multiples $variables at the same time because each injectVariables() will have his own parameters.
I'm not up-to-date but I know phpBB template system use (back near 2000) the exact same pattern as your injectVariables()).
You wrote that each of your page do call injectVariables(), you could also (but it may be complex) do it only in one file - like php frameworks.
Framework handle HTTP requests into one specific file
(eg. http://yoursite.com/hello/world or http://yoursite.com/login would call (internaly) http://yoursite.com/index.php?page=hello&subpage=world and http://yoursite.com?page=login).
Then this page (index.php in my exemple) would include a specific file (controller) according with his GET parameters ($_GET['page'] and $_GET['subpage']).
The controller job would be to fetch data, apply logic then fill $variables.
Then index.php will call injectVariables() with the releated HTML.
Hey guys, I am new to CodeIgniter and need some help. I have a controller that formats the content area of a post. The problem is that I also need to create a sidebar that contains dynamic groups, and a right column that contains recent posts. This isn't hard, the problem I'm running into is that I want the sidebar, and right column on every page, and I don't want to recode the same bits to get the data in every controller.
What would be the best way to do this without copy/paste?
There are a lot of ways to do this.
1) Templating: This is my preference for most cases (because my templates are complex), I render my view into a variable using something like:
$content = $this->load->view('myview', $page_data, true);
Then I load it into the template parser (fyi you could load it into another view too) like this:
$this->load->library('parser');
$data = array(
'page_title' => 'My Page Title',
'page_content' => $content,
'side_bar' => side_bar(), // function which generates your side bar
'right_col' => right_col() // function which generates your right column
);
$this->parser->parse('my_template', $data);
Then your template is like:
<html>
<head>
<title>{page_title}</title>
</head>
<body>
<div>{side_bar}</div>
<div>{page_content}</div>
<div>{right_col}</div>
</body>
</html>
2) Load another view in your view: (assumes you menu is a view not a controller) Something like this:
<?php $this->load->view('menu_view'); ?>
3) PHP Includes: exactly how you would do it in plain PHP (just include a url which points to a controller which returns a menu), Something like this:
<?php include("/common/sidebar"); ?>
Codeigniter will render that page and then include it.
4) AJAX.. i use this if the content in the "template" content is less important, like banners, suggested related item lists and such.
Use PHP to generate a static HTML page, such as side_bar.html...
Then you can include it on other pages.
You could look into HMVC. It's especially suited for "widget"-type areas like you are talking about.
Essentially what you will do is create two full MVC structures - one for your sidebar and right column, including a controller, a model(if required), and a partial view. Then, you can call this controller directly from the main view to pull the required content in to the page.
To actually call it from within a view, just place the following in the markup wherever you want the sidebar to appear:
<?php echo modules::run('module/sidebar/index'); ?>
The index isn't required, but I put it there to demonstrate that you can call different methods using modules::run(). You can also pass an unlimited number of parameters to modules::run().
In code igniter, there is an optional third parameter to $this->load->view that lets you return a rendered view as a string, which can in turn be used for assignment. What you can do is create a master template, that has all the common parts, as a very simplified example:
<html>
<head>
</head>
<body>
<?php echo $sidebar; ?>
<?php echo $content; ?>
<?php echo $right_column; ?>
</body>
</html>
Then you can create a private function in your controller to populate the dynamic content of your common parts, and combine them with your content and master template:
private function BuildTemplate($view, $data) {
// Generate sidebar content
$sidebar_data['...'] = 'blah blah';
$master_data['sidebar'] = $this->load->view('sidebar', $sidebar_data, true);
// Generate right column data
$right_data['...'] = 'blah blah';
$master_data['right_column'] = $this->load->view('right_column', $right_data, true);
// Now load your content
$master_data['content'] = $this->load->view($view, $data, true);
// Merge it into the master template and return it
return $this->load->view('master' $master_data, true);
}
Then in your appropriate controller method:
public function index() {
$data['...'] = 'blah';
echo $this->BuildTemplate('index', $data);
}
Which will pull everything together for you. You can optionally add extra arguments to BuildTemplate if you want to add things like page specific titles or scripts.
I'm not sure if your problem is in the view, or in the (dynamic) data to be shown in the (common parts of) that view.
If it's the later (as seems to suggest the phrase 'I don't want to recode the same bits to get the data in every controller'), then you have several options. For example.
Put the logic to get the 'common' data in some function outside the controller, as a helper or inside some model, and call it from your controllers.
Make your controllers inherit your own custom controller, that implements that data gathering function.
Refactor your two controllers into a single controller, with different functions for each scenario.
1-Create a custom library class in library folder with the below code
if (!defined('BASEPATH')) exit('No direct script access allowed');
class LoadView{
function __construct(){
$this->CI =& get_instance();
}
function load_view($page){
$this->CI->load->view('header');
$this->CI->load->view('sidebar');
$this->CI->load->view($page);
$this->CI->load->view('footer');
}
}
2-Now load this library in your controller like this
$this->load->library('loadview');
3-Now call the library method and simply insert your page name and you don't have to include header,sidebar and footer again and again as they will be dynamically included by your library.
$this->loadview->load_view('about.php');
I'm actually creating websites for fun and some of my friends told me that I could be more efficient if I could create the output of the page with a php class that would represent the whole page.
I was wondering how you people would do it.
Thanks
I am not a OO programmer .. but, as a proof of concept, and as per your question, you can do/try something like this.
class Page {
public $meta_title;
public $meta_keywords;
public $html_body;
public function displayPage() {
$page ='
<html>
<head>
<title>'.$this->meta_title.'</title>
<meta name="keywords" content="'.$this->meta_keywords.'" />
</head>
<body>
'.$this->html_body.'
</body>
</html>
';
echo $page;
}
}
Then you just use this Page class as ..
$page = new Page();
$page->meta_title ="Hello world!";
$page->meta_keywords = "some text keywords";
$page->body = '<h1>Contact Us </h1>
<p>you can contact us at blah ... blah .. etc.</p>
<address>Dummy Address </address>
';
$page->displayPage();
Please note that you can add so many things into to it like .. class variables (type array) to define stylesheets, javascripts files .. then you just loop over it to define these files dynamically for individual pages.
You can amend the display page function so that it accomodate left, right, or top navigation bar. Then you can also have variables like $show_right_bar, $show_left_bar to control which pages display which side navigation bar. So you can amend and extend to whatever your requirements are.
Alternatively, you can try some php frameworks, which are much more evolved solutions, but that really depends on your programming skills and your requirements.
Hope this helps.
I'd suggest looking into some PHP frameworks, such as Cake which is good for beginners, or Symfony or Zend Framework if you're more skilled. Those will streamline your PHP development a lot.
What you could do is have all the tags in the page stored as a multi-dimensional array. PHP is mushy enough to do this. As such, every tag would be stored in an element of the array.
Then, you can use control statements to print different parts of the page at different times depending on certain conditions.
It would kinda look like what you get when you convert an XML file to an array.
Here's what I'm thinking.
$page['html']['title'] = "Page's Title";
$page['body']['p'][1] = "1st Paragraph";
$page['body']['p'][2] = "2nd Paragraph";
And then you could use your own functions to print these out as HTML fields.
I would suggest using some of the MVC (Model, View, Controller) frameworks out there like KohanaPHP. It is essentially this. You're working in a strictly Object-Oriented environment. A simple page in Kohana, build entirely from a class would look like this:
class Home_Controller extends Controller
{
public function index()
{
echo "Hello World";
}
}
You would then access that page by visiting youur url, the class name, and the method name:
http://www.mysite.com/home/ (index() can be called after home/, but it's implicit)
When you start wanting to bring in database-activity, you'll start working with another Class called a Model. This will contain methods to interact with your database, like the following:
class Users_Model extends Model
{
public function count_users()
{
return $this->db->count_records('users');
}
}
Note here that I didn't write my own query. Kohana comes with an intuitive Query Builder.
That method would be called from within your Controller, the first class that we mentioned at the beginning of this solution. That would look like this:
class Home_Controller extends Controller
{
public function index()
{
$usersModel = new Users_Model;
$userCount = $usersModel->count_users();
echo "We have " . $userCount . " users!";
}
}
Eventually, you'll want more complicated layouts, which will involve HTML/CSS/Javascript. At this point, you would introduce the "Views," which are just presentation layers. Rather than calling echo or print from within the Controller, you would load up a view (an HTML page, essentially) and pass it some variables:
class Home_Controller extends Controller
{
public function index()
{
$myView = new View("index");
$usersModel = new Users_Model;
$userCount = $usersModel->count_users();
$myView->userCount = $userCount;
$myView->render(TRUE);
}
}
Which would load the following "View"
<p>We have <?php print $userCount; ?> users!</p>
That should be enough to get you started. Using the MVC-style is really clean, and very fun to work with.
So many ways to do this, indeed.
When I make a simple personal site, I typically have a banner at the top and a menu on one side. To prevent myself from having to copy'n'paste everything, my index.php checks a get variable, and based on that get variable loads the appropriate content in the middle. By way of example, my index.php. My menu actually loads the content with a function, but this is not necessary/recommended:
<?php
$page = strtolower($_GET['page']);
if(!isset($page) || $page === '')
{
$page="home";
}
switch($page)
{
case "loomis":
require_once("loomis.php");
break;
case "home":
default:
$page = "home";
require_once("home.php");
break;
}
include "menu.php";
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Nebulous - <?php getTitle($page) ?></TITLE>
<link href="style.css" rel="stylesheet" type="text/css" />
</HEAD>
<body>
<?php displayMenu() ?>
</body>
</html>
Classes are really only useful as representations of data or functionality, not really to render pages.
OOP can be useful in page rendering if you have something that is static throughout your pages. (e.g. Header, Footer)
$index = new Page();
$index->displayHeader();
// php/html per needs
$index->displayFooter();
This isn't all that intuitive but as the others said, the possibilities are vast. I would suggest picking up a book on the subject.
The best way to achieve your aim is to start with an html class. Its purpose is to write any HTML tags and indent them properly. It is not a simple task as I have hardly ever found one such html class on the web written properly. Hint: you will need a stack for managing the automatic closure of HTML tags and a working buffer for the current tag that rolls into a bigger holding buffer.
Using the services of the html class, you can build a webpage class that will create pages from settings. The trick here is to remain flexible and not hard-code too much inside your class. You need a setter for basic settings such as title, author, keywords etc. holding them into an array. Then webpage->header() should output the top of your page automatically. Then you should use functions such as webpage->heading($string,$size) to create a heading, webpage->paragraph($text), webpage->image() and so on. At the end you just need webpage->footer().
To make the webpage class worthwhile, make it create the css as well. For example if you want to use bootstrap, you can have the class include the css and style your code. Note that you can also make the webpage class create advanced objects such as webpage->breadcrumbs() or webspage->pagination()
I had exactly the same thought and could not find any modern OO PHP library to do what I wanted. I also wanted to use the Foundation CSS framework, so I wrote my own PHP library and have been using it for years. Very easy to make pages once you set up your base page and menus. Check it out on Packagist.org. I also wrote a PHP documentor library because I did not like any of the existing static generation packages out there with their complex setup. Take a look at PHPFUI.com for an example of the library in use. Run the HTML through an HTML validator if you want. Very clean because no HTML was touched by human hands in the construction of the site.