Method Scope and Multiple Classes

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
User avatar
Weiry
Forum Contributor
Posts: 323
Joined: Wed Sep 09, 2009 5:55 am
Location: Australia

Method Scope and Multiple Classes

Post by Weiry »

HI again everyone, have a slight issue when working on and inheritance/method scope.

Lets assume i have 4 classes.
  1. VNcore
  2. VNdatabase
  3. VNxml
  4. UPIconnector
Now the construct of the situation i am in is as follows.

Code: Select all

UPIconnector extends VNxml{}
VNdatabase extends VNcore{}
VNdatabase has a public constructor which initialises the settings from VNcore, but all other methods are protected
UPIconnector makes use of the VNxml functions for working out stuff from xml inputs.

The time has come however, for UPIConnector to make use of the VNdatabase class. However because i have already extended another class, i can't extend the VNdatabase class too.
I also don't think that having the entire VNdatabase class constructed during the UPIconnector constructor and stored is a great way of doing things.
eg:

Code: Select all

UPIconnector extends VNxml{
    private $db;

    public function __Construct(VNdatabase $_db){
        $this->db = $_db;
    }
}
I had thought about possibly making the methods inside of VNdatabase static, however because the database connection is not initialised until the VNdatabase constructor is called, this would mean that any static function wouldn't work at all, both because the database connection was never initialise and if the class was initialised, then the static methods are no longer available in the same scope.

This really leaves me with the only option that i can see currently, make all the functions in the VNdatabase class public. But i didn't want to expose the database connector.

ideas?
User avatar
requinix
Spammer :|
Posts: 6617
Joined: Wed Oct 15, 2008 2:35 am
Location: WA, USA

Re: Method Scope and Multiple Classes

Post by requinix »

There's a few things you can do. Here are some.

A singleton, if you need to restrict the number of instances of the class (eg, to just one).

Code: Select all

private static $_instance;

public static function getInstance() {
	if (self::$_instance == null) self::$_instance = new self();
	return self::$_instance;
}
A factory method, if you don't (and this is preferable to a singleton).

Code: Select all

public static function factory() {
	return new self();
}
A registry.

Code: Select all

class Registry {

	public static $db;
	
	private function __construct() { }
	
}

Registry::$db = new VNdatabase();
(I prefer a dedicated $db variable for something like a database; other stuff would get the typical ::get() and ::set() treatment.)

Then you can mix pieces together, like a registry which exposes a factory method to get a database instance.

Code: Select all

class VNdatabase extends VNcore {
	
	public static function factory() {
		return new self();
	}
	
}

class Registry {

	private static $_db_method;
	
	private function __construct() { }
	
	public static function getDB() {
		if (self::$_db_method == null) throw new Exception("No database factory method defined.");
		return call_user_func(self::$_db_method);
	}
	
	public static function setDB($factory) {
		if (!is_callable($factory)) throw new Exception("\$factory is not a callback.");
		self::$_db_method = $factory;
	}
	
}

Registry::setDB("VNdatabase::factory");
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Method Scope and Multiple Classes

Post by Christopher »

Weiry wrote:I also don't think that having the entire VNdatabase class constructed during the UPIconnector constructor and stored is a great way of doing things.
Actually, I think injecting the dependency may be a good idea. Or instantiating them internally. I am not sure you want inheritance at all here. Your parent classes have all protected methods which means they would be better as composited objects. They are not extended at all. They are just utility objects used for their specific functionality. That should be encapsulated. Composition is usually preferable to inheritance.
(#10850)
User avatar
Weiry
Forum Contributor
Posts: 323
Joined: Wed Sep 09, 2009 5:55 am
Location: Australia

Re: Method Scope and Multiple Classes

Post by Weiry »

@requinix ~ I really like the idea of using the mixed method for the Regsitry and factory as this could be used for a much larger scale in obtaining specific objects securely.
Though because i am unfamiliar with this method, assuming that the methods within VNdatabase are still protected, once you retrieve the fully constructed object through "Registry::setDB("VNdatabase::factory");", aren't the methods still protected making them inaccessible after using the "Registry::getDB()" or even simpler, using $this->db-> inside the Registry class?

@Christopher ~ The only reason i am avoiding injecting the dependancy is so that i don't run into construction problems later where specific classes MUST be constructed before other classes. This really gives me headaches when you have 6 classes that each potentially require (for some unknown and hypothetical reason) to use methods from other classes.

---
I do think that the method that requinix suggests is possibly the more desirable solution, although from what i can tell, i will still have to make the methods in the VNdatabase class publicly accessible so that they can be called by any other class and if the registry is going to be a holding class, then i still have to pass the registry object itself into the class requiring it the database class. Leading me to my hypothetical point i made to Christopher.

However i think this could also require me to think about how i initialise everything as whole rather than individually which is not such a bad thing!

My only other question i suppose is:

say you have a function similar to sprintf() which takes:
string sprintf ( string $format [, mixed $args [, mixed $... ]] )

how do you create the [, mixed $... ] section of the function to allow for extra parameters?
x_mutatis_mutandis_x
Forum Contributor
Posts: 160
Joined: Tue Apr 17, 2012 12:57 pm

Re: Method Scope and Multiple Classes

Post by x_mutatis_mutandis_x »

The solution to your original problem is very simple.... a wrapper. Better yet a wrapper and an interface. What I mean is have an interface IUPIConnector. Then you can have:

Code: Select all

interface IUPIConnector {
      function function1();
      function function2();
      //...
}

Code: Select all

UPIConnectorXml extends VNXml implements IUPIConnector {
        //...
  
       
        public function function1() {
              //...
        }

       
        public function function2() {
              //...
        }


}

Code: Select all

class UPIConnectorDBWrapper extends VNDatabase implements IUPIConnector {
        /**
          * Wrapped
          * @var IUPIConnector
          */
        private $upiConnector;

        public function __construct(IUPIConnector $upiConnector, $config = array()) {
                parent::__construct($config); //$config can be your DB config etc..
                $this->upiConnector = $upiConnector;
        }

        public function function1() {
              //keep same implemenation of the wrapped instance (in your case the xml)
              $this->upiConnector->function1();
        }

        public function function2() {
              //override the implementation of wrapped instance (with a VNDatabase function call)
              parent::doSomething();
        }
}
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Method Scope and Multiple Classes

Post by Christopher »

Weiry wrote:@Christopher ~ The only reason i am avoiding injecting the dependancy is so that i don't run into construction problems later where specific classes MUST be constructed before other classes. This really gives me headaches when you have 6 classes that each potentially require (for some unknown and hypothetical reason) to use methods from other classes.
I actually thing you should just instantiate these other object internally, not inject them. Sorry, that was not clear from my post. Both your original solution and using patterns like Factory or Registry are much more complex that just instantiating these utility classes as objects inside the classes that use them.
(#10850)
x_mutatis_mutandis_x
Forum Contributor
Posts: 160
Joined: Tue Apr 17, 2012 12:57 pm

Re: Method Scope and Multiple Classes

Post by x_mutatis_mutandis_x »

Christopher wrote:
Weiry wrote:@Christopher ~ The only reason i am avoiding injecting the dependancy is so that i don't run into construction problems later where specific classes MUST be constructed before other classes. This really gives me headaches when you have 6 classes that each potentially require (for some unknown and hypothetical reason) to use methods from other classes.
I actually thing you should just instantiate these other object internally, not inject them. Sorry, that was not clear from my post. Both your original solution and using patterns like Factory or Registry are much more complex that just instantiating these utility classes as objects inside the classes that use them.
Strictly speaking factories provide a generic way to instantiate various objects following a contract (an interface/abstract class). Registries, on the other hand "bind/unbind" and "look up" references of one or many different types of objects. And I agree in this case, that neither of them is necessary.
User avatar
Weiry
Forum Contributor
Posts: 323
Joined: Wed Sep 09, 2009 5:55 am
Location: Australia

Re: Method Scope and Multiple Classes

Post by Weiry »

Well i think i may have come to a possible solution actually using a mix of ideas provided.

Code: Select all

require_once 'UPIConnectorDBWrapper.php';

$registry = new Settings();
$registry->dbsettings = array("db.database" => "chaf", "db.hostname" => "localhost", "db.username" => "chaf", "db.password" => "chaf");

$upi = new UPIConnectorDBWrapper(new UPIConnectorXml(),$registry);

print $upi->upi_login();
The set up looks a little more like this now, with more objects to help make things cleaner.

Code: Select all

abstract class store{} //simple registry abstract
interface IUPIConnector{}
class settings extends store{} //allows for specific functionality such as creating a singleton registry.
class VNdatabase{}
class UPIConnectorXml{}
class UPIConnectorDBWrapper extends VNDatabase implements IUPIConnector{}
I have (currently) removed the need for VNcore which originally looked like this where the settings were stored in an array and could be individually called if needed. Although the only thing is it is no longer secured by the fact the it was a protected function.

Code: Select all

class VNcore{
    protected function getSettings($val = NULL){}
}
I am thinking however though i could in fact implement a singleton registry object to specifically contain the database information so that it can only be used by a single database connector.
Scratch that, i realised that if i in fact did make it a singleton registry, then any other class which makes use of the database settings object may run into problems where they cant access it all at once, causing the program to crash :S

And here is an example implementation of a UPIConnectorDBWrapper function which implements both a query and the use of the UPIConnectorXml class.

Code: Select all

        public function upi_login() {
	    //example implementation of database
	    $q = $this->query("select * from mytable where id = '1'");
	    while($r = $this->fetch("assoc")){
		$row[] = $r;
	    }
	    
            return $this->upiConnector->upi_login($row);
	    
        }
The only change that i had to make was in the constructor of UPIConnectorDBWrapper where x_mutatis_mutandis_x suggested i use, because i noticed that i was having to construct the IUPIConnector and pass that in instead of the the actual UPIConnectorXml which is where all the actual UPI functions would have been.

Code: Select all

public function __construct(IUPIConnector $upiConnector, $config = array()) {} //suggested
public function __construct(UPIConnectorXml $upiConnector, Settings $config) {} //actual
What is everyone's thoughts about this new set up? I think it has the potential to be a lot cleaner and manageable.
x_mutatis_mutandis_x
Forum Contributor
Posts: 160
Joined: Tue Apr 17, 2012 12:57 pm

Re: Method Scope and Multiple Classes

Post by x_mutatis_mutandis_x »

You are good. $config being an array was just an example, was meant DB connection properties any way (which you can set it in a VO aswell).
Better to accept any IUPIConnector though, so tomorrow, if you have another implementation of IUPIConnector that needs to be wrapped to utilize DB access, instead of having another wrapper class, you can have

Code: Select all

public function __construct(IUPIConnector $connector, Settings $config) {} //UPIConnectorDBWrapper
$connector = new UPIConnectorDBWrapper(new UPIConnectorSomeOtherImpl(), $registry); //some other function
User avatar
Weiry
Forum Contributor
Posts: 323
Joined: Wed Sep 09, 2009 5:55 am
Location: Australia

Re: Method Scope and Multiple Classes

Post by Weiry »

x_mutatis_mutandis_x wrote:Better to accept any IUPIConnector though, so tomorrow, if you have another implementation of IUPIConnector that needs to be wrapped to utilize DB access, instead of having another wrapper class, you can have
Well i guess there are two points here, first that i know there wont be more than a single UPIConnector (just because of the nature of the program in this particular case)
And second, how does PHP feel for requiring the interface (IUPIConnctor) into that constructor rather than the object with the actual methods (UPIConnectorXml)?

Because (i making an assumption here, and its very late here currently so i may not be seeing this now) doesnt an interface only provide you with the abstract methods which must be implemented and in fact not usable functions?
So why would you pass an interface object into the class which requires the fully constructed methods that IUPIConnecter defines?

maybe im missing something here :)

will have to check back in the morning when my head is fresh and not struggling to keep my eyes open!
x_mutatis_mutandis_x
Forum Contributor
Posts: 160
Joined: Tue Apr 17, 2012 12:57 pm

Re: Method Scope and Multiple Classes

Post by x_mutatis_mutandis_x »

Weiry wrote:
x_mutatis_mutandis_x wrote:Better to accept any IUPIConnector though, so tomorrow, if you have another implementation of IUPIConnector that needs to be wrapped to utilize DB access, instead of having another wrapper class, you can have
Well i guess there are two points here, first that i know there wont be more than a single UPIConnector (just because of the nature of the program in this particular case)
And second, how does PHP feel for requiring the interface (IUPIConnctor) into that constructor rather than the object with the actual methods (UPIConnectorXml)?

Because (i making an assumption here, and its very late here currently so i may not be seeing this now) doesnt an interface only provide you with the abstract methods which must be implemented and in fact not usable functions?
So why would you pass an interface object into the class which requires the fully constructed methods that IUPIConnecter defines?

maybe im missing something here :)

will have to check back in the morning when my head is fresh and not struggling to keep my eyes open!
It's not an interface object (no such thing as interface object), its an object that implements an interface. Your constructor is just declared to accept any object that implements the interface IUPIConnector, and yes it should work since "UPIConnectorXml" implements IUPIConnector. [syntax]$object instanceof IUPIConnector[/syntax] will be true, where [syntax]$object = new UPIConnectorXml() //or new UPIConnectorDBWrapper()[/syntax]
Post Reply