custom super global in PHP - php

Is it possible to define a custom super global variable? (whether in code, or using php.ini)
Example, for all projects I use a custom framework. The framework essentially stores all data about running instance of the script (template loaded, template variables, etc.) in a single variable. I'd like that variable to become cross-system accessible.
I am perfectly aware of $_GLOBALS and global, however the question is asking if it is possible to define custom super global variable, e.g. $foo, which would become accessible by the same name in any scop.

Sadly there is no way to define superglobals.
(There is no mechanism in PHP for user-defined superglobals.)
Source

Sorry, but all answers are wrong.
Correct answer: yes, it is possible, but not with the so-called "core" PHP functionalities.
You have to install an extension called runkit:
http://www.php.net/manual/en/runkit.installation.php
After that, you can set your custom superglobals in php.ini as documented here:
http://www.php.net/manual/en/runkit.configuration.php#ini.runkit.superglobal

This is not possible, and also bad design. (as are super globals).
If you do think global state is the answer for you, you could use static classes.

I'm not offering a solution to this problem as such, but I suggest that you avoid using globals. Generally speaking use of globals is considered bad practice, even in programming languages that make use of them by design. You cannot be sure how your global will affect other applications that declare the same variable. Personally I would prefer a more managed approach to retrieving data, either specifically from another php script or by writing a php extension that defines a new function and returns the data you want. It's not unusual to store application settings in a database, be that MySQL or flat file text, and would be my preferred method for sharing information cross-application.

A little hack(trick) that can be used as superglobals, it is a singletone like implementation but I am agree with #Matt Esch, don't use superglobals ...
class K
{
public $CONST = array();
private static $_instance = null;
private function __construct()
{
}
protected function __clone()
{
}
static public function I()
{
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
}
then you can use this in all methods, class, functions, like a superglobal var.
K::I()->CONST[0] = "somevar";
K::I()->CONST[1] = array(1, 2, 3, 4, 5);

My solution
Really php dont support to define more superglobals but if you want share vars between differents users and sessions, my solution is to create a unique session for save shared information. The proccess consist in to close current session, open the shared session to write and read and back in to the previous session.
Code:
<?php
ini_set('display_errors',1);
error_reporting(E_ALL);
function get_global($key){
//Get current session
if(session_status()!=PHP_SESSION_ACTIVE)session_start();
$current_id=session_id();
session_write_close();
//Set a global session with session_id=1
session_id(1);
session_start();
//Get superglobal value
$value=null;
if(isset($_SESSION[$key]))$value=$_SESSION[$key];
session_write_close();
//Set the before session
session_id($current_id);
session_start();
return $value;
}
function set_global($key,$value){
//Get current session
if(session_status()!=PHP_SESSION_ACTIVE)session_start();
$current_id=session_id();
session_write_close();
//Set a global session with session_id=1
session_id(1);
session_start();
//Set superglobal value
$_SESSION[$key]=$value;
session_write_close();
//Set the before session
session_id($current_id);
session_start();
}
//Example
//Begin my session normally
session_start();
if(empty($_SESSION['count'])){
$_SESSION['count']=0;
$_SESSION['color']="rgb(".rand(0,255).",".rand(0,255).",".rand(0,255).")";
}
$_SESSION['count']++;
$id=session_id();
//Get the superglobal
$test=get_global("test");
//Set the superglobal test with empty array if this dont set
if($test==null)$test=array();
//Get the superglobal
$test=get_global("test");
//Set values for each reload page and save the session_id that create it
$test[]="<span style='color:".$_SESSION['color']."'>Value: ".rand(0,100)." SessionID: $id</span><br>";
//Save the superglobal
set_global("test",$test);
//Show the superglobal
foreach($test as $t){
echo $t;
}
echo "<b>Reloads = ".$_SESSION['count'].", <span style='color:".$_SESSION['color']."'>This my color</span></b>";
exit;
?>
Test:
In this example $test is superglobal var that contain array with random number and session_id that created it. Each session defines two local variables for color text and count reloads..

Related

PHP variables inside a function

I have not been able to find a good example for this particular case. Most people who ask this question have a more complex use case and so the answer usually involves a complex solution. I simply have a few variables at the beginning of the script that need to be available throughout all the code including several functions. Otherwise I would have to set it in each function, and considering this is a user set value, changing it all throughout the code is just not possible.
<?php
//**** Example User Config Area ****
$name = "blah";
$tag = "blah";
//**********************************
function taco() {
echo $name; //this function needs to use these user set variables
echo $tag;
}
?>
Everyone says NOT to use global variables. Is this a case where global variables actually DOES make sense? If not, what should I do here to make this work?
It should be noted that those values do not change in the program. They only change if the user edits them. Like a DB location or a username etc.
Just pass these variables:
function taco($name, $tag) {
echo $name;
echo $tag;
}
// and
taco($name, $tag);
There are two main ways you can do configuration in PHP.
The first is to create a configuration object that you can pass around to each function. Some people consider this a little clunky, but it does get around using global variables.
That being said, if you're just trying to store configuration details, a global variable is not a bad option and has been discussed on this site before.
You need to think about your use case. If you're dealing with something that could create a race condition, then global variables are of course a bad idea. If you just want to store some static information to reference throughout your code... it's not the end of the world.

Is it possible to declare a variable as "always global"?

Is there any way I can define a variable such a way that I don't need to global $var1; in every function? Just define it in beginning and keep using where ever needed. I know $GLOBALS exist, but don't want to use it.
First let me try to explain why you shouldn't use globals because they are extremely hard to manage. Say a team member overrides an $ADMIN variable in a file to be 1, and then all code that references the $ADMIN variable will now have the value of 1.
globals are so PHP4, so you need to pick a better design pattern, especially if this is new code.
A way to do this without using the ugly global paradigm is to use a class container. This might be known as a "registry" to some people.
class Container {
public static $setting = "foo";
}
echo Container::$setting;
This makes it more clear where this variable is located, however it has the weakness of not being able to dynamically set properties, because in PHP you cannot do that statically.
If you don't mind creating an object, and setting dynamic variables that way, it would work.
You need to pass the variable as a parameter to that function to avoid using GLOBALS.
The Problematic Scenario (Works ! but avoid it at all costs)
<?php
$test = 1;
function test()
{
global $test;
echo $test; // <--- Prints 1
}
test();
The right way...
<?php
$test = 1;
function test($test)
{
echo $test; // <--- Prints 1
}
test($test); //<--- Pass the $test param here
This behaviour called as superglobal variable. But php has limited predefined list of them: Superglobals.
So you cannot add your own superglobal variable.
Variable scope
My suggestions from the most to less radical:
Edit source code of PHP and add your own superglobals.
Do not write code at all.
Do not use PHP (use different language with different variable scope policy).
Do not use functions (write plain script).
Use constants instead of variables.
Use function params instead of using globals.
Use static variables instead of globals. (\G::$variable)

Global vs static variables in PHP

I'm creating a basic framework in PHP. I need to pass data for the current page into different functions, allow them to modify and save it, and then pass it back to the page to be displayed. I was originally planning on storing the data in a global variable like $GLOBALS['data'], but I'm starting to think that using a global is a bad idea. So I'm thinking that instead I will put a static variable in the system class, and access it using system::$data. So, my question is, which would be better and why?
This:
$GLOBALS['data'] = array();
$GLOBALS['data']['page_title'] = 'Home';
echo $GLOBALS['data']['page_title'];
Or this:
class system
{
public static $data = array()
}
function data($new_var)
{
system::$data = array_merge(system::$data, $new_var);
}
data(array('page_title' => 'Home'));
echo system::$data['page_title'];
There really is no difference between a global variable and a public static variable. The class variable is namespaced a tiny bit better, but that hardly makes any difference. Both are accessible anywhere at any time and both are global state.
As it happens, I just wrote an exhaustive article on the subject:
How Not To Kill Your Testability Using Statics
So, my question is, which would be better and why?
You already sense that there is some problem putting this all into globals. Although you have developed some thoughts to encapsulate things into a class.
I think that is a good starting point. Let's add some more spice to the cooking to get this more fluent at the beginning:
$data = new ArrayObject(array());
$data['page_title'] = 'Home';
You have created an object now that you can pass along containing your data. Just pass $data to the area's where it's needed. No global or global static variable needed.
You can even make that type more concrete later on by extending from ArrayObject with your own type.
For the record.
Pro of static:
Clarity of the code. For example:
function fn() {
System::data()
}
versus
function fn() {
global $system;
$system->data()
}
Cons of static:
If you are using psr-4 then you must add (and include) a new class (and a new file). It impacts the performance even if you use opcache (opcache aleviates it but it's not magic).
You must define a block of code.

Working with cookies and session inside PHP class

I have some function (pasted below little piece of function), which I used as separate function and it worked well.
Now I want to move this functon into some class. As you see, it works with $_SESSION and $_COOKIE.
Question is, is it required to send $_SESSION and $_COOKIE as input data while calling this function (I mean something like that: calling like protect($_SESSION, $_COOKIE) and then fetch them from inside function)? or it will work without sending them?
...
public function protect() {
session_start();
if (isset($_SESSION['HTTP_USER_AGENT'])) {
if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT'])) {
$this->logout();
exit;
}
}
if (!isset($_SESSION['id']) && !isset($_SESSION['login'])) {
if (isset($_COOKIE['id']) && isset($_COOKIE['key'])) {
...
$_COOKIE and $_SESSION are superglobals, which means they are available everywhere. You never need to import them, pass them as arguments or anything similar, they are always available in any scope.
For this reason, they should always be treated as read-only - assigning a new value to them will affect the rest of the scripts execution in every scope.
$_SESSION AND $_COOKIE are superglobals, meaning they are available in all scopes. So it is not strictly necessary to pass them as parameters to functions.
However, there is a benefit in passing them as parameters when you start unit testing. Parameters will make it considerably easier to test values to the function without needing them to be available in $_SESSION or $_COOKIE.
You dont need to pass the $_SESSION or $_COOKIE variables as they are superglobals - accessible from anywhere ... from the docs :
This is a 'superglobal', or automatic global, variable. This simply means that it is available in all scopes throughout a script. There is no need to do global $variable; to access it within functions or methods.

Feedback on a session storage class design

I have a session class that basicly just sets and retrieves session variables,
the reason I made it was so I could easily change it to use sessions or something
like memcache to set the items and have them accessible on multiple pages without hitting the database
I then have this user class which uses the session object to get session variables in it.
I am wanting to add to this user class though, to make it more encapsulated I would like to be able to set the variables that I am retrieving in this class
so right now I can display the userid with $user->userid; I would like to first have a method or something that sets its value from the session object I guess
Does this sound lke a good idea or possibly a lot of overhead?
And if what I am trying to do is a good idea maybe you could suggest/show example of how I should do it? I am thinking that if I add that method in that possibly I should move the code in the __construct method into it's own method
Basicly, I have the variables listed in the top part of the class that are used in the construct method, if I have multiple methods in the class though would I need to set them all at the top like that?
<?PHP
//user.class.php file
class User
{
public $userid;
public $name;
public $pic_url;
public $gender;
public $user_role;
public $location_lat;
public $location_long;
public $newuser;
function __construct()
{
global $session;
if($session->get('auto_id') != ''){
//set user vars on every page load
$this->userid = $session->get('auto_id'); //user id number
$this->name = $session->get('disp_name');
$this->pic_url = $session->get('pic_url');
$this->gender = $session->get('gender');
$this->user_role = $session->get('user_role');
$this->location_lat = $session->get('lat');
$this->location_long = $session->get('long');
$this->newuser = $session->get('newregister');
}else{
return false;
}
}
}
//with the class above I can easily show some user variables I have saved into a session like this below
$user = new user();
$user->userid;
?>
In general your idea is a good one
3 things I would do differently:
1) In your implementation doesn't seem to consider having several users. ie Several instances of the same class.
2) I would use factories instead of using IF in the constructor.
So for a user you have saved in the session you would call:
$savedUser = User::fromSession($userId);
for a new user
$user = new User()
3) Use the serialize and unserialze functions to save that data to the session
Then your class could could be implemented as
public static function fromSession($userId) {
return unserialize($session->get('users_'.$userId));
}
public function save() {
return $session->set('users_'.$this->id , serialize($this));
}
I guess this is vaguely an answer to the "is this a good idea" question. In my understanding, locating variables in the session versus refreshing them from the database is a question of the trade off between complex queries and deserializing data. The session data isn't a free magic cache that escapes database calls, it is just a convenient wrapper around a database call that you don't have to deal with. Any variable that you place in the session must be serializable. The whole collection of serialized data is then managed; the server fetches the data using the session key, deserializes it all, and hands it to the php script. Then when it closes the session for that request-response cycle it serializes it all and puts it back in the db.
So the mess in dealing with all that can, in some cases, be worse than the mess of just opening a connection and asking the db for the same stuff (or a subset of stuff) directly.
I would say that putting one or two key values in the session is a good stopping place, and relying on it too heavily for statefulness is a less-optimal plan.
I would set a new session with a name like "ValuesInSession" to true or false depending on whether or not you have session values for the fields in your user class. Then, in the sessions\users class you can check whether this session is true or false and set your values accordingly (IE from the existing sessions or to empty strings\0)
EDIT: You could, alternatively to putting that code in the user or sessions class, write a new class which could work with your users class to set the values properly (perhaps it could extend the sessions class?)
I'm not sure I understand the question, however, if you are using php 5, you can use the __set magic method to help with this.
Modifying your current class:
class User
{
private $id;
private $data = array();
public function __construct()
{
global $session;
$this->id = $session->get('auto_id');
$this->data = array(
'disp_name'=>$session->get('disp_name'),
'pic_url'=>$session->get('pic_url'),
'gender'=>$session->get('gender'),
'user_role'=>$session->get('user_role'),
'lat'=>$session->get('lat'),
'long'=>$session->get('long'),
'newregister'=>$session->get('newregister')
);
}
// return the user id
public function id()
{
return $this->id;
}
// the __get magic method is called when trying to retrieve a value of a
// property that has not been defined.
public function __get($name)
{
if(array_key_exists($name, $this->data))
{
return $this->data[$name];
}
return null;
}
// the __set magic method is called when trying to store a value in a property
// that has not been defined.
public function __set($name, $value)
{
global $session;
// check if the key exists in the 'data' array.
// if so, set the value in the array as well as the session
if(array_key_exists($name, $this->data))
{
$this->data[$name] = $value;
$session->set($name, $value);
}
}
}
This way you can still get and set values the same as you were, but will also store the set the value in your session class.
To test this:
$user = new User;
if($user->id())
{
echo $user->disp_name;
$user->disp_name = 'new name';
echo $session->get('disp_name');
}
I would not suggest you that because:
It is not a good practice to select an architecture "in case of future need" ('the reason I made it was so I could easily change'). Check http://www.startuplessonslearned.com (Eric Ries) or http://highscalability.com articles
Your code is hard/impossible to test (See Misko Hevery's blog (A google evangelist) http://misko.hevery.com for further information).
You are using "global" (never a good idea if you want to keep track of the dependencies).
It is better to seperate "the business logic" (a User class) and the wiring/building (a factory class for example). (See http://en.wikipedia.org/wiki/Single_responsibility_principle and "separation of concerns")
For really good code examples (and to understand which OO laws should not be broken), I can advice you Misko's blog (Also do not miss his technical talks at google that you can find on youtube). I am sure you will love them.
Hope this helps.

Categories