configuration variables passed to objects

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post by Kieran Huggins »

There may be issues with this method, but it works for me, and I do love me some chaining.

Code: Select all

class testClass{

	private $config = array();

	// if you want to do the single line thing
	public function inst(){
		return new testClass;
	}

	// could use __call() for flexibility
	public function color($var=null){
		if (is_null($var)){
			return $this->config['color'];
		}else{
			$this->config['color'] = $var;
			return $this;
		}
	}

}

// the long way
$t = new testClass;
$t = $t->color('blue')->color();
print_r($t);

echo '<hr/>';

// the single line way
$t = testClass::inst()->color('blue')->color();
print_r($t);
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

A few thoughts. Once you use a lot of singletons you'll discover passing them around gets ever more complex if their numbers increase. Also a configuration object need not be a single - I have more then one config object floating around in my projects. You might look at implementing a simple Registry Pattern (which itself can be singleton object/ArrayAccess) which tends to be the final preferable solution in many cases.

Code: Select all

class Foo {

    protected $_configuration = null;

    public function __construct(array $registry) {
        $this->_configuration = $registry['config'];
    }

}
I think it's worth being careful to minimise the level of configuration objects need. It's usually neater to have only a handful of objects (other than atypical application-level code) know about the configuration, and for the rest the system to query these knowledgeable objects independently. For example, any class can be created by querying a Factory class responsible for its creation (only the Factory is aware of any config data, the created object has it's own separate setters/getters but does not encapsulate the config object to limit exposure of the overall system to singletons/variable objects).

One reason here, is that config data can become so pervasive in an application, and so overused, that you get a huge amount of coupling. What if I decide to rename config option FOO to BAR? How many classes in the app would need to be edited? If the answer looks like "more than a few" then it's a sign of that pervasive coupling.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Post by Mordred »

Since PHP has several inadequacies in its syntax, I have found that simply using a global function serves both my taste for shortness and functionality:

require_once('config.inc.php');

function DoStuff() {
$a = GetConfig('value', 'default');
echo $a;
}

No need for global, $_GLOBAL, Registry::GetInstance(), $this->m_config, etc.
If you are writing a library, you could use a static function Library::GetConfig() to avoid namespacing problems.
For unit tests you can define your own per-test GetConfig() that returns fixed values.

Too much OOP sugar leads to OOP caries I think ;)
User avatar
nathanr
Forum Contributor
Posts: 200
Joined: Wed Jun 07, 2006 5:46 pm

Post by nathanr »

I may aswell through this one in here then..

Code: Select all

<?php
class core {
	
	public static $core = array();
	public $variables;
	
	/* self saving object */
	public static function saveCore(&$core) {
		self::$core = $core;
	}
	public static function getCore() {
		return self::$core;
	}
	
	public function __construct($variables) {
		self::saveCore($this);
		$this->variables = $variables['GLOBALS'];
		unset($this->variables['GLOBALS']);
	}
	
}

/* breaks some rules but very useful : inside any class or function call extract(core()); and you have every object and variable you could need */
function core() {
/* return the core object */
	$core = core::getCore();
	/*  along with all root level variables */
	foreach($core->variables as $varName => $var) {
		/* skip the superglobals */
		if(substr($varName, 0, 1) != '_') {
			$$varName = $var;
			$compactString[] = $varName;
		}
	}
	/* compact everything for extraction at the other side */
	return compact($compactString);
}
$core = new core(get_defined_vars());
?>
and how to use it..

Code: Select all

<?php
/* set a variable */
$variable_a = 'testing';

/* test if we can access it from a function */
function testCore() {
	extract(core());
	echo $variable_a."\n\n";
}
echo 'root variable accessed from a function'."\n";
testCore();

/* test if we can access it from a class */
class egg {
	public $classVar;
	
	public function __construct() {
		extract(core());
		$this->classVar = $variable_a;
	}
}
$egg = new egg;
echo "\n".'root variable accessed from an object'."\n";
print_r($egg);

/* test if we can access it in our new egg object from anywhere */
function checkEgg() {
	extract(core());
	print_r($egg);
}
echo "\n".'object accessed from a function'."\n";
checkEgg();

/* change a variable in egg object and see if it's updated */
echo "\n".'object variable changed from root level + object accessed from a function'."\n";
$egg->classVar = 'a new string';
checkEgg();
?>
makes my life a lot easier I tell you.. and i have a pet hate for the word "global" in php!

and I suppose the best bit is that get_defined_vars() passes references, therefore you ahve references to ever changing variables stored statically in an object :)

:?
User avatar
kyberfabrikken
Forum Commoner
Posts: 84
Joined: Tue Jul 20, 2004 10:27 am

Post by kyberfabrikken »

nathanr wrote: makes my life a lot easier I tell you.. and i have a pet hate for the word "global" in php!
Wow -- You managed introduce global variables into PHP. That's really scary ...
fredrik
Forum Newbie
Posts: 13
Joined: Sun Nov 04, 2007 4:41 am

Post by fredrik »

nathanr: Am I missing something or...

Code: Select all

<?php
$registry = array();
$registry['foo'] = new Foo;

function myDemoFunc() {
    global $registry;
    extract($registry);
}
...
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

kyberfabrikken wrote:
nathanr wrote: makes my life a lot easier I tell you.. and i have a pet hate for the word "global" in php!
Wow -- You managed introduce global variables into PHP. That's really scary ...
Ahhh! Run!!!! Godzilla!!!

Seriously, is it so hard to pass variables as parameters?
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Jcart wrote:
kyberfabrikken wrote:
nathanr wrote: makes my life a lot easier I tell you.. and i have a pet hate for the word "global" in php!
Wow -- You managed introduce global variables into PHP. That's really scary ...
Ahhh! Run!!!! Godzilla!!!

Seriously, is it so hard to pass variables as parameters?
That's fine with me. However (as mentioned in my injecting objects topic), I'm passing that one to a lot of methods, which seems redundant, which from what I know would defeat the purpose of OO?

I'm currently researching registries, and how they can help me in that circumstance.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Sorry you lost me.. can you give an example?
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

I meant, a lot of objects

Code: Select all

$userAuth = new userAuth(db::getInstance);

$formValidator = new formValidator(db::getInstance);

//etc...
And when dealing with extending classes, I have to pass the member to the extending object.

Code: Select all

class example
{
     private $_db;
     public function __construct($db) { $this->_db = $db; }
     public function someMethod() { $o = new someOtherClass(); $o->_db = $this->_db; }
}
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

scottayy wrote:I meant, a lot of objects

Code: Select all

$userAuth = new userAuth(db::getInstance);

$formValidator = new formValidator(db::getInstance);
What's wrong with this? Looks legitimate to me. By passing the value as a parameter you are creating a clear interface and clear dependencies. With globals it is not so obvious and you risk namespace clashing and dependency.
And when dealing with extending classes, I have to pass the member to the extending object.

Code: Select all

class example
{
     private $_db;
     public function __construct($db) { $this->_db = $db; }
     public function someMethod() { $o = new someOtherClass(); $o->_db = $this->_db; }
}
That smells like a poor design if you have to pass the parent variables from it's child. You are meant to be extending the functionality.

Code: Select all

class example extends foobar
{
   public function __construct($db) 
   {
      parent::__construct($db);
      var_dump($this->db);
   }
} 

class foobar
{
   protected $db;

   public function __construct($db) 
   {
      $this->db = $db;
   }
}
User avatar
nathanr
Forum Contributor
Posts: 200
Joined: Wed Jun 07, 2006 5:46 pm

Post by nathanr »

well yeah..

Code: Select all

<?php
class test {

     public function c() {
         // we got an error in this function so we want to debug it!
         global $var1;
         global $var2;
         global $var4;
         global $var5;
         global $var8;
         global $var9;
         global $var11;
         global $var13;
         //.......code
         print_r(get_defined_vars());
     }
}

$tester = new test;
$tester->c();
?>
or

Code: Select all

<?php
class test {

	public function a($var1, $var5, $var8, $var11, $var14) {

	}
   
	public function b($var1, $var2, $var4, $var5, $var8, $var9, $var11, $var13) {

	}
	
	public function c($var1, $var2, $var4, $var5, $var8, $var9, $var11, $var13) {
		print_r(get_defined_vars());
	}
}

$tester = new test;
$tester->c($var1, $var2, $var4, $var5, $var8, $var9, $var11, $var13);
?>
or

Code: Select all

<?php
/* define our registry and pass variables to it by reference */
$registry = array();
$registry['var1'] = &$var1;
$registry['var2'] = &$var2;
$registry['var3'] = &$var3;
$registry['var4'] = &$var4;
$registry['var5'] = &$var5;
$registry['var6'] = &$var6;
$registry['var7'] = &$var7;
$registry['var8'] = &$var8;
$registry['var9'] = &$var9;
$registry['var10'] = &$var10;
$registry['var11'] = &$var11;
$registry['var12'] = &$var12;
$registry['var13'] = &$var13;
$registry['var14'] = &$var14;
$registry['var15'] = &$var15;
$registry['var16'] = &$var16;

class test {

	public function a(&$registry) {
		
	}
   
	public function b(&$registry) {

	}
	
	public function c(&$registry) {
		print_r(get_defined_vars());
	}
}

$tester = new test;
$tester->c($registry);
?>
or

Code: Select all

<?php
/* define our registry and pass variables to it by reference */
$registry = array();
$registry['var1'] = &$var1;
$registry['var2'] = &$var2;
$registry['var3'] = &$var3;
$registry['var4'] = &$var4;
$registry['var5'] = &$var5;
$registry['var6'] = &$var6;
$registry['var7'] = &$var7;
$registry['var8'] = &$var8;
$registry['var9'] = &$var9;
$registry['var10'] = &$var10;
$registry['var11'] = &$var11;
$registry['var12'] = &$var12;
$registry['var13'] = &$var13;
$registry['var14'] = &$var14;
$registry['var15'] = &$var15;
$registry['var16'] = &$var16;

class test {

	public function a() {
		global $registry;
                extract($registry);
	}
   
	public function b() {
		global $registry;
                extract($registry);
	}
	
	public function c() {
		global $registry;
                extract($registry);
		print_r(get_defined_vars());
	}
}

$tester = new test;
$tester->c();
?>
or my method..

Code: Select all

<?php
/* define our registry and pass variables to it by reference */
$registry = new registry(get_defined_vars());

class test {

	public function a() {
                extract(registry());
	}
   
	public function b() {
                extract(registry());
	}
	
	public function c() {
                extract(registry());
		print_r(get_defined_vars());
	}
}

$tester = new test;
$tester->c();
?>
I know from coding everyday which one uses the least lines, is easiest to use, and never needs updated no matter how many new variables you need access too from all levels.. the only other thing that comes close to it (in my opinion) is:

Code: Select all

<?php
class test {

	public function c() {
                extract($GLOBALS);
		print_r(get_defined_vars());
	}
}

$tester = new test;
$tester->c();
?>
however if you have to debug with the print_r(get_defined_vars()); you'll soon see which one gives you the shortest tidiest print_r results - and purely to prove how much of a matter of personal preference this is.. in mine it unset($GLOBALS) - which means there are no globals available anymore (i know the technicalities involved and that's not strictly true.. however you can't $GLOBALS['var'] as $GLOBALS is undefined :) *joy*

also as if to compound my sadness there's just something that I like about saving variables by reference in a static variable within a self saving object *shrugs*
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

I'm not going to get into why globals are bad, the world has discussed this enough.

One thing I do want to discuss is why you think this the best fit. There is a ton of stuff I can see problematic

- Possible name space collisions (any global variable you use you must make sure
- Makes your objects reliant on the global namespace (ever heard of encapsulation?)
- Horrible debugging (having to track down where changes were made, accidental or not would be a hassle)
- Interface is not clear (we don't clearly know what components this object associates with)

I'll see if anyone else has anything to suggest. Now, again, I'm not saying you method isn't viable.. but I certainly won't let people advocate using global variables without the user hearing the other side of the story. :)
User avatar
nathanr
Forum Contributor
Posts: 200
Joined: Wed Jun 07, 2006 5:46 pm

Post by nathanr »

fair enough.. how about this then...?

Code: Select all

<?php

class db_mysql_connection {
	protected static $dbHandle;
	
	public function openConnection($connection) {
		if (!self::$dbHandle) {
			echo "opening a connection.."; # remove this for real use..
			self::$dbHandle = mysql_connect($connection['server'], $connection['user'], $connection['password'], true) or die('Could not connect: '.mysql_error());
			if(isset($connection['schema'])) {
				self::select_db($connection['schema']);
			}
		}
	}
	
	public function select_db($database_name) {
		mysql_select_db($database_name, self::$dbHandle) or die('Could not select db: '.mysql_error());
	}
	
	public function __destruct() {
		if(is_resource(self::$dbHandle)) {
			mysql_close(self::$dbHandle);
		}
	}
}

class db_mysql_interface extends db_mysql_connection {

	public function __construct($connection=false) {
		if($connection) {
			$this->openConnection($connection);
		}
	}
}

class db_mysql_handler extends db_mysql_interface {

	public function select($query) {
		$result = mysql_query($query, self::$dbHandle) or die('Query failed: ' . mysql_error());
		$result_rows = mysql_num_rows($result);
		if ($result_rows !== 0) {
			if($result_rows > 1) {
				for ($r=0;$r<$result_rows;$r++) {
				   $output[$r] = mysql_fetch_assoc($result);
				}
			} else {
				$output[0] = mysql_fetch_assoc($result);
			}
		} else {
			$output = false;
		}
		mysql_free_result($result);
		return $output;
	}
		
	public function insert($query) {
		$result = mysql_query($query, self::$dbHandle) or die('Query failed: ' . mysql_error());
		$last = mysql_insert_id();
		if ($last !== 0) {
			$output = $last;
		} else {
			$output = false;
		}
		return $output;
	}
	
	public function update($query) {
		$result = mysql_query($query, self::$dbHandle) or die('Query failed: ' . mysql_error());
	}
	
	public function delete($query) {
		$result = mysql_query($query, self::$dbHandle) or die('Query failed: ' . mysql_error());
	}
	
	public function magic_quote_trim($in) {
		if (get_magic_quotes_gpc()) {
		   $in = stripslashes($in);
	   }
	   return $in;
	}
	
	public function clean($in) {
		return mysql_real_escape_string($this->magic_quote_trim($in));
	}
	
}
$connection = array();
$connection['server'] = 'localhost';
$connection['user'] = 'user';
$connection['password'] = 'password';
$connection['schema'] = 'dbname';

$db = new db_mysql_handler($connection);
unset($connection);

function testConnection() {
	$db = new db_mysql_handler();
	print_r($db->select('show databases'));
}

testConnection();
?>
again.. works for me and saves me doing any of that instance gubbins..
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

No offense to anyone posting, but if anything, the discussion in here would turn me off to object oriented programming if I didn't have such a strong learning desire. I've stopped reading really, because I don't comprehend any of it. I'm a beginner at this, and these concepts are all way over my head.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
Post Reply