Why does only one of these PHP variables enter the global scope? - php

I have PHP files a bit like this
public_html/environment.php
public_html/task.php
phpbin/actions.php
phpbin/library.php
environment.php is included by public_html/* before any other php files are included, phpbin/* assumes everything in environment.php is already available.
It defines these two globals
$g_foo = "...";
$g_bar = "...";
task.php includes this logic
function do_stuff ()
{
require_once determine_required_file ();
...
}
In this case, determine_required_file() returns "/path/to/phpbin/actions.php"
actions.php in turn contains
require_once "/path/to/phpbin/library.php"
Finally, library.php contains
$x = $g_foo;
$y = $g_bar;
I get this error:
Undefined variable: $g_bar;
Now, $g_foo and $g_bar are strictly read-only except in environment.php, I have exhaustively grepped and verified that there are no other places which create or modify variables with these names.
I am aware that PHP globals are weird, and doing things like including files from within functions can mess up your scope. I know that I should probably use define() or some other method, yeah yeah.
My question is this (yes, I'm asking you to speculate in the absence of full code, sorry):
Why might $g_bar generate an error but not $g_foo?
I assume the in-function-inclusion is probably responsible, but assuming these globals really are read-only, what should I be looking for as the culprit for why one ends up in global scope in library.php but not the other?

You need to use include_once instead of required_once for your fields that needed your global variables, or define a global $g_bar, $g_foo.
http://php.net/manual/en/language.variables.scope.php

Related

About variables

So if I have a variable in a php file I just made , if I put it in the same folder as other php files and include it into one of them , I can use any variable from that file right?
Sorry if it's a bit of noobish
Basically yea. Running the include is equivalent to pasting the code.
Specifically, from the PHP manual:
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.
In general the PHP manuals are pretty much self explained. You will find there most answers for basic questions as this one.
In short, yes.
From the documentation
The scope of a variable is the context within which it is defined. For
the most part all PHP variables only have a single scope. This single
scope spans included and required files as well. For example:
<?php
$a = 1;
include 'b.inc';
?>
Here the $a variable will be available within the included b.inc script.
Note that this also works in the other direction:
<?php
/* a.inc */
$a = 1;
?>
<?php
/* b.php */
include 'a.inc';
echo $a;
?>

PHP Global Variables Issue

I've got a scope problem here. and no idea why its not working, ive got a setup as follows:
functions.php
global $id;
$id = $_GET['id'];
index.php
require_once('functions.php');
echo $id;
now inside functions.php i can echo out $id. however my echo $id; inside index.php is bringing up blank. absolutely nothing.
what am i doing wrong?
In PHP, the global keyword allows you to reference variables in the global scope from inside a local scope - eg to access a global variable inside a function. You don't need global in your example, because you are in the global scope anyway.
I suspect you are showing us a simplified version of what you have, where the issue is in code you haven't shown us.
Why you shouldn't use globals
Confusion like this is part of why using globals is a bad idea and should be avoided.
The alternative is to pass variables around explicitly, so for example if you call a function or instantiate a class from another file, you pass the variable in as a parameter to that function or constructor. Doing this, instead of using global variables, makes it easier to follow what function is accessing what variable because you can follow the trail easier.
You don't need globals between files, only for functions.
Functions.php
<?php
$foobar = "Hello";
?>
Index.php
<?php
include('Functions.php');
echo $foobar;
?>
You shouldn't use globals, but you have it backwards. You declare the variable global after you include its definition:
file1.php:
$name = 'Josh';
file2.php:
require_once('file1.php');
global $name;
echo $name;
#thomasrutter is correct (+1) Global variables areA Bad Thing. Always seek alternatives.
Perhaps you can use $_SESSION (which sort of amounts to the same thing, I know), or declare a class which has a static variable and use a getter() and setter() ? (the latter uis definitely cleaner, but $_SESSION might tie in better with your design, I can't say)
Btw, I hope that functions.php was just an example name, or that you have an extermely simple project.
Otherwise fucntions.php is going to get extermaly large and hard to oversee. If you are going OO then user one file per class, otherwse try to group your functions into separate files (file_management.php, databse.php, forms.php and the like).
If you are just starting out, I would advise you to use Netbeans and document your code with PhpDoc comments which will allow you to generate good documentation which you can view in your browser (including the structure of your coed, what gets declared where, used where, descruptions of function parameters and return values, etc)
Btw, I notice that you use include() I prefer require_once. The _once helps spee dperformnce a little and hte require makes sure that you are aware of missing files more quickly.
Oh, and learn to use Xdebug, which plays well with NetBeans.

Can you pass values to included files in PHP without using globals?

A pattern I tend to use often in PHP is setting a few globals (such as $page, $user, $db, etc), and then including a file which uses those globals. I've never liked the idea of using globals for this, though, so I'm looking for a better way.
The obvious solution is to define a class or function in the subfile, and call it after the file is included. There are cases where that can't work though, such as this:
// Add entries to a URI table from each section of the site
global $router;
$router = new VirtualFileSystem();
$sections = array('store', 'forum', 'blog');
foreach($sections as $section)
include dirname(__FILE__) . $section . '/routing.php';
// Example contents of 'forum/routing.php'
// implicitly receive $router from caller
$router->add('fourm/topic/', 'topic.php');
$router->add('forum/topic/new/', 'new_topic.php');
// etc
If I tried to wrap each routing.php in a function and call them each with $router as an argument, the same function name would clash after being defined in multiple files.
I'm out of ideas. Is there a better way to pass variables to included files without polluting the global namespace?
include and its siblings are basically just copy-paste helpers, and the code inside them shares scope with the calling block - as if you'd copy & paste it just where the include statement is. The sane way of using them is to think of them the same way you'd use #include in C or using in C# or import in Java: import some code to be referenced later on. If you have code in the included file that needs parameters, then wrap it in a function, put the parameters in the function arguments, use include_once at the top of the including file, and call the function with the parameters you want, wherever you need to. No globals required. As a rule of thumb, in regular operation, putting any code that "does" something (executes statements in the global scope) in an included file is best avoided IMO.
No, there is not. You're not passing variables to included files anyway. The code that is included behaves as if it was written where the include statement is written. As such, you're not passing variables into the included file, the code in the file can simply use the variables that are in scope wherever the include statement is located.
In your case the contents of forum/routing.php are not really standalone code, they're code snippets that depend on a very specifically set up scope to function correctly. That's bad. You should write your includable files in a way that does not couple them to the including code. For example, you could make your Router a static class and call it statically in forum/routing.php:
require_once 'virtual_file_system.class.php';
VirtualFileSystem::add('forum/topic/', 'topic.php');
As long as there is a class VirtualFileSystem in your app, this will work, and won't pollute the namespace any more than it already is anyway.
just isolate includes in a function:
function add_entries_to_router($router, $sections) {
foreach($sections as $section)
include dirname(__FILE__) . $section . '/routing.php';
}
$router = new VirtualFileSystem();
add_entries_to_router($router, array('store', 'forum', 'blog'));
You can try an OOP way by making a Configuration class as a singleton and retrieving it when you need it.
You could define magic methods for __get and __set to add them to an private array var and make the constructor private.
I usually define as constant only the path to my src project in order to load class files quickly and properly (and use some SPL too).
But I agree with #tdammers about the fact that an include keep the environment variables like if you were on the caller file (the one who makes the include).

PHP variable from other file comes back as NULL

In one PHP file, I have this code:
require_once $_SERVER['DOCUMENT_ROOT'] . '/custom/functions.php';
global $testVar;
var_dump($testVar);
In the functions.php file, I have this at the beginning, followed by a few other functions:
function pr($s) {
echo '<pre>', htmlspecialchars(print_r($s,true)), '</pre>';
}
$testVar = 'hello world';
When running the first file, the variable comes back as NULL. I added the global bit but it shouldn't be necessary. This is part of a Joomla module but I've never had problems including files before, it should just work like regular PHP. Why might this be happening?
First, try to use Joomla's path constants such as JPATH_BASE instead of $_SERVER['DOCUMENT_ROOT']. Joomla has a lot of useful constants, check it's documentation.
I've read your answer, and reading php documentation I tried to find a reason to why you need to use global keyword twice.
First, Variable scope.
The scope of a variable is the context within which it is defined. For the most
part all PHP variables only have a single scope.
(...)
However, within user-defined functions a local function scope is introduced.
Any variable used inside a function is by default limited to the local
function scope.
The variable isn't in a function scope, so that's why we thought the NULL was a strange behavior.
But then I read include and found something interesting:
(...)
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.
I can't see any mention about the variables being global in this paragraph. So,it seens that, being cumbersome or not, your solution is the right thing to do when you want to use global variables like that.
In your situation, if doing this is cumbersome, I would create a simple class. If you have just helper functions in your file, create a class Util{} with a lot of methods and $testVar as an attribute.
I have found a solution that seems to work: using the global keyword both when setting the variable initially, and just before I need to use it.
(However this is quite cumbersome, and I'm still not sure why it happens, so if anyone has a better solution, feel free to post.)

"Namespaces", constants and multiple PHP includes

I have some PHP code similar to the following:
foreach ($settingsarray as $settingsfile)
{
include ($settingsfile);
// do stuff here
}
$settingsarray is an array of file names from a particular folder.
The problem is, that each $settingsfile defines constants (with the same names), which of course, can not be redefined.
What possible methods are there to prevent errors occurring in this situation?
Two options I can think of include, changing all the constants to variables and using PHP namespaces.
However, I'm not sure how I would go about using namespaces, it would require the declaration at the start of every $settingsfile? Is there a method of isolating constants, ariables and functions without using namespaces?
What I would really love, is to be able to do something as simple as:
foreach ($settingsarray as $settingsfile)
{
{//added braces to indicate where the isolation is
include ($settingsfile);
// do stuff here
}//what happens in here does not affect what happens outside of here
}
I should just note, this is part of a new feature, and is the only part of the code that loads all the $settingsfiles. The rest of the code only ever loads one file at a time. One reason I am using constants is so that I don't have to worry about defining variables "global" to be able to access them inside functions.
My answer is somewhat complex, but should work for you quite nicely. I'm assuming you have a ton of these settings files, since you're so averse to changing each one individually.
If you're able to use namespaces, I'll assume you've already upgraded to PHP 5.3RC2. Copy the following into a .php file, and change the namespace to your liking:
<?php
namespace myapp\config;
function define($key, $val) {
Config::set($key, $val);
}
class Config {
private $vars = array();
// This class should not be instantiated
private function __construct() {}
public function set($key, $val) {
self::$vars[$key] = $val;
}
public function get($key) {
return isset(self::$vars[$key]) ? self::$vars[$key] : NULL;
}
}
?>
Include that in your code, and now making everything work is a simple matter of changing the
<?php
in your settings files to
<?php namespace myapp\config;
If you have a ton of them, a quick 'sed' command in your terminal should take care of it quite nicely, all in one fell swoop.
If I lost you on how to use my code, here's an example:
<?php
require_once('the_php_file_i_just_gave_you.php');
use \myapp\config\Config;
foreach ($settingsarray as $settingsfile) {
include ($settingsfile);
$varname = 'key';
echo "In this file, '$varname' equals: " . Config::get($varname);
}
?>
Good luck!
Okay, so if I'm understanding you correctly, inside that loop you'd like to do things with the constants defined inside each $settingsfile and then basically get rid of them after each loop?
Unlike variables, a constant is a value that, once set, cannot be changed or unset during the execution of your script
src
Your only option is to change the define() declarations to variables.
not easy but you could read the php file with file_get_contents($settingsfile) then rewrite the constants, and eval the code.
$settingsstr = file_get_contents($settingsfile);
$settingsstr = preg_replace('/<\?\w*(.*)/', '\\1', $settingsstr);//get rid of the php open tag
$settingsstr = preg_replace('/define\("(\w+)"/', 'define("NAMESPACE_\\1"', $settingsstr);
eval($settingsstr);
You have a few options:
Use PHP Namespaces
Surprisingly, PHP actually does have support for namespaces. I was very surprised to find this since I have been working in PHP for years and have never heard of this feature. It is likely a newer feature and one that is not commonly used, so I would suggest not using this method
Use Class Constants
You could use classes and class constants to load your settings. Class constants are local to the defining class, so you will not need to worry about name collisions within the class. You would probably need to do more than include the file, but you could call a method on each class, such as defineConstants() or loadConfiguration() that would define the application constants, keeping the class constants for internal use.
Use Functions to Extend the Settings
Another idea is to create a settings array and separate functions to 'extend' those settings. That way you can over-write any configuration in subsequent functions without causing an error. Finally, if you want to put those in constants, you could loop over the array and use define() to define the constants.
There are many other ways to accomplish what you are trying to do, but these are just a few ideas.
A settings file should not contan PHP code.
It should be some kind of "standard" format.
http://www.php.net/parse_ini_file
In the first step (in your foreach) you read all files
Collect the variables into a temporary array
In another loop over this array, you can define them as constants.
If you routinely feel the need to "redefine constants" then something is amish in the application design.

Categories