SitePoint Sponsor

User Tag List

Results 1 to 24 of 24
  1. #1
    SitePoint Member
    Join Date
    May 2005
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Question about class/core design

    I've tested a model that is based on a super class.

    You can call it $core. The core has instances of all other classes, example:

    $core->error->error_functions
    $core->logging->logging_functions
    $core->sql->sql_functions

    $core is passed in the constructor of all my other classes.

    In one way this works quite good but I am starting to think that it only slows down my sites. The $core object is getting larger everytime I make a plugin/modul and its already very large :P.

    So my question is, are there any similar ways (easier/faster ways) to design but still be able to give classes access to each other in a nice way?

    (Hope this is in the right forum)

  2. #2
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

  3. #3
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    In which way do you think it slows things down ? If you don't create objects until they are actually used, there's no problem with your approach. It's normally referred to as a Service Locator. You could do something like :
    PHP Code:
    class Locator
    {
        function & 
    getErrorHandler() {
            if (!isset(
    $this->_errorHandler)) {
                
    $this->_errorHandler =& new ErrorHandler();
            }
            return 
    $this->_errorHandler;
        }

        function & 
    getLogger() {
            if (!isset(
    $this->_logger)) {
                
    $this->_logger=& new Logger();
            }
            return 
    $this->_logger;
        }
        
    // and so forth ...


  4. #4
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I wonder if it would be a good idea to allow 'request filters' to register services in a sort of pluggable service locator architecture. I will give it a whirl and see how it pans out.

  5. #5
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by b1ind
    I wonder if it would be a good idea to allow 'request filters' to register services in a sort of pluggable service locator architecture. I will give it a whirl and see how it pans out.
    I've used that approach for some things. Authentication could for example happen in a filter, which then sets the current user on the locator.

  6. #6
    SitePoint Member
    Join Date
    May 2005
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hmm if I understand correctly, will the Service Locator work like this:

    PHP Code:
    // index.php
    $locator= new Locator();
    $error $Locator->getErrorHandler();
    $error->dosomething(); 
    PHP Code:
    // errorclass.php
    class Error {
      public function 
    __construct()
      {
        
    $locator= new Locator();
        
    // And if I want to use a special class inside the error class i do:
        
    $myspecialclass $locator->getSpecialHandler();
        
    $myspecialclass ->dosomething();
      }

    Or am I way off?

    Must I do something special with the Locator class or will something I save in $myspecialclass in my Error class be saved if I use getSpecialHandler in index.php ? Or will these to be like two diffrent handlers/objects?

    I'm kinda new to this OO thinking.

  7. #7
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Better to pass the locator around in the constructor than to create more than one; that way you can also use it to store information.

  8. #8
    SitePoint Member
    Join Date
    May 2005
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    33degrees, okey

    By the way, what was those filters you talked about (b1ind & kyberfabrikken)? Could someone explain how it works?

  9. #9
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yep, the idea is to pass the locator around. It's kind of a local global (did that make sense?).

    A filter is a type of decorator. Watch this example :
    PHP Code:
    class MyController
    {
        function 
    execute($context) {
        }
    }
    class 
    LoadUserFilter
    {
        function 
    LoadUserFilter($next) {
            
    $this->next $next;
        }
        function 
    execute($context) {
            if (isset(
    $_SERVER['PHP_AUTH_USER'])) {
                
    $context->user User::LoadUser($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
            } else {
                
    $context->user = new AnonymousUser();
            }
            
    $this->next->execute($context);
        }
    }
    $controller = new LoadUserFilter(new MyController());
    $controller->execute(new Locator()); 
    The benefit is that you can add or remove filters without changing the interface of the original object (MyController).

  10. #10
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here is an example of how I do just about the same thing, though instead of Decorators, I use Intercepting Filters,

    http://www.sitepoint.com/forums/showthread.php?t=333957

    If you've any questions, I'll be around to answer them

  11. #11
    SitePoint Member
    Join Date
    May 2005
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    First of all, I tried understanding the filters but at the time they are just confusing. I need to focus and fully understand this Service Locator design

    So back to my locator troubles :P

    I've posted some code (check the bottom of the post). The code produce both echo's so the classes can communicate with each other. But which is best, to store $oLocator as a recursion or just store the needed classes. I've added comments in cError.php where I show both methods.

    Is this how Service Locators are suppose to work or am I totally wrong? It seems to work fine.

    With the code below I get the following printout from $oLocator.

    Code:
    Locator Object
    (
        [_loggingHandler] => cLogging Object
            (
                [logs] => Array
                    (
                        [0] => Log this please
                        [1] => Log this too
                    )
    
                [oLocator:private] => 
                [oLogging:private] => 
            )
    
        [_errorHandler] => cError Object
            (
                [oLocator:private] => Locator Object
     *RECURSION*
                [oLogging:private] => cLogging Object
                    (
                        [logs] => Array
                            (
                                [0] => Log this please
                                [1] => Log this too
                            )
    
                        [oLocator:private] => 
                        [oLogging:private] => 
                    )
    
            )
    
    )
    The Code:

    PHP Code:
    // Locator.php
    class Locator
    {
        public function & 
    getErrorHandler()
        {
            if (!isset(
    $this->_errorHandler))
            {
                
    $this->_errorHandler =& new cError(&$this);
            }
            return 
    $this->_errorHandler;
        }

        public function & 
    getLoggingHandler()
        {
            if (!isset(
    $this->_loggingHandler))
            {
                
    $this->_loggingHandler =& new cLogging(&$this);
            }
            return 
    $this->_loggingHandler;
        }

    PHP Code:
    // cError.php
    class cError
    {
        private 
    $oLocator;
        private 
    $oLogging;
        
        public function 
    __construct(&$oLocator)
        {
                    
    // RECURSION
            
    $this->oLocator $oLocator;

                    
    // Or save each class that is needed
            
    $this->oLogging $oLocator->getLoggingHandler();

            
    $this->oLogging->PrintLog('Log this please');
        }
        
        public function 
    PrintError($error)
        {
            echo 
    'Error: '.$error;
        }

    PHP Code:
    // cLogging.php
    class cLogging
    {
        public 
    $logs = array();
        
        public function 
    __construct(&$oLocator)
        {
        
        }
        
        public function 
    PrintLog($log)
        {
            
    $this->logs[] = $log;
            echo 
    'Log: '.$log;
        }

    PHP Code:
    // index.php
    $oLocator = new Locator();

    $oError $oLocator->getErrorHandler();

    $oLogging $oLocator->getLoggingHandler();

    $oLogging->PrintLog('Log this too'); 

  12. #12
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So you ask whether you should do like this :
    PHP Code:
    public function __construct($oLocator) {
        
    $this->oLocator $oLocator;

    Or like this :
    PHP Code:
    public function __construct($oLocator) {
        
    $this->oLogging $oLocator->getLoggingHandler();

    I don't think there's an obvious answer to that, except to mention the third option :
    PHP Code:
    public function __construct($oLoggingHandler) {
        
    $this->oLogging $oLoggingHandler;

    BTW. Since you are using PHP5, you don't need all thoose ampersands in your code - just remove them.

  13. #13
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    except to mention the third option
    I would have to agree with that point, but just to be sure that you enforce it...

    PHP Code:
    // ...
    public function __constructILogger $log_handler ) {
    $this -> log_handler $log_handler;

    That way your dependencies are based on an Interface, nothing more no?

  14. #14
    SitePoint Member
    Join Date
    May 2005
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Doh, you (kyberfabrikken) explained my problem with just a 1/3 of what I wrote :P

    The third solution works like the second solution, except that you have to specify them in the constructor?

    What if I write a class that needs errorhandling, logging, sql ...

    Isn't the second alternative better then? Or else I have to change both the constructor and the getHandler function everytime I feel the need to use a new outside-class?

    Dr Livingston, could you explain interfaces quick? How do they work and how do they get in the picture?


    (many questions :/)

  15. #15
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The third solution works like the second solution, except that you have to specify them in the constructor?
    If your class only requires logging facilities, then only pass in the logger, but if the class requires more, then by all means, pass in the Locator it's self, but only if you need to...

    With Interfaces, you develop to an Interface, and not the Implementation as the saying goes; In the case of the ILogger, you can contract that Interface to numerous implementations, such as an Xml Logger, or a Database Logger, or a File Logger - it doesn't matter which implementation you use, as the Interface specifies what is to be implemented...

    In the example I gave earlier, the class using the logger expects an object that complies with a given Interface, ie A logger, but it's not interested, or I should say, it doesn't know which kind of logger it is - it; the class in question that uses the logger, doesn't care.

    Here is something you may be interested in reading, which I've refered to in the past myself

    http://www.artima.com/lejava/article...rinciples.html Do note the Java twist to it though, there are slight differences between the PHP and Java

  16. #16
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Da_ToTe
    What if I write a class that needs errorhandling, logging, sql ...
    I wouldn't pass the locator in a constructor. While there is a golden rule of keeping your number of parameters in a function low (two or three being the painlevel), I would say that this doesn't apply to constructors.
    You should keep in mind, that constructors aren't happening as often in your code, and often it will happen inside some kind of factory anyway, so you won't need to do it in the utilizing code. The real force of the locator is when you pass dependencies in the calling function.

    Allow me to recap on the situation here;
    To sum up, you have different options, which all translates into how much flexibility (decoupling) you need vs. how much trouble you will allow for the programmer (yourself most likely).

    The least flexible is to create the object inside the calling function :
    PHP Code:
    class PersonGateway
    {
        function 
    getPersonById($id) {
            
    $connection = new Connection();
            return 
    $connection->query("SELECT FROM persons WHERE id = ".$id);
        }

    With this solution, you have the object type completely sealed inside your function.

    A step up is to create the object in the constructor, and make it a private member :
    PHP Code:
    class PersonGateway
    {
        function 
    PersonGateway() {
            
    $this->connection = new Connection();
        }
        function 
    getPersonById($id) {
            return 
    $this->connection->query("SELECT FROM persons WHERE id = ".$id);
        }

    The main advantage to this approach, is that you only create the object in one place, so if you need to change the connection, you'll only have to fix it in the constructor.
    This is a common solution, and it is usable under most circumstances. There is still a tight dependency on the used class though. If you need to change to a different connection-class, you'll have to edit your PersonGateway class (and all thoose other classes, which use connection). Not good.

    The next step up is to pass the object from outside, through the constructor :
    PHP Code:
    class PersonGateway
    {
        function 
    PersonGateway($connection) {
            
    $this->connection $connection;
        }
        function 
    getPersonById($id) {
            return 
    $this->connection->query("SELECT FROM persons WHERE id = ".$id);
        }

    This is much better, since you completely removed the dependency on the connection-object from your class. You can now change the connection-object without having to change anything in your class.
    This level of decoupling is good enough for most uses, and is scalable to a level of complexity which is rarely exceeded with the types of applications normally done in PHP.

    An even more flexible solution is to pass the dependencies through the calling function :
    PHP Code:
    class PersonGateway
    {
        function 
    getPersonById($connection$id) {
            return 
    $connection->query("SELECT FROM persons WHERE id = ".$id);
        }

    The advatage of this is that you can change the connection-object at runtime. So if you f.ex. had two databases, you could reuse the same gateway for both. (OK bad example, but you get the idea).

    At this point, the cure (decoupling) begin to be a real hazard for the programmer. In thoose above examples, I only have one dependency, but often your class will have several, so with the pass-in-function approach (the last one), you get some pretty hairy code. The solution is to use a form of dependency-injection. The by-far simplest is the servicelocator. Just to complement the examples above, this is how it would look :
    PHP Code:
    class PersonGateway
    {
        function 
    getPersonById($locator$id) {
            return 
    $locator->getConnection()->query("SELECT FROM persons WHERE id = ".$id);
        }


  17. #17
    SitePoint Member
    Join Date
    Jun 2005
    Posts
    7
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    Don't post here much, however I hope my input helps.

    I would approach this by placing all my common classes in a folder, lets say called 'common'. This folder could then be accessed by a CommonClasses class - which loads them in if needed.

    However, the following would be required:

    Common class file names must be the same as the class name itself, e.g. Logger.php would have to contain a class named Logger (class Logger {}).

    Now back to the CommonClasses class, simply include this in all files that need the common classes.


    PHP Code:
    include 'CommonClasses.php';
    $core $this->CommonClasses->use(array('Logger','Error','DB')); 
    CommonClasses.php
    PHP Code:
    Class CommonClasses 
    {
        var 
    $classes = array();

        function 
    __construct()
        {
            
    $dir 'common/';

            if (
    is_dir($dir)) {
                    if (
    $dh opendir($dir)) {
                            while ((
    $file readdir($dh)) !== false) {
                                
    $this->classes[] = substr($file04);
                            }
                            
    closedir($dh);
                    }
            }
        }

        function use(
    $classes=array())
        {
            foreach(
    $classes as $class)
            {
                if(
    in_array($class$this->classes))
                {
                    include 
    $class 'php';    
                    
    $this->$class = new $class;
                }
            }
        }

    This should provide you with a flexible way to include what common classes you need in a specific class.

    This has no error handling, and I haven't even tested it. I borrowed the DIR file recursion from php.net/opendir docs.

    There are no doubt a few errors, but I hope this helps.

    Thanks,
    Chris

  18. #18
    SitePoint Member
    Join Date
    Jan 2006
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What about Registry instead of passing the $locator around all the time?

  19. #19
    SitePoint Member
    Join Date
    May 2005
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Blameless
    What about Registry instead of passing the $locator around all the time?
    You want to elaborate on that? I don't know what Registry is :P

    kyberfabrikken, i must say, very nice examples, I guess I missunderstood the role of a locator and I can see how the very last example is the simpliest and the best(?). I'm gonna try your examples a bit and play around with them

    chris.partridge, not sure if thats what im looking for, but thanks for your input.

    Dr Livingston, thanks for the explanation and the link.

  20. #20
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Blameless
    What about Registry instead of passing the $locator around all the time?
    I'd say that a locator is a type of Registry. You're probably referring to having a static registry, aren't you ? (As opposed to passing the instance around). You could make the locator static, but I wouldn't - it makes it imposible to switch it out at runtime, in case you ever need to have more than one through the same process. That may be mostly an academical issue, but it is one that can potentially be hurtful later on.

  21. #21
    SitePoint Member
    Join Date
    May 2005
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I got a new problem now, but its linked to this so I continue this topic

    I got an ErrorHandler method (set_error_handler is used) in one of my classes. The problem is I want to log my errors but I can't send the $locator in the set_error_handler? Don't know if there is any good solution or should I just add the $locator as a class varible in the constructor so my ErrorHandler can access it?

    As it is now, I pass the $locator as an object to all methods that have dependencies as the example I got from kyberfabrikken.

  22. #22
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Da_ToTe
    I got a new problem now, but its linked to this so I continue this topic

    I got an ErrorHandler method (set_error_handler is used) in one of my classes. The problem is I want to log my errors but I can't send the $locator in the set_error_handler? Don't know if there is any good solution or should I just add the $locator as a class varible in the constructor so my ErrorHandler can access it?

    As it is now, I pass the $locator as an object to all methods that have dependencies as the example I got from kyberfabrikken.
    Can register a class method as the error handler, so that'll have $this scope available.
    PHP Code:
    class ErrorHandler
    {
    private 
    $locator;
    private 
    $previousHandler;

    function 
    __construct($locator
    {
         
    $this->locator $locator;
         
    $this->previousHandler set_error_handler(array(&$this'onError'));
    }
    function 
    __destruct()
    {
          if (
    $this->previousHandler !== FALSE)
                
    restore_error_handler();
    }
    function 
    onError($type$str$file$line)
    {
    $this->locator->doStuff();
    }



  23. #23
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Should your error handler know about the locator, and all it's complexities? No... What I am thinking, is what you need is some delegation; You would delegate your error handler, to your logger, rather than the logger, to the error handler

    I don't have a concrete example at the moment, but I'll see if I can knock up one for you...

  24. #24
    SitePoint Member
    Join Date
    May 2005
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PHP Code:
    try {
        throw new 
    Exception('My error.');
    }
    catch (
    Exception $e)
    {
        
    // And in CatchError I access the log functions with $oLocator.
        
    $oLocator->getErrorHandler()->CatchError($oLocator$e);

    Thats how I do it atm. It works for try and catch errors but with set_error_handler errors I can't do it as the error is sent to a function directly.

    Dr Livingston, if you have time I would really appreciate some example code


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •