SitePoint Sponsor

User Tag List

Results 1 to 18 of 18
  1. #1
    SitePoint Member
    Join Date
    Jul 2012
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    OOP PHP Is it possible to call on a function from other classes

    I am a bit new to OOP although I have been coding procedural PHP for years.

    Here is what I am trying to figure out.

    Say I have several classes and I want to call on a function from one of those classes inside a function in the other classes. What is the best way to do this? Do I have to extend them all from the first one, that seems like it would get really confusing if I have 8-10 different classes.

    Below is a scenario of something similar to what I would like to do.

    PHP Code:
    class History
    {           
              public function 
    setHistory()
              {
                   
    $sql "insert into history (some columns here) values (some values here)";
                   
    $result mysql_query($sql);
              }
    }


    class 
    Tasks
    {
              public function 
    updateTask()
              {
                   
    $result mysql_query("update task set subject = "testing" where task_id = $this->task_id ");
                   
    $setHistory();   //TRYING TO CALL FUNCTION FROM OTHER CLASS
              
    }
    }


    class 
    Contact
    {
              public function 
    updateContact()
              {
                  
    $result mysql_query("update contact set first_name = "John" where contact_id = $this->contact_id ");
                  
    $setHistory();   //AGAIN IM TRYING TO CALL FUNCTION FROM OTHER CLASS
              
    }

    So I might want to put data into the history table from all the other classes but I don't really want to have to build a function inside each and every class that does the same thing.

    Any help would be very much appreciated.

  2. #2
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,156
    Mentioned
    14 Post(s)
    Tagged
    0 Thread(s)
    The simplest option (I'll show a better way in just a bit) is to create a global history object.

    PHP Code:
    $history = new History();

    class 
    Tasks 

              public function 
    updateTask() 
              { 
                   global 
    $history;
                   
                   
    $history->setHistory();
              } 

    The downside, though, is that by relying on a global variable, the Tasks class is tightly coupled to this particular application environment, and it's also harder to create different task objects that might have different histories. A better way is to inject a history object into your instantiated tasks object.

    PHP Code:
    class Tasks 

              private 
    $history;
              
              public function 
    __construct(History $history)
              {
                   
    // Tasks doesn't need to know where this history object came from or how it was created
                   // It only needs to know that this is the object it should use
                   
    $this->history $history;
              }
              
              public function 
    updateTask() 
              { 
                   
    $this->history->setHistory();
              } 


    $history = new History();

    $tasks = new Tasks($history); 
    "First make it work. Then make it better."

  3. #3
    SitePoint Wizard gRoberts's Avatar
    Join Date
    Oct 2004
    Location
    Birtley, UK
    Posts
    2,439
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    use call_user_func or call_user_func_array

    i.e.


    $instance = new History();
    $output = user_call_func(array($instance, 'setHistory'));


    If your setHistory accepted parameters, then you'd use:


    $instance = new History();
    $output = user_call_func(array($instance, 'setHistory'), $param1, $param2);


    http://php.net/manual/en/function.call-user-func.php


  4. #4
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    4,827
    Mentioned
    142 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Thewhistler_1 View Post
    I am a bit new to OOP although I have been coding procedural PHP for years.

    Here is what I am trying to figure out.

    Say I have several classes and I want to call on a function from one of those classes inside a function in the other classes. What is the best way to do this? Do I have to extend them all from the first one, that seems like it would get really confusing if I have 8-10 different classes.

    Below is a scenario of something similar to what I would like to do.

    PHP Code:
    class History
    {           
              public function 
    setHistory()
              {
                   
    $sql "insert into history (some columns here) values (some values here)";
                   
    $result mysql_query($sql);
              }
    }


    class 
    Tasks
    {
              public function 
    updateTask()
              {
                   
    $result mysql_query("update task set subject = "testing" where task_id = $this->task_id ");
                   
    $setHistory();   //TRYING TO CALL FUNCTION FROM OTHER CLASS
              
    }
    }


    class 
    Contact
    {
              public function 
    updateContact()
              {
                  
    $result mysql_query("update contact set first_name = "John" where contact_id = $this->contact_id ");
                  
    $setHistory();   //AGAIN IM TRYING TO CALL FUNCTION FROM OTHER CLASS
              
    }

    So I might want to put data into the history table from all the other classes but I don't really want to have to build a function inside each and every class that does the same thing.

    Any help would be very much appreciated.
    Here is a question for you. Would you ever want to call setHistory() by itself? Or will it only be called from other classes such as Tasks and Contacts?

    If you answer is No, you will never call it by itself and it will only be called by other classes such as Tasks and Contacts, then I recommend the following approach
    PHP Code:
    class History 
    {            
              protected function 
    setHistory() // Note I made this protected
              

                   
    $sql "insert into history (some columns here) values (some values here)"
                   
    $result mysql_query($sql); 
              } 



    class 
    Tasks  extends History // Extend History

              public function 
    updateTask() 
              { 
                   
    $result mysql_query("update task set subject = "testing" where task_id = $this->task_id "); 
                   
    // use $this to indicate you want to call a function that exists within the class or extended class
                   
    $this->setHistory();   
              } 



    class 
    Contact extends History

              public function 
    updateContact() 
              { 
                  
    $result mysql_query("update contact set first_name = "John" where contact_id = $this->contact_id "); 
                   
    // use $this to indicate you want to call a function that exists within the class or extended class
                  
    $this->setHistory();
              } 

    You can technically accomplish this as well if you DO plan to call History by itself and not through another class (such as Tasks/Contacts) by using the above code, but keeping setHistory as public instead of protected.

  5. #5
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,156
    Mentioned
    14 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    class Tasks extends History
    ...
    class Contact extends History
    I'm not sure this would pass the "is-a" test. Is it correct to say that a task "is-a" history? Or that a contact "is-a" history? Then I don't think inheritance is the right choice in this situation. I think it's more correct to say that a task "has-a" history, and that a contact "has-a" history.
    "First make it work. Then make it better."

  6. #6
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    4,827
    Mentioned
    142 Post(s)
    Tagged
    0 Thread(s)
    That is true, but I also don't see the History class as being a "true" history, it is more of a Log/Entry. A Task/Contact could be considered as an "Entry".

    Another aspect would to have Tasks and Contacts implement History, so they can self define how the history log is written, which may be even a better approach.

  7. #7
    SitePoint Member
    Join Date
    Jul 2012
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    That is true, but I also don't see the History class as being a "true" history, it is more of a Log/Entry. A Task/Contact could be considered as an "Entry".

    Another aspect would to have Tasks and Contacts implement History, so they can self define how the history log is written, which may be even a better approach.
    Thanks cpradio. This implement option you spoke of, can you elaborate on this a bit please? I haven't used it and I am not sure how to use it or what it does. Could you please show me how that would work, how it differs from "extends", and maybe provide an example?

    I really appreciate everyone's help on this.

  8. #8
    SitePoint Member
    Join Date
    Jul 2012
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by gRoberts View Post
    use call_user_func or call_user_func_array

    i.e.


    $instance = new History();
    $output = user_call_func(array($instance, 'setHistory'));


    If your setHistory accepted parameters, then you'd use:


    $instance = new History();
    $output = user_call_func(array($instance, 'setHistory'), $param1, $param2);


    http://php.net/manual/en/function.call-user-func.php
    Thanks groberts, I'll try to look into this and see if that will work for me.

  9. #9
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    4,827
    Mentioned
    142 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Thewhistler_1 View Post
    Thanks cpradio. This implement option you spoke of, can you elaborate on this a bit please? I haven't used it and I am not sure how to use it or what it does. Could you please show me how that would work, how it differs from "extends", and maybe provide an example?

    I really appreciate everyone's help on this.
    I'd be glad to
    PHP Code:
    interface IHistory  
    {             
              public function 
    setHistory();  
    }  


    class 
    Tasks implements IHistory // Implements History 
    {  
              public function 
    updateTask()  
              {  
                   
    $result mysql_query("update task set subject = "testing" where task_id = $this->task_id ");  
              }  

              
    // History Implementation
              
    public function setHistory()  
              {  
                   
    $sql "insert into history (some columns here) values (some values here)";  
                   
    $result mysql_query($sql);    
              }  
    }  


    class 
    Contact implements IHistory 
    {  
              public function 
    updateContact()  
              {  
                  
    $result mysql_query("update contact set first_name = "John" where contact_id = $this->contact_id ");  
              }  

              
    // History Implementation
              
    public function setHistory()  
              {  
                   
    $sql "insert into history (some columns here) values (some values here)";  
                   
    $result mysql_query($sql);    
              }  
    }  

    $task = new Task();
    $task->updateTask();
    $task->setHistory(); // granted you can run this within updateTask() using $this->setHistory();

    $contact = new Contact();
    $contact->updateContact();
    $contact->setHistory(); // granted you can run this within updateTask() using $this->setHistory();

    // The best part about interfaces is setHistory can be called without knowing you are using a Task or a Contact
    // Example: assume someone defined $mightBeATaskMightBeAContactWhoKnows as either a Task or Contact
    if ($mightBeATaskMightBeAContactWhoKnows instanceOf IHistory)
    {
        
    $implementsHistory->setHistory();

    The interface simply allows you to define functions that need to be written in a class that implements that interface. It is a design contract for your objects. If you have a set of objects that all need to react in a similar fashion, your interface can define what functions those objects need to implement.

    You can even make update() part of an implementation, but I'd argue that wouldn't fit as the parameters for both Tasks and Contacts would likely differ.

  10. #10
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    The simplest option (I'll show a better way in just a bit) is to create a global history object.

    PHP Code:
    $history = new History();

    class 
    Tasks 

              public function 
    updateTask() 
              { 
                   global 
    $history;
                   
                   
    $history->setHistory();
              } 

    The downside, though, is that by relying on a global variable, the Tasks class is tightly coupled to this particular application environment, and it's also harder to create different task objects that might have different histories. A better way is to inject a history object into your instantiated tasks object.

    PHP Code:
    class Tasks 

              private 
    $history;
              
              public function 
    __construct(History $history)
              {
                   
    // Tasks doesn't need to know where this history object came from or how it was created
                   // It only needs to know that this is the object it should use
                   
    $this->history $history;
              }
              
              public function 
    updateTask() 
              { 
                   
    $this->history->setHistory();
              } 


    $history = new History();

    $tasks = new Tasks($history); 
    this is the best answer in the thread. This is actual oop methodology in practice.

  11. #11
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by aaarrrggh View Post
    this is the best answer in the thread. This is actual oop methodology in practice.
    Wait now... Interfaces and inheritance are both accepted uses in OOP. The compisitional approach that Jeff Mott showed in his example is one way and it is true that it is the simplest to understand. Interfaces are properly demonstrated by cpradio where a contract for every class that implements the interface must adhere to and implement in their own way. Earlier Jeff and cpradio discussed if inheritance is warranted by a 'is a' test versus not using inheritance if the 'has a' test proves true; if it had worked in a 'is a' test then inheritance would have been a fine OOP method to use.
    ictus==""

  12. #12
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ServerStorm View Post
    Wait now... Interfaces and inheritance are both accepted uses in OOP. The compisitional approach that Jeff Mott showed in his example is one way and it is true that it is the simplest to understand. Interfaces are properly demonstrated by cpradio where a contract for every class that implements the interface must adhere to and implement in their own way. Earlier Jeff and cpradio discussed if inheritance is warranted by a 'is a' test versus not using inheritance if the 'has a' test proves true; if it had worked in a 'is a' test then inheritance would have been a fine OOP method to use.
    But if the history object is more of a log/entry class in reality, then it should be renamed.

    It simply doesn't makes sense for a task to be an instance of a history.

    Also, one of the primary reasons for using an interface is to take advantage of polymorphism, but this was never mentioned. I think Mott's original answer was tidier and makes more sense.

  13. #13
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    4,827
    Mentioned
    142 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by aaarrrggh View Post
    But if the history object is more of a log/entry class in reality, then it should be renamed.

    It simply doesn't makes sense for a task to be an instance of a history.

    Also, one of the primary reasons for using an interface is to take advantage of polymorphism, but this was never mentioned. I think Mott's original answer was tidier and makes more sense.
    My only argument to the implementation Mott proposed (and this isn't stated anywhere, just my opinion), is that the implementation of the $history -> setHistory can't change by the type of object implementing it. With an interface it can, as your object can define how the setHistory is executed (so long as it matches the function declaration).

    Maybe a Contact and a Task would execute the history entry the same way, I highly doubt it, but I guess it is possible. If it were up to me, I'd want to implement the two differently and that is where an interface would shine. Just my two cents.

    I must also agree, that Mott's implementation is very simple and tidy as well and if the execution would be the same between the two, I'd go with his method (so long as it matches any other objects execution as well).

  14. #14
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by aaarrrggh View Post
    But if the history object is more of a log/entry class in reality, then it should be renamed.

    It simply doesn't makes sense for a task to be an instance of a history.

    Also, one of the primary reasons for using an interface is to take advantage of polymorphism, but this was never mentioned. I think Mott's original answer was tidier and makes more sense.
    Doh... He did show polymorphism... That'll teach me to read the forum on my iPad. Didn't see his example until just now...

  15. #15
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    My only argument to the implementation Mott proposed (and this isn't stated anywhere, just my opinion), is that the implementation of the $history -> setHistory can't change by the type of object implementing it. With an interface it can, as your object can define how the setHistory is executed (so long as it matches the function declaration).

    Maybe a Contact and a Task would execute the history entry the same way, I highly doubt it, but I guess it is possible. If it were up to me, I'd want to implement the two differently and that is where an interface would shine. Just my two cents.

    I must also agree, that Mott's implementation is very simple and tidy as well and if the execution would be the same between the two, I'd go with his method (so long as it matches any other objects execution as well).
    He used dependency injection, so you could just pass a different history object to the Task constructor to change the implementation.

  16. #16
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    4,827
    Mentioned
    142 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by aaarrrggh View Post
    He used dependency injection, so you could just pass a different history object to the Task constructor to change the implementation.
    I'm not sure I'd ever approve having multiple History objects without some sort of IHistory interface that defined how those objects should be utilized. Then once you do that, you can pass in IHistory and let each object run the appropriate functions.

    Or you could have the objects themselves implement IHistory and take one less class out of the mix.

  17. #17
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by cpradio View Post
    I'm not sure I'd ever approve having multiple History objects without some sort of IHistory interface that defined how those objects should be utilized. Then once you do that, you can pass in IHistory and let each object run the appropriate functions.

    Or you could have the objects themselves implement IHistory and take one less class out of the mix.
    ++
    ictus==""

  18. #18
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    I'm not sure I'd ever approve having multiple History objects without some sort of IHistory interface that defined how those objects should be utilized. Then once you do that, you can pass in IHistory and let each object run the appropriate functions.

    Or you could have the objects themselves implement IHistory and take one less class out of the mix.
    Yeah, we'll I was thinking the History objects would have a setHistory method enforced by an interface or abstract base class. I tend to use abstract classes personally, but it's used for the same effect. Polymorphism really tidies things up and helps make things easier to maintain.


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
  •