SitePoint Sponsor

User Tag List

Results 1 to 16 of 16
  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,140
    Mentioned
    16 Post(s)
    Tagged
    3 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 Member
    Join Date
    Nov 2006
    Posts
    18
    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 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.

  9. #9
    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.

  10. #10
    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.

  11. #11
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,397
    Mentioned
    61 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.

  12. #12
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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). 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.
    No, you're right on target. The most important technique in programming, OOP or otherwise, is encapsulation. The whole idea is to consolidate the code which performs a task in one location. This makes maintenance manageable for the program. There's also the advantage that each place where you call that block of code has no idea exactly HOW it happens. This lets you make any changes that you need to down the road, and the code which depends on it still runs just fine. That's priceless.
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  13. #13
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by GiffordBruno View Post
    Why do not you want to use this with web developing PHP???? That is really more effective than OOP. Thanks.
    You misunderstand, OOP is not a language like PHP. OOP is an approach to programming. There are two main styles to programming on the web today. OOP and Procedural programming where you execute a step, then execute the next step, then the next, and the next, until you're done. As it developed in the 40s and 50s, people found ways to take advantage of different language features to make programming easier and more simple.

    One big one was encapsulation. At the lowest level of encapsulation, people developed functions as a way to consolidate a common program-level task that needed to be executed throughout the program. This was revolutionary because you no longer had to write the same snippet of code a hundred times throughout a program. You just wrote it once. As you constructed the rest of your program, this made it very easy to modify and adjust the code because it only lived in that one spot, inside the function. You could now much more easily cope with client change requests during construction, and program maintenance was almost infinitely improved.

    This idea was extended into routines which perform one "real-world" task and may require multiple function calls to execute. Then in 1979, a C programmer name Bjarne Stroustrup, extended this idea further still in what he called C with classes (which has since become known as C++). The class has a particular responsibility in the program which typically entails multiple functions / routines and key pieces of data as well. Each type of encapsulation works together to bridge the gap from the human realm where we look at a problem domain, down to the programming language level where we give the computer instructions for tasks it understands how to do. While encapsulation is probably the most important programming technique, this is probably the most important concept in modern programming, abstraction. Classes are just one more level of abstraction that help you as a developer to make the structure of your program conform to the high level problem resolution techniques that you use in application whether that be use case scenarios / diagrams, UML, flowcharts, or even pencil scratches on the back of a napkin.

    Good procedural programming looks almost identical to good OOP programming. My father is a C programmer and his is C code looks very similar to the OOP programming I do, and is guided by the same fundamental principles. OOP languages simply add in the language structure, classes, which give you a new technique to accomplish the same goals, but in a good procedural program you'll see other language features like structs which have been pressed into use to serve basically the same purpose. The native support for classes in an OOP language simply gives you more power and makes it simpler to accomplish the same things instead of jumping through some of the hoops that a non-OOP language makes you deal with. That's exactly why PHP5 finally adopted much more robust support for classes which PHP6 is extending even further. If you just want to do a simple one-off script which requires no encapsulation and will never be reused, then writing some inline code may be the way to go; and even then, if front-end developers may be mucking around in the code, you might be wise to protect your code from their keystrokes by pulling it into an external .php file and plug it back in with an include. However, whether you write procedurally or OOP encapsulation is the same technique you should use either way to improve your abstraction (and a host of other issues too including information hiding, reusability, simplicity, self-documentation, etc.)
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  14. #14
    SitePoint Member
    Join Date
    Dec 2009
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wow! Thanks for the great response.

    After reading through several of these posts, I did a bit more research into your suggestions. I think composition and aggregation are definitely my solution(s). My Document Info class can instantiate a new Date Formatter object and return the appropriately formatted date to wherever it needs to go.

    In fact, everyone's help not only improves this application but I have even been able to go back and improve some of the other things I created as part of my initial foray into OOP.

    Quote Originally Posted by arborint View Post
    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().
    arborint - After reading some of these posts, I realized that I probably chose bad class names to use for my original question since, you're right, this information could be handled by existing functions.

    My application is actually a bit more complicated than my example... it is a document management system. The Document Info class handles the things like Author, Original Upload, etc... that is tracked in the database for each document. The date class allows me to format the unix timestamps to something a bit more readable. The original application is built with procedural code and just became a nightmare when I wanted to add a feature which is one of the driving reasons why I chose to rework the whole thing.

    Quote Originally Posted by Chroniclemaster1 View Post
    No, you're right on target. The most important technique in programming, OOP or otherwise, is encapsulation. The whole idea is to consolidate the code which performs a task in one location. This makes maintenance manageable for the program. There's also the advantage that each place where you call that block of code has no idea exactly HOW it happens. This lets you make any changes that you need to down the road, and the code which depends on it still runs just fine. That's priceless.
    Chroniclemaster1 - This helped a lot. It helps me wrap my head around the idea of encapsulation.


    lastcraft - Thanks for the examples. They were very helpful.

    Thanks again everyone for the feedback.

  15. #15
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by bmeadowcroft View Post
    After reading through several of these posts, I did a bit more research into your suggestions. I think composition and aggregation are definitely my solution(s).

    In fact, everyone's help not only improves this application but I have even been able to go back and improve some of the other things I created as part of my initial foray into OOP.
    Glad we can help. There's definitely a lot to learn in OOP as long as you realize that you don't have to learn everything on day one. Composition is an excellent choice, IMO the Java guys have pretty conclusively demonstrated that composition/aggregation is a much better structure than inheritance in 90% of situations. I have a security class which defines a whitelist in an abstract class and automatically validates whatever input is fed to it automatically in the constructor. I've then built classes which inherit from the abstract class so that data for any input is automatically validated to ensure application security. However, that's the only case I can cite where I've favored inheritance over composition. And clearly it was the need for application security (which only inheriting from the base class could provide) that prompted me to use this. Inheritance is not as self-documenting because you are using variables, functions, etc. in a class which are defined in a separate piece of code. That just makes working with inheritance more difficult during construction and in some cases a nightmare during the maintenance cycle.

    Quote Originally Posted by bmeadowcroft View Post
    arborint - After reading some of these posts, I realized that I probably chose bad class names to use for my original question since, you're right, this information could be handled by existing functions.
    After seeing your description, I'm not sure you're names are bad. I think we simply lacked the context to know exactly what was going on inside them. I'm struggling to come up with a better name for consolidating those pieces of information. Proper naming conventions fall into the category of Semantics. This is how you write all those beautiful OOP abstractions that are saving your butt into the application; that means the semantics in your code will always remind you what your abstractions are so they can continue to save you (and anyone else) throughout construction and on into the maintenance cycle. It's also a critical factor in creating Self-Documenting code.
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  16. #16
    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
    arborint - After reading some of these posts, I realized that I probably chose bad class names to use for my original question since, you're right, this information could be handled by existing functions.

    My application is actually a bit more complicated than my example... it is a document management system. The Document Info class handles the things like Author, Original Upload, etc... that is tracked in the database for each document. The date class allows me to format the unix timestamps to something a bit more readable. The original application is built with procedural code and just became a nightmare when I wanted to add a feature which is one of the driving reasons why I chose to rework the whole thing.
    Well ... I was not really saying that your names were bad. They seem fine. My point was that you had some bigger class implied in your first post. And you have now clarified that you have a "document management system." If that is what you are building then the first class you might think about creating is a Document class! And above that you might need some some ways to find Documents and some ways to orgainize Documents into collections of some sort.

    Once you have the real Domain of your app roughed out, then you can start thinking about whether you need little utility classes like your Doc Info and Date Formatter classes. Or you might be fine just having methods in your Document class to get information like dates.

    There is some question whether your need here is for utility classes or configuration data? What you originally asked for is a global way to specify date formats. If you put that in a class then you are responsible for it. If you put it in configuration data then its moved out of your code. You just need to get that configuration data into your Document class...
    Christopher


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
  •