Why can't I pass variables into an included file in PHP? - php

I've had this problem before before with no real resolution. It's happening again so I'm hoping you experts can help.
I set a variable on my index.php based on the $_GET array. So I use this code:
$admin = isset($_GET['admin']) ? $_GET['admin'] : "dashboard";
Below that, I use a function to include my layout:
include_layout("admin_header.php");
which calls this function:
function include_layout($template="") {
include(SITE_ROOT.DS.'layouts'.DS.$template);
}
So far, so good, everything works. But if I try to echo the $admin variable from within the admin_header.php file, I get nothing. It's as if it's not set. I even test it by using echo $admin; right before I include the header file, and it outputs there, but it's not set from the admin_header.php file's perspective.
Why would this happen? Is it something to do with using a function to include, rather than including it directly? And if so, WHY would that matter?

Its because that variable is out of scope because you are including the file using a function. You either need to pass the variable to the function in another parameter or declare the variable as global within the function
either:
function include_layout($template="") {
global $admin;
include(SITE_ROOT.DS.'layouts'.DS.$template);
}
or:
function include_layout($template="",$admin="") {
include(SITE_ROOT.DS.'layouts'.DS.$template);
}
include_layout("admin_header.php",$admin);

It's not possible to access global variables unless you say so explicitly. You have to put global $admin in the function to be able to access the variable.
The reason is you include the file inside of a function, so the $admin variable lives in the global scope, while the included file lives in the functions scope.
function include_layout($template="") {
global $admin;
include(SITE_ROOT.DS.'layouts'.DS.$template);
}
Another possibility is to use the extract method.
function include_layout($template="", $variables) {
extract($variables, EXTR_SKIP);
include(SITE_ROOT.DS.'layouts'.DS.$template);
}
include_layout("test.php", array('admin' => $admin));

It's a variable scoping issue. $admin is not defined inside the function, include_layout(). You can adjust your code a few ways. One is to say "global $admin;" inside include_layout(). Another is to set the variable, $admin, inside of "admin_header.php" if, for instance, the variable is only needed in that one layout context.

It might matter because PHP doesn't give functions access to global variables unless you explicitly ask for it. Sanitizing your input would also be a good idea.

Related

PHP Variable Scope Issue

In my code, I use a public load_snippet function of a class when I need to include HTML or PHP snippets. (I do this instead of a direct include_once because the directory structure varies depending on certain variables).
I had some issues with variable scopes, so I've narrowed down the problem to this: let's say I define a variable within my page:
$variable = 'Hello World!";
Then, I need load in a snippet:
$APP->load_snippet("slider");
The snippet renders perfectly, except that PHP gives an undefined variable error if I try to reference $variable in the slider code. If I directly include the php file, it works as expected, so I don't understand why I'm having this problem, since this is the load_snippet function:
public function load_snippet($snippet){
if(file_exists("app/".$this->APP_TYPE."/snippets/".$snippet.".php")){
include "app/".$this->APP_TYPE."/snippets/".$snippet.".php";
}
else{
include 'common/txt/404.txt';
}
}
Any help you can give me is much appreciated.
The file is being included within the context of the load_snippet() function, and therefore has only those variables which exist within that function. One way to modify this is to make your function accept two variables: the filename and an array of values.
public function load_snippet($snippet, $content) {
if (is_array($content)) extract($content);
if (file_exists("app/".$this->APP_TYPE."/snippets/".$snippet.".php")) {
include "app/".$this->APP_TYPE."/snippets/".$snippet.".php";
} else {
include 'common/txt/404.txt';
}
}
Then
$arr = array('variable' => 'Hello world!');
load_snippet('slider', $arr);
I think include inside a function makes no sense to me... I think that you should put in function
global $variable;
Note that include will put the code inside the function(include will be replaced by code) as i know..
The way you are doing it is an ugly one, but you can use global $variable inside the snipped to refer to the variable. However if you include the snipped inside a function or a method, you'll have to make the variables in that function/method global as well
If you need $variable inside of the App::load_snippet() method, it would probably be best to pass it in:
public function load_snippet($snippet, $var='Hello world'){
if(file_exists("app/".$this->APP_TYPE."/snippets/".$snippet.".php")){
include "app/".$this->APP_TYPE."/snippets/".$snippet.".php";
}else{
include 'common/txt/404.txt';
}
}
//do something with $var
}
You can set a default for when $variable hasn't been set. No globals, no out of scope variables.
Instead you can use the constants like define('VARIALABLE','value'). which will be available to you anywhere in your file
You are including inside a class. Which means that the included file has the same variable scope as the line of code which includes it has. TO fix this all you need to do is put
global $variable;
Above the include.

Function scopes in regards with includes in PHP

I'm having a bit of trouble understanding includes and function scopes in PHP, and a bit of Googling hasn't provided successful results. But here is the problem:
This does not work:
function include_a(){
// Just imagine some complicated code
if(isset($_SESSION['language'])){
include 'left_side2.php';
} else {
include 'left_side.php';
}
// More complicated code to determine which file to include
}
function b() {
include_a();
print_r($lang); // Will say that $lang is undefined
}
So essentially, there is an array called $lang in left_side.php and left_side2.php. I want to access it inside b(), but the code setup above will say that $lang is undefined. However, when I copy and paste the exact code in include_a() at the very beginning of b(), it will work fine. But as you can imagine, I do not wish to copy and paste the code in every function that I need it.
How can I alleviate this scope issue and what am I doing wrong?
If the array $lang gets defined inside the include_a() function, it is scoped to that function only, even if that function is called inside b(). To access $lang inside b() you need to call it globally.
This happens because you include 'left_side2.php'; inside the include_a() function. If there are several variables defined inside the includes and you want them to be at global scope, then you will need to define them as such.
Inside the left_side.php, define them as:
$GLOBALS['lang'] = whatever...;
Then in the function that calls them, try this:
function b() {
include_a();
print_r($GLOBALS['lang']); // Now $lang should be known.
}
It is considered 'bad practice' to use globals where you don't have to (not a consideration I subscribe to, but generally accepted). The better practice is to pass by reference by adding an ampersand in front of the passed variable so you can edit the value.
So inside left_side or left_side2 you would have:
b($lang);
and b would be:
function b(&$lang){...}
For further definitions on variable scopes check this out

PHP include_once inside a function to have global effect

I have a function in php:
function importSomething(){
include_once('something.php');
}
How do i make it sot that the include_once has a global effect? That everything imported will be included in the global scope?
You can return all the variables in the file like so...
function importSomething(){
return include_once 'something.php';
}
So long as something.php looks like...
<?php
return array(
'abc',
'def'
);
Which you could assign to a global variable...
$global = importSomething();
echo $global[0];
If you wanted to get really crazy, you could extract() all those array members into the scope (global in your case).
include() and friends are scope-restricted. You can't change the scope that the included content applies to unless you move the calls out of the function's scope.
I guess a workaround would be to return the filename from your function instead, and call it passing its result to include_once()...
function importSomething() {
return 'something.php';
}
include_once(importSomething());
It doesn't look as nice, and you can only return one at a time (unless you return an array of filenames, loop through it and call include_once() each time), but scoping is an issue with that language construct.
If you want ordinary variable definitions to be teleported into the global scope automatically, you could also try:
function importSomething(){
include_once('something.php');
$GLOBALS += get_defined_vars();
}
However, it if it's really just a single configuration array, I would also opt for the more explicit and reusable return method.
I know this answer is really late for this user, but this is my solution:
inside your function, simply declare any of the vars you need to ovewrite as global.
Example:
Need to have the $GLOBALS['text'] set to "yes":
Contents of index.php:
function setText()
{
global $text;
include("setup.php");
}
setText();
echo 'the value of $text is "'.$text.'"'; // output: the value of $text is "yes"
Contents of setup.php:
$text = "yes";
The solution is similar to mario's, however, only explicitely globally-declared vars are overwritten.
All variables brought in from an included file inherit current variable scope of the including line. Classes and functions take on global scope though so it depends what your importing.
http://uk.php.net/manual/en/function.include.php
(Final Paragraph before example)

Help with include function

Im trying to make a simple template system in PHP. I'm new to PHP, so it's nothing serious. I have some problems though:
A regular include works:
$variable = "test";
include("templates/news.html");
But this won't:
This says $variable is undefined:
$variable = "test";
getTemplate("news");
The Function:
function getTemplate($tpl) {
$file = "templates/$tpl.html";
if (file_exists($file))
return include($file);
return false;
}
news.html
<h1>php echo $variable</h1>
the function works and includes the page but it dont write out the variables
I include the function on top of all pages.
Thanks in advance!
With the extract function, you can define different variables from an array.
You can make it like this:
$vars = array('var1' => "value1", 'var2' => "value2");
function getTemplate($tpl, $vars) {
$file = "templates/$tpl.html";
extract($vars, EXTR_SKIP)
if (file_exists($file))
return include($file);
return false;
}
getTemplate('news', $vars);
In your template, you can use $var1 and $var2.
you are trying to reach global variable in a function.
you must either declare a variable global in a function or just use: $GLOBALS["variable"]
function () {
global $variable;
etc..
}
Because you're including the file from inside a function, the contents of that file are no longer in the global scope.
You either need to add global $variable; to the start of the function, or use echo $GLOBALS['variable']; in news.html.
I believe your problem has to do with variable scoping. When you call a function, you are unable to access variables outside of that function, except global variables (more on this in a second) or variables passed to your function. The global function tells PHP that you'd like to use the same variable in the function as in the parent scope. You can also use the define function to define constants for your script, which are universally accessed.
Using large number of global variables is very frowned upon, I'd suggest passing either an associative array to the function, or use a more object oriented approach.
Hope this helps,
Jacob
Have you considered using Smarty
If you don't want to use this template engine maybe you should check some of the ideas behind it. In short, it deals with assigning variables to templates by assigning variable values to an object. If you assigned variable to a template object you can then use it in template file.

PHP Preserve scope when calling a function

I have a function that includes a file based on the string that gets passed to it i.e. the action variable from the query string. I use this for filtering purposes etc so people can't include files they shouldn't be able to and if the file doesn't exist a default file is loaded instead.
The problem is that when the function runs and includes the file scope, is lost because the include ran inside a function. This becomes a problem because I use a global configuration file, then I use specific configuration files for each module on the site.
The way I'm doing it at the moment is defining the variables I want to be able to use as global and then adding them into the top of the filtering function.
Is there any easier way to do this, i.e. by preserving scope when a function call is made or is there such a thing as PHP macros?
Edit: Would it be better to use extract($_GLOBALS); inside my function call instead?
Edit 2:
For anyone that cared. I realised I was over thinking the problem altogether and that instead of using a function I should just use an include, duh! That way I can keep my scope and have my cake too.
Edit: Okay, I've re-read your question and I think I get what you're talking about now:
you want something like this to work:
// myInclude.php
$x = "abc";
// -----------------------
// myRegularFile.php
function doInclude() {
include 'myInclude.php';
}
$x = "A default value";
doInclude();
echo $x; // should be "abc", but actually prints "A default value"
If you are only changing a couple of variables, and you know ahead of time which variables are going to be defined in the include, declare them as global in the doInclude() function.
Alternatively, if each of your includes could define any number of variables, you could put them all into one array:
// myInclude.php
$includedVars['x'] = "abc";
$includedVars['y'] = "def";
// ------------------
// myRegularFile.php
function doInclude() {
global $includedVars;
include 'myInclude.php';
// perhaps filter out any "unexpected" variables here if you want
}
doInclude();
extract($includedVars);
echo $x; // "abc"
echo $y; // "def"
original answer:
this sort of thing is known as "closures" and are being introduced in PHP 5.3
http://steike.com/code/php-closures/
Would it be better to use extract($_GLOBALS); inside my function call instead?
dear lord, no. if you want to access a global variable from inside a function, just use the global keyword. eg:
$x = "foo";
function wrong() {
echo $x;
}
function right() {
global $x;
echo $x;
}
wrong(); // undefined variable $x
right(); // "foo"
When it comes to configuration options (especially file paths and such) I generally just define them with absolute paths using a define(). Something like:
define('MY_CONFIG_PATH', '/home/jschmoe/myfiles/config.inc.php');
That way they're always globally accessible regardless of scope changes and unless I migrate to a different file structure it's always able to find everything.
If I understand correctly, you have a code along the lines of:
function do_include($foo) {
if (is_valid($foo))
include $foo;
}
do_include(#$_GET['foo']);
One solution (which may or may not be simple, depending on the codebase) is to move the include out in the global scope:
if (is_valid(#$_GET['foo']))
include $_GET['foo'];
Other workarounds exists (like you mentioned: declaring globals, working with the $_GLOBALS array directly, etc), but the advantage of this solution is that you don't have to remember such conventions in all the included files.
Why not return a value from your include and then set the value of the include call to a variable:
config.php
return array(
'foo'=>'bar',
'x'=>23,
'y'=>12
);
script.php
$config = require('config.php');
var_dump($config);
No need to mess up the place with global variables
Is there any easier way to do this, i.e. by preserving scope when a function call is made
You could use:
function doInclude($file, $args = array()) {
extract($args);
include($file);
}
If you don't want to explicitly pass the variables, you could call doInclude with get_defined_vars as argument, eg.:
doInclude('test.template.php', get_defined_vars());
Personally I would prefer to pass an explicit array, rather than use this, but it would work.
You can declare variables within the included file as global, ensuring they have global scope:
//inc.php
global $cfg;
$cfg['foo'] = bar;
//index.php
function get_cfg($cfgFile) {
if (valid_cfg_file($cfgFile)) {
include_once($cfgFile);
}
}
...
get_cfg('inc.php');
echo "cfg[foo]: $cfg[foo]\n";

Categories