What are the differences in variable and function scoping, between the "includer" and the "includee"?
For example, these two tests work identically, but are there scoping subtleties I should know about?
Test 1:
File "one.php":
<?php
$a = 5;
include("two.php");
?>
File "two.php:
<?php
function f($x) { return $x * 2; }
echo f($a);
?>
Test 2:
File "one.php":
<?php
$a = 5;
?>
File "two.php:
<?php
include("one.php");
function f($x) { return $x * 2; }
echo f($a);
?>
When you execute a PHP file, it starts off in the global scope. The include documentation states;
When a file is included, the code it contains inherits the variable
scope of the line on which the include occurs. Any variables available
at that line in the calling file will be available within the called
file, from that point forward. However, all functions and classes
defined in the included file have the global scope.
Since when you include the second file you're in both cases in global scope, the variable scope will stay global and everything else included will always have global scope. In other words, everything in both files and in both cases ends up in global scope and there is no difference in scoping between the two.
When you are including a file you can think of it like a single file ie:
Test 1 as a single file:
<?php
// execute commands in this file
$a = 5;
// then continue execution of include("two.php");
function f($x) { return $x * 2; }
echo f($a);
?>
Test 2 as a single file:
<?php
// first include include("one.php");
$a = 5;
// then continue execution of two.php
function f($x) { return $x * 2; }
echo f($a);
?>
Hope you can understand the difference between the two. In essence it is similar to copying and then pasting the included commands of the include file.
Variables and function scope is as if it were the same file as long as the included file precedes the variable or function call in the "includee"
Related
My php script includes another en.php file which contains the required english strings. This same page also calls a html page which uses the file and formats it using the contents of the en.php file.
I have a function in this script which references variables defined in the included script but I am getting error messages of the variable not being found. If I reference the variable outside the function, the variable is accessed correctly. Why can I not access these variables inside the function?
en.php
<?php
$lang_yes = 'Yes';
$lang_no = 'No';
?>
example.php
<?php
include_once('addons/assq/lang/en.php');
echo $lang_yes;
$q1 = convertToYesOrNoString(0);
echo $q1;
function convertToYesOrNoString($value){
//include_once('addons/assq/lang/en.php');
if ($value == 0){
return $lang_no;
}else if ($value == 1){
return $lang_yes;
}else{
return "---";
}
}
?>
My output is as follows:
Yes
Undefined variable: lang_no in example.php on the line in the function
I tried including the en.php directly into the function but that did not work either. How can I access these variables inside my function while including the file as implemented above?
You can either define it as a constant, pass it as an argument or declare it as a global within the function:
function convertToYesOrNoString($value){
global $lang_no, $lang_yes;
//...
}
That's a scope issue. That variable $lang_no will not be accessed under that function , you need to pass that as a parameter instead to the function definition.
function convertToYesOrNoString($value,$lang_no){ //<--- Like this.
Since you have mentioned that you have a lot of parameters .. you can write a turnaround like this...
Your en.php
<?php
//Map all those variables inside an array as key-value pair. as shown
$varArray=array('lang_yes'=>'Yes','lang_no'=>'No');
Some test.php
<?php
include('en.php');
function convertToYesOrNoString($varArray)
{
extract($varArray);
echo $lang_yes; // "prints" Yes
echo $lang_no; // "prints" No
}
convertToYesOrNoString($varArray);
I have a file called test1.php with lots of variable and some function definitions. I am trying to include this file to one another file called test2.php and use the variables and the functions.
test1.php
$i = "a";
$ii = "b";
$iii = "c";
function test1a(){ return "lol"; }
test2.php
function test2a(){ include 'test1.php'; return $i; }
function test2b() { include 'test1.php'; return test2c(); }
function test2c(){ include 'test1.php'; return $iii; }
function test2d() { include 'test1.php'; return test1a(); }
index.php
include 'test2.php'
echo test2a();
echo test2b();
echo test2c();
echo test2d();
Reason:
I have the same code base in two different servers. Only the test2.php file will be different.Each server will have different values inside the test2.php but with same variable name. test2.php will act somewhat like a localization file.
My problem is some of the variables or not showing up. Is there a better way to do this. I don't want to include the file in every function.
Thanks.
Just do it the other way round:
put all the different variables into one file in to an array, without any functions:
//config.php
$config['setting1'] = "val1";
$config['setting2'] = "val2";
$config['setting3'] = 42;
...
and further:
//index.php
include_once("config.php");
echo $config['setting1'];
....
now you may have different configs on different servers w/o need to change any functions.
You are trying to include file with one function several times. Functions are always global. So second include gives you an fatal error Fatal error: Cannot redeclare test1a() (previously declared in [..]).
You should put this function to separate file and use include_once.
I am trying to include a file using a function, and I have several variables defined. I want to access the included file to access the variables, but because I am including it using a function, it is not accessible. The sample scenerio is as follows:
i.e the contents of index is as follows
index.php
<?
...
function include_a_file($num)
{
if($num == 34)
include "test.php";
else
include "another.php"
}
...
$greeting = "Hello";
include_a_file(3);
...
?>
And the contents of test.php is as follows
test.php
<?
echo $greeting;
?>
The test file is throwing a warning saying the $greeting is not defined.
This will not work. include and require act as if the code you're including was literally part of the file at the point the include/require was executed. As such, your external files are going to be in the scope of the include_a_file() function, which means $greeting is OUT OF SCOPE within that function.
You'll have to either pass it in as a parameter, or make it global within the function:
function include_a_file($num, $var) {
^^^^-option #1
global $greeting; // option #2
}
$greeting = 'hello';
include_a_file(3, $greeting);
Are you sure your correctly including? And remember PHP is case sensitive:
$Test = "String";
$TEst = "String";
Both completely different variables..
Furthermore, don't just echo out a variable, wrap it within in a isset condition:
if (isset($greeting)){
echo $greeting;
} // Will only echo if the variable has been properly set..
Or you could use:
if (isset($greeting)){
echo $greeting;
}else{
echo "Default Greeting";
}
Just a quick question, but I've been working on a small MVC framework and noticed something.
For example:
--PHP file--
class loadFiles{
function __construct($file = NULL){
include $file . '.php';
}
}
$loadfiles = new $loadFiles('crucialsettings');
echo $randomstring; //Throws an error
--crucialsettings.php--
<?php
$randomstring = 'hello';
?>
I only just realised that files included inside an objects scope are inaccessable from the global scope. What is the best way to include a file inside an object so it can be accessed globally?
I would like to be able to:
$loadfiles->settings();
$loadfiles->classes();
$loadfiles->passwords();
I want to build a class that handles global file includes.
It doesn't matter where you include or require code from in PHP. The interpreter is pretty linear in it's first definition pass, that is to say that it will basically compress all of the included / required files into one large file in the exact order in how it was read.
One thing to note about this is that scope does change. but everything is applied to the "global" scope. You can always import something from the global scope into your current scope using the "global" keyword to declare a variable prior to using it. So when you want to use a "global" variable from another script just ask for it.
A little example...
a.php
include('b.php');
global $myVar;
echo $myVar;
b.php
include('c.php');
c.php
$myVar = 'Hello World';
What the interpreter see's this code as after it's first pass
// In global scope
$myVar = 'Hello World'
// In a.php scope
global $myVar;
echo $myVar;
In short from your php file simply add the line
global $randomstring;
After you include the crucialsettings.php file and your echo will work.
Appears that your framework here is too reliant on non-OOP for its innards. Not a preferable way to build up, but you can do what you want by cycling through list of variables and making them part of your class/instance scope. A rather helpful function here is get_defined_vars();
Lets say you have files a.php, b.php and c.php. Each looks like this:
a.php: <?php $a = "AAAAAA";
b.php: <?php $b = "BBBBBBBBBB";
c.php: <?php $c = "CCCCCCCCCCCCCCCCCCCCCCCCCCC";
class mystuff {
function include_with_vars( $____file ) {
// grab snapshot of variables, exclude knowns
$____before = get_defined_vars();
unset( $____before['____file'] );
// include file which presumably will add vars
include( $____file );
// grab snapshot of variables, exclude knowns again
$____after = get_defined_vars();
unset( $____after['____file'] );
unset( $____after['____before'] );
// generate a list of variables that appear to be new
$____diff = array_diff( $____after, $____before );
// put all local vars in instance scope
foreach( $____diff as $variable_name => $variable_value ) {
$this->$variable_name = $variable_value;
}
}
function __construct($file = NULL){
$this->include_with_vars( "a.php" );
$this->include_with_vars( "b.php" );
$this->include_with_vars( "c.php" );
}
}
$t = new mystuff();
echo "<PRE>";
print_r( $t );
This program will now take local variables from your include() directives and put them in the class scope:
mystuff Object
(
[a] => AAAAAA
[b] => BBBBBBBBBB
[c] => CCCCCCCCCCCCCCCCCCCCCCCCCCC
)
In other words, your local variables from file a.php ($a) are now $t->a.
So the senario is that I want to have a custom function for requiring libraries. Something like:
define('E_ROOT', str_replace('//','/',dirname(__FILE__)));
/* ... */
function e_load($fn, $allowReloading = FALSE) {
$inc = E_ROOT.'/path/here/'.$fn.'.php';
if($allowReloading)
require $inc; // !!!
else
require_once $inc; // !!!
}
The problem being that require and require_once will load the files into the namespace of the function, which doesn't help for libraries of functions, classes, et cetera. So is there a way to do this?
(Something avoiding require and require_once altogether is fine, as long as it doesn't use eval since it's banned on so many hosts.)
Thanks!
Technically include() is meant to act as though you're inserting the text of included script at that point in your PHP. Thus:
includeMe.php:
<?php
$test = "Hello, World!";
?>
includeIt.php:
<?php
include('includeMe.php');
echo $test;
?>
Should be the exact same as:
<?php
/* INSERTED FROM includeMe.php */
$test = "Hello, World!";
/* END INSERTED PORTION */
echo $test;
?>
Realizing this, the idea of making a function for dynamically including files makes about as much sense (and is about as easy to do) as having dynamic code all-together. It's possible, but it will involve a lot of meta-variables.
I'd look into Variable Variables in PHP as well as the get_defined_vars function for bringing variables into the global scope. This could be done with something like:
<?php
define('E_ROOT', str_replace('//','/',dirname(__FILE__)));
/* ... */
function e_load($fn, $allowReloading = FALSE) {
$prev_defined_vars = get_defined_vars();
$inc = E_ROOT.'/path/here/'.$fn.'.php';
if($allowReloading)
require $inc; // !!!
else
require_once $inc; // !!!
$now_defined_vars = get_defined_vars();
$new_vars = array_diff($now_defined_vars, $prev_defined_vars);
for($i = 0; $i < count($new_vars); $i++){
// Pull new variables into the global scope
global $$newvars[$i];
}
}
?>
It may be more convenient to just use require() and require_once() in place of e_load()
Note that functions and constants should always be in the global scope, so no matter where they are defined they should be callable from anywhere in your code.
The one exception to this is functions defined within a class. These are only callable within the namespace of the class.
EDIT:
I just tested this myself. Functions are declared in the global scope. I ran the following code:
<?php
function test(){
function test2(){
echo "Test2 was called!";
}
}
//test2(); <-- failed
test();
test2(); // <-- succeeded this time
?>
So the function was only defined after test() had been run, but the function was then callable from outside of test(). Therefore the only thing you should need to pull into the global scope are your variables, via the script I provided earlier.
require_once E_ROOT.$libName.'.php';
KISS
Instead of doing this...
$test = "Hello, World!";
... you could consider doing this ...
$GLOBALS[ 'test' ] = "Hello, World!";
Which is safe and consistent in both local function context, and global include context. Probably not harmful to visually remind the reader that you are expecting $test to become global. If you have a large number of globals being dragged in by your libraries maybe there's justification for wrapping it in a class (then you have the benefit of spl_autoload_register which kind of does what you are doing anyhow).