Page 2 of 2
Posted: Sat Oct 21, 2006 11:17 pm
by s.dot
this has also been a confusing aspect to me while learning and utilizing object oriented practices
doing this....
Code: Select all
require 'one.class.php';
require 'two.class.php';
require 'three.class.php';
$one = new one();
$two = new two();
$three = new three();
just feels
too "dirty" and I don't like doing it. creates overhead.
Posted: Sun Oct 22, 2006 12:10 am
by RobertGonzalez
I agree. As I started to put the project together I started to realize that I had a bunch of objects floating around. I thought that it would be nice to be able to have one main object that binds all the others together, so instead of:
Code: Select all
<?php
$error = new error();
$mysql = new MySQL();
$config = new Config();
?>
and using a bunch of different objects all over, I could instantiate them with a 'main' object and use them from that:
Code: Select all
<?php
$myapp = new MyApp;
$myapp->initialize();
// That method would set up all the objects
if (!$myapp->mysql->connected) {
$myapp->error->show_error($myapp->mysql->last_error_message);
}
?>
Posted: Sun Oct 22, 2006 8:52 am
by Maugrim_The_Reaper
Your initialisation is basically a bootstrap function where the classes are included and instantiated and stored for future use. I'm aware it's a limited and private implementation - but a few comments on the theory side...
1. It's not reuseable
2. It's aware of the class names
3. It instantiates everything - even if everything is not needed in all situations
4. It adds complex method usage - the extra ->mysql bit to all uses
5. It requires manual editing for external changes
6. It's heading for a Pattern you may not see yet...see below.
7. Add complex instantiation logic and it will turn into spaghetti
You could for example fit your classes into a PEAR like naming convention where the class name bears a relationship to the file's location. So you would have a Namespace_Error class - say Everah_Error stored in ./Everah/Error.php. Then have a Registry/ServiceLocator...
I have not tested the below - take with a pinch of salt and skepticism until it is...
Code: Select all
<?php
// set include path to include the path to Everah's parent directory. Not Everah itself though...
set_include_path(get_include_path() . PATH_SEPARATOR . '/path/to/Everah/parent/directory');
// Use static methods - i.e. globally accessible from all classes
class Everah_Locator
{
private static $registry = array();
// ...
public static function get($class)
{
// re: Class Name is the relative Path replacing / with _ (PEAR convention)
$path_components = explode('_', $class);
$class_path = implode(DIRECTORY_SEPARATOR, $path_components) . '.php';
$registry_key = end($path_components);
if(array_key_exists($registry_key ,self::$registry))
{
return self::$registry[$registry_key];
}
// else
require_once $class_path;
$object = new $class;
self::$registry[$registry_key] = $object;
return $object;
}
// ...
}
Code: Select all
// usage:
require '/path/to/Everah/Locator.php';
// Everah_Mysql is stored in the Mysql.php file at ./Everah/Mysql.php
$mysql = Everah_Locator::get('Everah_Mysql');
// you now have the Everah_Mysql object, which was required, instantiated and cached
// Further get('Everah_Mysql') calls will return the cached object reference
$mysql2 = Everah_Locator::get('Everah_Mysql');
// mysql and mysql2 contain a reference to the same object as cached in Locator
No guarantees this works, it's just illustrating a possible implementation of a static ServiceLocator/Registry where inclusion/initialisation is handled in addition to object caching on a lazy loading basis - i.e. only initialise when/if required. If the initialisation logic were more complex, one might include the ability to call a Factory method which handles such logic separately.
Notably, a ServiceLocator is a once off class, reuseable with the same PEAR convention elsewhere, and fairly simple to Unit Test. So we currently have:
- Singleton
- Registry
- Static ServiceLocator (doesn't have to be static - pass by parameter)
Other possibility is Dependency Injection - something I'm not very familiar with in PHP though implementations exist...
Posted: Sun Oct 22, 2006 9:00 am
by Maugrim_The_Reaper
viewtopic.php?p=274267
This topic seems related - I posted a summary of the related problems being raised here which looking back seems oddly concise for a rambling Irishman... Must have been unusually sober at the time

.
Posted: Sun Oct 22, 2006 3:59 pm
by RobertGonzalez
WOW! Thanks Maugrim. That gives me a ton to think about tomorrow when I go through my structure. I truly appreciate the time you put into the explanation. Thanks again.
Posted: Sun Oct 22, 2006 4:08 pm
by Christopher
scottayy wrote:doing this....
Code: Select all
require 'one.class.php';
require 'two.class.php';
require 'three.class.php';
$one = new one();
$two = new two();
$three = new three();
just feels
too "dirty" and I don't like doing it. creates overhead.
Why? If you will use those three classes it that context then that is the cleanest and most concise way to do it. I would like to know what feels 'dirty' about it?
Posted: Sun Oct 22, 2006 4:45 pm
by Maugrim_The_Reaper
WOW! Thanks Maugrim. That gives me a ton to think about tomorrow when I go through my structure. I truly appreciate the time you put into the explanation. Thanks again.
NP

. If it was food for thought, it was worth the time... I'd make several changes if I added a unit tested version - making the registry key's unique and not just the final term in the class name, refactoring the class loading logic into a distinct "load" method (for classes with static methods).
just feels too "dirty" and I don't like doing it. creates overhead.
What overhead? You just described the only method of instantiating an object...
Posted: Mon Oct 23, 2006 11:37 pm
by RobertGonzalez
Ok, lets see if my studying has paid off...
I build a registry class and essentially instantiate my objects within the registry. If I make the registry a singleton, I can actually call it from within my objects instead of passing my objects around from object to object and each object lends itself to the app overall.
Am I catching on?
Posted: Tue Oct 24, 2006 3:01 am
by Maugrim_The_Reaper
Getting there

.
I'd actually refer to the object as a "Service Locator". A Registry (having a debate in another thread on this) is largely a storage medium. In it's simplest form it's a glorified array. You can set values, retrieve values, etc. Of course a class format lends itself to Type Hinting limitations on parameters and other OOP benefits. A Service Locator is up the ladder a bit - not only does it cache, it also finds, includes and instantiates.
A Service Locator is commonly a Singleton.
A few things to watch for. A basic Service Locator has no trouble with instantiating unless the constructor of the new object require's parameters, or instantiating is only done on one Class based on some creational logic. In these complex cases the SL may need to fall back on some additional method to instantiate the "right" object - i.e.
Factory,
Abstract Factory, or other.
That's the additional fluff.

If what you have now works, and has some unit test support - then it's likely useful as it stands.
Posted: Tue Oct 24, 2006 8:14 am
by RobertGonzalez
What I am doing works, but I am not sure that it is the best way to go. The challenge that I am faced with right now is determining the correct pattern to apply so that all of the objects are instantiated correctly without being instantiated more than once (where applicable) and with the correct parameters. I have a few objects that take my MySQL database object, but I am really not wanting to make the connection details available application wide, so I have it set up to where the MySQL object instantiates with just a database name, called from the constructor within an initializers script (essentially one small page that calls all the objects that I need). The same thing holds true for the Sybase object and to a lesser degree, the template object.
I'll figure all this out eventually. Thanks for taking the time to make it more clear to me.
Posted: Tue Oct 24, 2006 10:19 am
by Jenk
That would be a Factory pattern, fo' sho'.
You'd then integrate it with a Registry, or god forbid a singleton.
With regards to dependency injection, it's now relatively simple with the introduction of the Reflection model in PHP's SPL. However, the problem lies in the implmentation of DI, not the technicalities of it.. if that makes sense.
Posted: Tue Oct 24, 2006 10:36 am
by Maugrim_The_Reaper
The Factory certainly seems applicable - also since a Factory is also a single class it can run off of a configuration value and not require a complicated setup - i.e. you could mangle the ServiceLocator to use it where appropriate, perhaps with a class constant to tell the SL that it needs to locate the factory and not the standalone class...
Posted: Tue Oct 24, 2006 10:58 am
by RobertGonzalez
Ok, here are the objects and the objects that drive them. They are necessary on every page. If you wouldn't mind, what do you suppose would be the best way to call them/user them in this app?
$dbsettings (singleton, but looking now like a registry)
$mysql (requires $dbsettings)
$sybase (requires $dbsettings)
$config (requires $mysql)
$pager (page handler, requires $mysql, $template)
$sessions (requires $mysql)
$user (requires $sybase)
$error
$template
Or should I scrap this entire layout and do something else?
Posted: Tue Oct 24, 2006 11:15 am
by Maugrim_The_Reaper
If I were to quickly take MySQL. It's dependent on the dbsetting object (presumably driven from a config file).
I could have a MySQL factory class (or a more generic DB Factory class if MySQL is one option of many). This takes in the dbsettings as a Singleton or from an already established Registry. It creates the MySQL object and returns a reference to the instance.
My Locator class would be of a form with a get() method with an optional parameter Locator::Factory constant. get('MySQL', Locator::Factory) would seek the Mysql_Factory class instead of the standalone Mysql class - the Factory would handle *all* steps required to instantiate a valid MySQL object. Same could apply to Sybase (use a Factory). All other classes have dependencies from here out which should be registered on the Locator - just use a Singleton call to the Locator to grab them, or pass the Locator as a standard parameter when instantiating.
Posted: Tue Oct 24, 2006 11:57 am
by RobertGonzalez
That's funny. I was already headed in that direction. Thanks for confirming what I thought might be appropriate Maugrim.