I have a class with this function:
public function loadTemplate($template)
{
return require "templates/$template.php";
}
It's fine, but I have a problem with it.
Let's say I have this code:
require('class.php');
$class = new ClassName; // ClassName is the class which contains the function loadTemplate()
$name = 'Jerry';
$class->loadTemplate('myname');
And "myname.php" is:
<?php
echo "My name is $name.";
In this case I get an error because "myname.php" is actually included in the file of the class and so $name is undefined.
How do I overcome this problem?
Not discussing whether that's the best design, here's how you can do it.
public function loadTemplate($template, $vars)
{
extract($vars);
ob_start();
require "templates/$template.php";
$viewContent = ob_get_contents();
ob_get_clean();
return $viewContent;
}
Pass your variables as an associative array:
$vars = ['name' => 'Jerry']
And then output it:
echo $class->loadTemplate('myname', $vars);
What this does is, it creates variables from the array and loads the view, but with the "ob_" functions we are capturing the output buffer and then echoing it if we want.
You could probably get away without using ob_ just by echoing the require method as you were trying. I'd say give it a go and use what you prefer.
Related
I'm currently doing a beginner course in coding, mainly focusing on PHP and in one exercise we're changing our code from including a template through normal namespacing to instead including it via a function so that we can use output buffering. To make it work we are using extract() and although I understand how extract works, I struggle to see why we need to use it to make include work. Before running it via the function, we didn't need to send in or extract new variables. Is someone able to explain the reasons behind this?
Here's what the function looks like:
const TEMPLATE_EXTENSION = '.phtml';
const TEMPLATE_FOLDER = 'templates/';
const TEMPLATE_PREFIX = 'cart_view_';
function display($template, $variables, $extension = TEMPLATE_EXTENSION) {
extract($variables);
ob_start();
include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
return ob_get_clean();
}
Here's how we call it:
<?php echo display('user', ['users' => $users, 'cart' => $cart]); ?>
<?php echo display('item', ['new_item' => $new_item]); ?>
<?php echo display('items', ['cart' => $cart]); ?>
And here's what's in the templates we're including:
<h2>New Item</h2>
<p><?php printf($new_item['name']);?> is $<?php printf($new_item['price']);?></p>
<h2>User: <?php printf($cart['user']); ?></h2>
<p>ID: <?php printf($users[$cart['user']]['id']); ?></p>
<p>Email: <?php printf($users[$cart['user']]['email']); ?></p>
<h2>Cart</h2>
<?php foreach ($cart['items'] as $item) {
printf("<p>%s is $%d</p>\n", $item['name'], $item['price']);
} ?>
The variables are definied in another file which is already included in the index.
Previously before using the function to buffer, all we needed was this:
<?php include 'templates/cart_view_user.phtml'; ?>
<?php include 'templates/cart_view_item.phtml'; ?>
<?php include 'templates/cart_view_items.phtml'; ?>
Functions do have their own variable scope. So when you created the display() function, it doesn't see what variables are available outside of it. See more on Variable Scopes in PHP. You are using extract() in order to convert an array into variables in the scope where you are calling it from eg: in the function.
Your first solution was probably something like this (I'm making up the variables):
$users = [];
$cart = [];
// you are including the template in the same scope as the variables are defined, aka it will "see"/have access to those variables
include 'templates/cart_view_user.phtml';
Now you have refactored your code and moved the including logic in a function. This function has its own local scope.
$users = [];
$cart = [];
function display($template, $variables, $extension = TEMPLATE_EXTENSION) {
// you don't have access to $users and $cart here as those are defined in the global scope
extract($variables); // <-- after this call you will have new variables created in the local function scope based on your $variables array
ob_start();
include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
return ob_get_clean();
}
So now you would have two options if you know what variables you want to make available inside the function you could use the global keyword, but I would discourage using it, it could lead to weird bugs when you don't understand why your variables being changed (ps later you will move onto using classes and you won't have the headache of global variables).
// you can drop $variables from the function signature
function display($template, $extension = TEMPLATE_EXTENSION) {
global $users, $cart, $new_item;
// no extract needed, but please try not using global, it can lead to weird bugs
ob_start();
include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
return ob_get_clean();
}
Or you can use the extract to create variables in the function's scope so when you include the files they will have access to those variables. On thing to note here that extract will use the array keys as the variable names it is creating. That's why you are passing in display('user', ['users' => $users, 'cart' => $cart]); after this call, extract will create a $users and a $cart variable inside the function call. If you would call it with different array keys, like: display('user', ['u' => $users, 'c' => $cart]); the included file would complain that it can't find the variables $users and $cart.
I hope this helped, feel free to ask more if I wasn't clear anywhere.
From the php manual
Include_once may help avoid problems such as function redefinitions, variable value reassignments, etc.
Ok, so include_once solves issues with function redefinitions, variable value reassignments, etc. but why are they an issue in the first place ?
I'm trying to understand what kind of risks are involved in redefining functions or reassigning variable values except for a decline in performance due to additional input/output and processing ?
Is it because php parser gets confused which version of function to load/use or is the original version of the function lost once redefined? What else and what about variable reassignments?
I do understand where to use include vs include_once.
Imagine the following include file, hello.php:
function hello()
{
return 'Hello World';
}
$a = 0;
Now imagine the following file, index.php:
include 'hello.php';
$a = 1;
hello();
include 'hello.php';
hello();
echo $a; // $a = 0, not 1
Your code would now have a fatal error, since the function has been defined twice. Using include_once would avert this, since it would only include hello.php once. Also, to do with variable value reassignment, $a (should the code compile) would be reset to 0.
From the comments, please consider this a side answer - If you're looking for something where resetting a set of variables many times was required, I'd look to use a class for this with a method like Reset, you can even make it static if you didn't want to have to instantiate it, like so:
public class MyVariables
{
public static $MyVariable = "Hello";
public static $AnotherVariable = 5;
public static function Reset()
{
self::$MyVariable = "Hello";
self::$AnotherVariable = 5;
}
}
Usage like:
MyVariables::$MyVariable = "Goodbye";
MyVariables::Reset();
echo MyVariables::$MyVariable; // Hello
Let's say you have an include script vars.inc.php:
<?php
$firstname = 'Mike';
$lastname = 'Smith';
?>
And then you have a script script.php:
<?php
echo "$firstname $lastname"; // no output
include('vars.inc.php');
echo "$firstname $lastname"; // Mike Smith
$firstname = "Tim";
$lastname = "Young";
echo "$firstname $lastname"; // Tim Young
include('vars.inc.php');
echo "$firstname $lastname"; // Mike Smith
?>
What happens is that if you modify your vars in code exection and then you include once again the file defining them, you are changing their content. include_once will ensure that this will never happens throwing an error.
It will stop you loading pages more than once. Typically you'll use this at the top of your pages to bring in your init, function, class files etc.
Especially useful if you are loading pages within pages dynamically.
newbie in PHP here, sorry for troubling you.
I want to ask something, if I want to include a php page, can I use parameter to define the page which I'll be calling?
Let's say I have to include a title part in my template page. Every page has different title which will be represented as an image. So,
is it possible for me to call something <?php #include('title.php',<image title>); ?> inside my template.php?
so the include will return title page with specific image to represent the title.
thank you guys.
An included page will see all the variables for the current scope.
$title = 'image title';
include('title.php');
Then in your title.php file that variable is there.
echo '<h1>'.$title.'</h1>';
It's recommended to check if the variable isset() before using it. Like this.
if(isset($title))
{
echo '<h1>'.$title.'</h1>';
}
else
{
// handle an error
}
EDIT:
Alternatively, if you want to use a function call approach. It's best to make the function specific to activity being performed by the included file.
function do_title($title)
{
include('title.php'); // note: $title will be a local variable
}
Not sure if this is what you're looking for, but you can create a function to include the file and pass a variable.
function includeFile($file, $param) {
echo $param;
include_once($file);
}
includeFile('title.php', "title");
In your included file, you could do this:
<?php
return function($title) {
do_title_things($title);
do_other_things();
};
function do_title_things($title) {
// ...
}
function do_other_things() {
// ...
}
Then, you could pass the parameter as such:
$callback = include('myfile.php');
$callback('new title');
Another more commonly used pattern is to make a new scope for variables to be passed in:
function include_with_vars($file, $params) {
extract($params);
include($file);
}
include_with_vars('myfile.php', array(
'title' => 'my title'
));
The included page will already have access to those variables defined prior to the include. If you require include specific variables, I suggest defining those variables on the page to be included
I have a function that needs to include a file, however this functions is used in a place from 200 to 300 times, this is obviously causing efficiency issues, is there a way to optimize this inside the function? (i know there are many ways in which i can fix this but it will cause too much impact in the whole application)
I will just put a little example, this is not the whole function.
function getString(arrayName, strValue){
inclue('stringArrays.php');
return $$arrayName[strValue];
}
I tried using include_once, but that doesn't do the job either.
Thanks in advance.
You could use a static variable in the function to hold your values:
function getString($arrayName, $strValue){
static $string_arrays = array();
if (empty($string_arrays)) {
include('stringArrays.php');
$string_arrays = array_diff_key(get_defined_vars(), array(
'string_arrays' => true,
'arrayName' => true,
'strValue' => true,
));
}
return $string_arrays[$arrayName][$strValue];
}
Should only include the file once.
You could always add another parameter, perhaps a boolean, to tell the function whether or not to include it.
function getString(arrayName, strValue, includeFile)
{
if (includeFile)
{
inclue('stringArrays.php');
}
return $$arrayName[strValue];
}
You can try globalizing what's in stringArrays.php so you can check to see if that global variable is already set before including the file. Hard to tell without seeing what structure is in stringArrays.php.
If your function does nothing more than include a file you should be first evaluating whether that function should be called in the first place or make the function determine if an include is required. Basically don't blindly include a file if you truly don't need it included. include_once will incur a performance hit.
Install APC, eAccelerator, XCache or any other code accelerator so PHP doesn't need to retrieve the include file from disk every time it's called. Code accelerators save the file in shared memory. That will improve performance significantly.
Is there anything preventing you from wrapping your current "bunch" of arrays in an array, then passing that wrapper array into the function by reference? You can then do a single require/include outside of the function. Alternatively, you can wrap both the set of arrays and the function inside an object, again bringing you down to a single require/include.
If stringArrays.php is simply a collection of arrays, what about creating a stringHandler singleton that includes stringArrays.php within the constructor and maps the each array to a class property, then a simple method to get whichever you want from that class. Then your getString() function simply references a getter method in the stringHandler.
stringArrays.php
<?php
$abc = array('def' => 'Hello',
'ghi' => ' '
);
$jkl = array('mno' => 'World',
'pqr' => '.'
);
?>
stringHandler.php
<?php
class stringHandler
{
private static $instance;
private function __construct()
{
include('stringArrays.php');
foreach(get_defined_vars() as $key => $val) {
$this->{$key} = $val;
}
}
public static function singleton()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
public function getStringFromArray($arrayName, $strValue)
{
return $this->{$arrayName}[$strValue];
}
}
function getString($arrayName, $strValue){
return stringHandler::singleton()->getStringFromArray($arrayName, $strValue);
}
echo getString('abc','def');
echo getString('abc','ghi');
echo getString('jkl','mno');
?>
Kludgy, but shouldn't be a big performance overhead.
I need some help with this please
I can't get a handle on it.
The problem is that I want to call a class method, in this case with static methods with an ajax call.
I have put the helper class in the same folder as the script that is called by ajax for easy referencing and try to include it.
Could it be that my refencing is wrong?
If I make a testclass in the file that is called by ajax I can get a response.
class test {
public function testit() {
return "testit";
}
}
$t=new test;
$check= $t->testit();
switch($action) {
case "someaction":
$data = array();
$file='input_helper.php';
include_once $file;
$check= input_helper::ip_address();
header('Content-type: application/json');
$output = array(
"check" => $check,
"user" => $data
);
echo json_encode($output);
exit(0); // Stop script.
break;
//...
EDIT FOR MORE CLARIFICATION
The action is set as a post variable in the ajax function
The ajax url points to a script that takes some action based on the posted variables
thanks, Richard
<?php
include_once 'your/class/path/helper_class.php';
.
.
at the top of your PHP page should do it. it really has nothing to do with AJAX. If your PHP file is in fact being hit on the callback, then that should work properly.
Optionally, to test that your path is correct, if you do:
<?php
require 'your/class/path/helper_class.php';
.
.
If the path is not correct PHP will throw a fatal E_ERROR level error.