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.
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.
I have a config file that contain credential information to connect to an API
I include my config file in 2 functions in 2 different file
In the first called function, I have my credential variables but when I call my second function, my credential variables are empty.
index.php
<?php
require_once("./connector/hot/hotelbeds/book.php");
if($_REQUEST['connector'] == 'hotelbeds')
{
require_once("connector/hot/hotelbeds/validate.php");
validate_hotelbeds($_REQUEST);
}
$booking_output = book_hotelbeds($_REQUEST);
?>
validate.php
<?php
function validate_hotelbeds($results)
{
$account = $results['header']['account'];
include_once("./connector/hot/hotelbeds/account_config/$account/config.php");
// $url contain my url
$validate = curl_get($url , $results);
}
?>
book.php
<?php
function book_hotelbeds($results)
{
$account = $results['header']['account'];
include_once("./connector/hot/hotelbeds/account_config/$account/config.php");
// $url is empty
$book = curl_get($url , $results);
}
?>
config.php
<?php
$url = "http://www.websitelink.com";
?>
The first time you require it, the variables will be introduced.
When you require it again from inside a function, the file has already been required so it is ignored.
The variables are outside the scope of the function at this point, so if you have to you would need to access them by declaring them as global.
Perhaps a better idea would be do declare those variables as constants instead, which means they will be available within the function scopes:
$myVariable = 'hello';
define('MY_CONSTANT', 'world');
echo 'Global scope: ', $myVariable, MY_CONSTANT, PHP_EOL; // helloworld
function myFunction()
{
echo 'Function scope: ', $myVariable, MY_CONSTANT, PHP_EOL; // world
}
function myGlobalFunction()
{
global $myVariable;
echo 'Function scope using global: ', $myVariable, MY_CONSTANT, PHP_EOL; // helloworld
}
Example.
Put your include_once("./connector/hot/hotelbeds/account_config/$account/config.php"); into index instead. Then if you wanted to use the var $url you would need a line above stating that you want that global var: global $url;.
Also I suggest changing $url name and change it to constant like: const API_URL = 'website_url'
Functions require_once and include_once include file only on time for one call of script. Because you include files book.php and validate.php in index.php then PHP include config.php only one time.
You can include config.php in index.php and use global directive inside your function.
Or you can just use functions include and require. These functions include one file to script many times - on each call.
I'm a newbie to php, and I'm in the process of hacking through some moodle code for a prototype.
Anyhows I have some data Id like passed from file1.php to file2.php, for which Im using global variables. The values are intialized in file2 and I need them for use in file1. Here's how I go about it
file1.php
<?php// top of file 1
global $content; // discussion content // line 3
file2.php
global $content;// line 3379
$content=$post->subject;
Back in file1.php
echo 'global scope'.$content; // this is always empty// line 168
Am I missing something here?
Attached files file1.php and file2.php
You use global to get a global variable
//file 1
$a = "im a global variable";
function foo(){
global $a;
echo $a;
}
//file 2
require "file1.php";
function foo2(){
global $a;
echo $a;
}
Just sharing a thought,
suppose in file1, you have $content = "myID";
and in file2, if you include("file1.php"), you can access by doing,
global $content;
var_dump($content) will output string 'myId' (length=4)
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"
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).