How to make a variable be visible inside functions?

Hello,

How does one give a variable value outside a group of functions and for that variable to be available inside all these functions?

For example say:

$x1 = ‘this is my value’;

function y1 ()
{

echo $x1;

}

function y2 ()
{

echo $x1;

}

Regards,


$x1 = 'this is my value';

function y1()
{
  global $x1;
  echo $x1;
}

function y2()
{
  global $x1;
  echo $x1;
}

However, using global is not very elegant (or even considered poor style by some). You could better use:


$x1 = 'this is my value';

function y1($x1)
{
  echo $x1;
}

function y2($x1)
{
  echo $x1;
}

y1($x1);
y2($x1);

Hi, you will need to use ‘global’ keyword:


$x1 = 'this is my value';

function y1 ()
{
  global $x1;

  echo $x1;
}

function y2 ()
{
  global $x1;

  echo $x1;
}

More on this topic: http://uk2.php.net/manual/en/language.variables.scope.php

Personally, I would go with ScallioXTX’s non-global suggestion where possible

Heck, if both functions use a single variable source, an object-based approach would make more sense.

That is, if you feel like delving into OOP.

There has been a couple of threads lately where global would of been the direct answer( like in this case ) and was thankfully omitted. I was hoping the keyword had become the dirty little secret no-one must talk about, like using magic quotes and relying on register global variables.

Saying use a global can be make or break in some interview situations, very high chance of instant no hire with anyone who cares for software design. It is pretty low on the totem pole of all possible solutions due to its horrendous scope annihilation and real high chance of real maintenance/ software evolution nightmares.

The good thing about software development is it can make you really pay if you are lazy.
The bad thing about software development is it can make others really really pay if you are lazy.

Please don’t use global, kittens amd puppies die. At least with GOTO velociraptors eat the culprit quickly diminishing the gene pool and reducing the global damage that individual may of done :wink:

The problems of using global is a real learning experience. It is also a dirty experience. If you are using them the chances are that usage is less than ideal. There is something better, maybe harder work in the short term but nicer to everyone in the long term.

Homework :slight_smile:

http://en.wikipedia.org/wiki/Action_at_distance_(computer_science)
http://c2.com/cgi/wiki?GlobalVariablesAreBad

Now whether you should use OOP is a whole other big discussion( one I vote for but I am very biased on that one, a bit like asking a cat if chasing small shiny things is worth doing )

In short ScallioXTX’s answer is the one to go for.

Scallio, what is your view on the use of the globals when the variable is a database object?

Then it’s still bad. Functions shouldn’t rely on the global scope to do anything for them. Although I must admit I too have been found guilty of using it in the past :rolleyes:

Later I created a static class which can hold instances of objects, and return it to me when I want to.


class InstanceHolder
{
   private static $_instances;

   public function getInstance($name, &$var)
   {
      self::$_instances[$name] = $var;
   }

   public function getInstance($name)
   {
      if (isset(self::$_instances[$name]))
         return self::$_instances[$name];
      return null; // or throw an exception if you like
   }
}

And then in the code where-ever you create the database object you do it like


$db = new MyDatabaseConnectionClass(...);
InstanceHolder::setInstance('DB', $db);

And then in functions you can use


function y()
{
  $db = InstanceHolder::getInstance('DB');
}

An other approach would be Dependency Injection but I’m not really a fan of that.

Scallio, would you create a new instance of the InstanceHolder object that needs it for or would you pass the one instance around (and by what method)?

It’s a static class, so you don’t need to create an instance, it just exists.


include('InstanceHolder.php');
include('MyDatabaseConnectionClass.php');

$db = new MyDatabaseConnectionClass(...);
InstanceHolder::setInstance('DB', $db); // note that InstanceHolder is not instantiated, but this works

As you can’t create instances, the answer to your question is no, you don’t create a new InstanceHolder for each instance of different types you want to hold, you stuff all instances in the single InstanceHolder.
Since this is static class you can call it anywhere in your code: in the global scope, in functions, and also in other classes, so there is no need to pass it around.

This is actually known as the Multiton Pattern.
Note in the article above they also implemented the Factory Method Pattern, which is not something you have to do.

Scallio,


class InstanceHolder
{
   private static $_instances;

   public function getInstance($name, &$var)
   {
      self::$_instances[$name] = $var;
   }

   public function getInstance($name)
   {
      if (isset(self::$_instances[$name]))
         return self::$_instances[$name];
      return null; // or throw an exception if you like
   }
}

Scallio, I’m guessing that you normally have it coded a bit different as the above gives "Fatal error: Cannot redeclare InstanceHolder::getInstance()"

I commented out first one.


$db = new MyDatabaseConnectionClass(...);
InstanceHolder::setInstance('DB', $db);

Is there a setInstance function that goes with that? This is a quick one I put together:

   static function setInstance($name,$object) {
       $_instances['$name'] = $object;
   }

When it is used in a class to grab for example the database object for use as a member object. Also in the first one, in the line:

public function getInstance($name, &$var)

The use of & is depreciated.

class foo {
    
      function __construct() {
          $this->db = InstanceHolder::getInstance('DB');
          
          var_dump($this->db);          
      }
  }

It throws up an error:

Strict Standards: Non-static method InstanceHolder::getInstance() should not be called statically, assuming $this from incompatible context

Also the database object is not loaded.

You’re right, that wasn’t the exact code I was using. This is the correct code:


<?php
class InstanceHolder
{
  /** @var array $_instances An array of object instances */
  private static $_instances;
  		
  /**
  * Set an object instance for this class
  * @param string $name Name of the object instance
  * @param pointer $instance Pointer to the object instance
  */
  public static function setInstance($name, &$instance)
  {
    self::$_instances[$name] = $instance;
  }
		
  /**
  * Get an instance from this class
  * @param string $name Name of the instance to get
  * @return pointer Pointer to the requested object instance
  */
  public static function getInstance($name)
  {
    if (isset(self::$_instances[$name]))
      return self::$_instances[$name];
    else
      throw new Exception('No instance found by the name of "'.$name.'"');
  }
}

That should work for you. Any question let me know :slight_smile:
PS. AFAIK &$var in the function definition is still allowed (not deprecated).

Personally, Dependency Injection is the way to go. Singleton registries have their benefits, however I have problems with their limitations.

Here’s how I’d do that with dependency injection:

<?php
class Application{
	public $Database;
	protected $Settings;
	public function getSetting($SettingName){
		return $Settings->get($SettingName);
	}
	public function setSetting($SettingName, $SettingValue){
		return $Settings->set($SettingName, $SettingValue);
	}
	
	//Static:
	protected static $DefaultInstance = null;
	public static function getDefault(){
		if(is_null($DefaultInstance)){
			$Application = new Application();
			$Application->Database = Database::getDefault();
			$Application->Settings = Settings::getDefault();
			self::$DefaultInstance = $Application;
		}
		return self::$DefaultInstance;
	}
}
class Database extends PDO{
	public function Construct($Host, $Database, $User, $Password){
		parent::__Construct("mysql:host={$Host};dbname={$Database}", $User, $Password);
	}
	//insert some useful functions here...
	
	//Static:
	protected static $DefaultInstance = null;
	public static function getDefault(){
		if(is_null($DefaultInstance)){
			self::$DefaultInstance = new Database('localhost', 'databasename', 'someuser123', 'pass123'); //would actually come from setting file, but for examples sake it's hardcoded here
		}
		return self::$DefaultInstance;
	}
}
class Settings{
	protected $SettingsArray = array();
	public function get($Name){
	    return $this->exists($Name) ? $this->SettingsArray[$Name] : false;
	}
	public function exists($Name){
	    return array_key_exists($Name, $this->SettingsArray);
	}
	public function set($Name, $Value){
		$this->SettingsArray[$Name] = $Value;
	}
	protected static $DefaultInstance = null;
	public static function getDefault(){
		if(is_null($DefaultInstance)){
			$Settings = new Settings();
			//load settings from a file or something
			self::$DefaultInstance = $Settings;
		}
		return self::$DefaultInstance;
	}
}

By passing the application class to any function or object’s constructor, you can then access anything you need - whilst maintaining enough flexibility to allow a branch of objects to have different settings etc.

The main benefit is that you can create an inner-application with spoofed information - for example, your application could create a new application and inject post information; the sub-application would then act as if the user had posted that information and run completely separate from the main application.

Though I have the feeling that, due to the nature of the original question, most of the last few posts may be way over the @OP’s head, so let’s try to keep this less…intimidating :stuck_out_tongue:

That’s exactly my problem with DI, it feels like using a God object.

Agreed :rofl:

A singleton register is a god object too - the only difference is it’s an all-powerful god object that doesn’t allow other god-objects :wink:

With DI the only difference is that you can use multiple ‘registers’ (the Application class), which means you can layer your application by creating sub-applications to do different parts of the work - delegation.

Thanks for all the replies.
In the end I decided that the cleanest thing was to put these variables in a separate file in various functions inside this file and whenever these values were needed then I would include this file inside the function that needed them and make a call to the function which would then return these values as effective Global values.

Sounds like you should use define() instead.


define('SOME_VAR', 5);

function x()
{
  return SOME_VAR * SOME_VAR;
}

Defines are available everywhere.
The “drawback” of define is that once you’ve defined something you can’t redefine it, but from what I read in your post that’s exactly what you want, isn’t it?

There is a slight variation on your “functions in a file” you might want to consider “methods in a class”.

Some settings are cross-domain (for a CMS) so I wanted to enforce as much as possible that a class simply must implement all the functions as laid out in an Interface.

You can then make those methods static, one of the big differences is that if you name your class descriptively when you call the methods you have some idea where the hell they are appearing from.

I don’t flag this info as best practice or anything, merely the next rung on the ladder in my particular case.

Your situation may well be completely different, but take a looksee anyway.

What do you mean that:
“Defines are available everywhere.”

I mean if I declare a value via define in file x1.php how is that value
going to be visible to a function in y1.php?

If you answer that a variable given value via a define in x1.php is visible to y1.php then my head is going to blow up :slight_smile:
since that would mean Php is working totally different than I thought all along, that is a Php file is not loaded/executed and its values taken into memory until it is either called or included via include.

I mean defines are available in functions and class methods without having to use “global”.
The value is visible in y1.php if you include() x1.php in y1.php :slight_smile: