SitePoint Sponsor

User Tag List

Results 1 to 16 of 16

Hybrid View

  1. #1
    SitePoint Member
    Join Date
    Dec 2009
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    PHP OOP Best Practices Question

    I have been playing around with OOP for a little bit, trying to wrap my arms around the concepts and I have run into a few different variations of the same problem. It seems there are multiple ways of handling it and I curious what the best option is?

    I have two classes:
    1. Document Information
    2. Date Formatting


    Neither of these classes are an extension of the other.

    The Document Information class contains the date the document was originally created in a unix timestamp. I want to format it as a readable date (i.e. 4/19/2010). My "Date Formatting" class is already set up to format the dates the way that I like.

    The way I understand OOP to work is that rather than formatting each date throughout the application individually (by placing date("D F d Y", $timestamp) wherever a date is displayed). There would be a class that contains the appropriate method and the class would instantiated as necessary. That way, if the developer decides they don't like the "D F d Y" formatting, they could change it in one place and would apply throughout the application. Please correct me if I am off base.

    Is there a good way for me to access the Date Formatting class within my Document Information class so I can format my date?

    Would I instantiate a new Date Formatting object within the Document Information Class?

    Or, should I create a Getter method in the Document Information class that will pass it to the Date Formatting class and back again?

    I have been using the second option but it can get tedious quickly creating all those getter and setter methods. I really curious to learn how others are working through this or whether I have "programmed myself into an OOP corner"...

  2. #2
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    4 Thread(s)
    Composition

    PHP Code:
    class DocInfo {

        private 
        
    $_dateFormater;
        
        public function 
    __construct(DateFormater $formater) {
            
    $this->_dateFormater $formater;
        }
        
        public function 
    doAction() {
        
            
    $time time();
            
            
    $date $this->_dateFormater->format_date($time);
            
        }

    }

    class 
    DateFormater {

        public function 
    format_date($date) {
            return 
    $date/* formated data */
        
    }



  3. #3
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by bmeadowcroft View Post
    The way I understand OOP to work is that rather than formatting each date throughout the application individually (by placing date("D F d Y", $timestamp) wherever a date is displayed).
    OO can do that, but so does a simple function. Your problem is not yet complicated enough that you need the full power of OO. Funnily, because your problem is not hard enough, you have lot's of solutions. Are you hopping from solution to solution, all of which could work, but you don't know which is best?

    Quote Originally Posted by bmeadowcroft View Post
    Is there a good way for me to access the Date Formatting class within my Document Information class so I can format my date?
    Lots! I'll throw in some scenarios and you can see how OO solves each.

    1) You just want to keep the date formatting code in one place and make it very obvious that's where it is.

    OO is a good tool for modularisation as well as for adding flex points...
    PHP Code:
    class DocumentMetadata {
        private 
    $date;

        function 
    __construct($timestamp, ...) {
            
    $this->date = new DateFormatter($timestamp);
            ...
        }

        function 
    date() {
            return 
    $this->date->render();
        }
    }

    class 
    DateFormatter {
        private 
    $timestamp;

        function 
    __construct($timestamp) {
            
    $this->timestamp $timestamp;
        }

        function 
    render() {
            return ... 
    // Mechanics of creating the string here.
        
    }

    Users of the DocumentMetadata never even know that it is using DateFormatter internally. It's for your benefit only so that you can group date stuff out of DocumentMetadata to keep your head clear.

    No OO Fu points yet, but less stress when you return to the code.

    2) You want to have multiple date formatting rules available, and the formatting is chosen at the time the DocumentMetadata class is constructed...
    PHP Code:
    class DocumentMetadata {
        private 
    $date;

        function 
    __construct($date, ...) {
            
    $this->date $date;
            ...
        }

        function 
    date() {
            return 
    $this->date->render();
        }

    You create the DocumentMetadata instance like so...
    PHP Code:
    new DocumentMetadata(new DateFormatter($time), ...) 
    Now you could swap DateFormatter for UkDateFormatter or for UsDateFormatter and the document would work just fine.

    We've added a flex point, namely the ability to choose the date format at construction time. We've made creating the DocumentMetadata a little more involved.

    You get a grudging OO Fu point for mechanical skill, but there is something a little ugly in doing formatting in the DocumentMetadata class. It's kind of doing two jobs.

    3) You want to add other methods to the DateFormatter, but you don't want to keep messing With DocumentMetadata...
    PHP Code:
    class DocumentMetadata {
        private 
    $date;

        function 
    __construct($date, ...) {
            
    $this->date $date;
            ...
        }

        function 
    date() {
            return 
    $this->date;
        }
    }

    class 
    DateFormatter {
        private 
    $timestamp;

        function 
    __construct($timestamp) {
            
    $this->timestamp $timestamp;
        }

        function 
    render() {
            return ... 
    // Mechanics here.
        
    }

        function 
    renderAsWords() {
            return ... 
    // Mechanics here.
        
    }

    Dead simple. You set it up with a formatter and clients of the class get the whole object back to do with as they please. DocumentMetadata now knows nothing about formatting anything. The problem is that DocumentMetadata is a bit vacuous. You could have just used a hash or StdObject. If DocumentMetadata does something, like date calculations, it will have to know about the innards of $date anyway. It now knows nothing about DateFormatter

    2 OO Fu points anyway, as this could be simplest solution in practice. There more refined solutions to this general problem, but my post would get too long.

    4) DocumentMetadata works with dates and fiddles with them internally, but you don't want display code mixed in to it. You also want to choose formatting later in the code...
    PHP Code:
    class DocumentMetadata {
        private 
    $date;

        function 
    __construct($date, ...) {
            
    $this->date $date;
            ...
        }

        function 
    writeOfficialPublicationDate($formatter) {
            
    $formatter->take($this->alwaysNoon($this->date));
        }

        function 
    alwaysNoon($date) {
            ... 
    // Our document handling works in mysterious ways
        
    }
    }

    class 
    DateFormatter {
        function 
    take($date) {
            return ... 
    // Mechanics here.
        
    }

    Now DataFormatter can do more than pass a string around. It can draw a whole display if needed. In fact it should. This trick solves the problem of passing data from class to class until it finds the target. Just take the mountain to Mohammid and save all those getters and setters.

    This is not just super flexible, the formatting and the date calculations are completely independent, it's also going to produce much less buggy code.

    When we returned our DateFormatter object in example 3, suppose the DateFormatter had a method called setDate()? That means clients of DocumentMetadata could mess with it's internals. That's an invitation to buggy code. If you find all the dates are wrong, who was to blame. That DateFormatter could have been passed around quite a way. Even to a separate process if it was persisted to the database and back.

    By handing clients the whole of DateFormatter we gave too much authority away. This breaks the "Principle of Least Authority" (POLA), usually more loosely termed encapsulation. Solution 4 does not do this.

    DocumentMetadata is free to pass the DocumentFormatter by value in example 3, but it would have to know a bit about the DateFormatter to do this. In example 4, by handing out a value (not the invisible thread called a reference) it maintains it's internal protection from corruption. Your lines of demarcation are safe.

    Oh yes, the code is simpler too.

    There just aren't enough OO Fu points for this solution. This is OO Ninja.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  4. #4
    SitePoint Enthusiast
    Join Date
    Nov 2006
    Posts
    28
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    another vote for composition.

    many say that inheritance is usually only good for a few levels .

    when you find functionality that you need somewhere in a class but is not related to the purpose of a class composition can be very useful.

    Alternatively use static method calls if the called method requires no instance information

  5. #5
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by bmeadowcroft View Post
    I have two classes:
    1. Document Information
    2. Date Formatting
    I think you have got some excellent advice on OO which you should follow. But I think you should step back an ask why you have those two "classes"? Getting document information is a couple of PHP function calls; fomatting a date from a timestamp is just a call to date().

    It sounds like you want to do OO, but you are starting from the micro instead of the macro. I recall Marcus once said (I think quoting a colleague) that OO in PHP is best used to build the structure of your application. Inside PHP objects is usually a lot of calls to PHP's extensive built-in library.

    So step back and take a look at your application. What are the big things (Entities) in it?
    Christopher

  6. #6
    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 arborint View Post
    I think you have got some excellent advice on OO which you should follow. But I think you should step back an ask why you have those two "classes"? Getting document information is a couple of PHP function calls; fomatting a date from a timestamp is just a call to date().
    Off Topicish

    date() is probably underpowered, IntlDateFormatter would be my starting point.

  7. #7
    SitePoint Addict
    Join Date
    Apr 2009
    Posts
    248
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren View Post
    Off Topicish

    date() is probably underpowered, IntlDateFormatter would be my starting point.
    Alternatively, the built-in PHP DateTime object would work *very* well.

    It is unfortunately only available in PHP 5.3+

  8. #8
    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 SituationSoap View Post
    Alternatively, the built-in PHP DateTime object would work *very* well.

    It is unfortunately only available in PHP 5.3+
    DateTime object only works for English.

    IntlDateFormatter will work for any given locale.

  9. #9
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,397
    Mentioned
    65 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren View Post
    IntlDateFormatter will work for any given locale.
    Any given locale, available to the script.
    Salathe
    Software Developer and PHP Manual Author.

  10. #10
    SitePoint Wizard silver trophybronze trophy Cups's Avatar
    Join Date
    Oct 2006
    Location
    France, deep rural.
    Posts
    6,869
    Mentioned
    17 Post(s)
    Tagged
    1 Thread(s)
    There was an underpowered version introduced 5.2 which does not contain all the bells and whistles added in 5.3.

    It emulates the strtotime() functions though and the ->format() method is just fine too.

  11. #11
    SitePoint Addict
    Join Date
    Apr 2009
    Posts
    248
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah, I'm aware of that version. DateTime is actually the reason I finally upgraded to 5.3 in my production system (internal application, and I basically own the server so I got the choice). I was trying to use DT functions, and kept getting unsupported method errors, so I bit the bullet.

    The upgrade was incredibly smooth, for what it's worth. I think I had all of about two code changes to make.


Tags for this Thread

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
  •