SitePoint Sponsor

User Tag List

Page 2 of 3 FirstFirst 123 LastLast
Results 26 to 50 of 52
  1. #26
    Database Jedi MattR's Avatar
    Join Date
    Jan 2001
    Location
    buried in the database shell (Washington, DC)
    Posts
    1,107
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally posted by spoorw8er
    Hi Mattr,

    Good to have some alternative views on the subject. Some comments on the comments
    Glad I can chime in! Although now we have two threads going and it is a bit hard to cope with everything like this too bad VB isn't threaded for times like this!


    1. Standards
    True, I think 75% is a bit much -- I'll agree and say at least 50% should be specifications, documentation, DB design documents, etc. 75% just happened to be the magic number for the project I just worked on which made coding it a breeze!

    2.OO
    Every instance of an application instantiates his objects.
    The fact that PHP has no shared-memory is not a drawback of OO, just a technical issue in the current PHP implementation.
    Certainly -- but we're discussing Classes in PHP projects . I'm a long-time C++ guy so I know how C++ handles memory. When you create it, it will allocate all the memory and such that it needs.

    However the nature of a 'regular' application is that it is a single executable which can keep an object around in memory as you call different functions and perform different operations.

    Unless you wish to cram all of your PHP into one document and somehow figure out a way to not call a PHP file each time you perform an operation you're going to have (perhaps) many objects created, manipulated, and destroyed.


    And be careful with the term web-apps. Some of these things are pretty complex from an architectural viewpoint and definitely could benefit from using the OO-approach.


    Certainly -- I worked on an enterprise Cold Fusion application which had a really sophisticated store engine as a 'class' (it was actually mainframe C++). It kept track of the user session and handled all the shopping cart stuff.

    Pretty cool, in my CF apps I could go:
    user.cart
    and loop through all the items in the cart, or a number of things.

    It worked really really well but it wasn't done in CF, it was done in C++ which, I think, is more suitable to that type of thing (shared memory and such). But CF has a pretty robust shared memory implementation so I think you can accomplish the same thing with CF -- place something in memory and have it global to all scripts.

    If PHP had something like that I'd be more inclined to say 'go hog wild with OOP!' -- I think PHP's implementation of OOP is pretty poor to begin with (compaired to my C++ experience) and has enough problems that it outweighs the benefits (currently; I'm interested in what Zeev and the rest do at Zend).

    3. DB Abstraction
    I think it is a balancing act -- but tell that to a customer who spent $3 million on Oracle that you're not using any subselects, stored procedures, or triggers so that your application works seamlessly from MySQL to Oracle.

    What I would suggest is engineer for 'standard' SQL -- use joins, subqueries, exists, etc. -- and then for weird cases like MySQL you'll have to do some extra work but for the majority out there you have already optimized, great SQL code.

    If you see a place for a stored proc, then you can go ahead and do it. Probably what I would do is as I sell or distribute a particular version cut out the SELECT statements in the code and replace with a stored proc call (like you have in your example). Best of both worlds!!


    Well, I would argue it is just the opposite. If I have a class encapsulating the database that exposes an insert_method, the only place I have to change code is in that method, not do a find and replace in several scripts.

    That is what I was suggesting -- however as I said there are 5 ways to insert to a table currently. You must have 5 methods to insert depending on what the user wants.

    Think of how many selects you can do:
    regular
    joins -- inner, outer, left inner, cartesian, etc.
    subqueries -- correlated? non-correlated
    EXISTS
    IN

    Now, do you include SELECT INTO in the SELECT statements or the INSERT statement group?

    Taking your example, you could create the method also like this:
    <snip>


    That is what I was going for, yes. If you want to take OOP for what it is designed for however you must go that far. Which, in my opinion, is a TON more work then necessary.

  2. #27
    Database Jedi MattR's Avatar
    Join Date
    Jan 2001
    Location
    buried in the database shell (Washington, DC)
    Posts
    1,107
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Re: We just have to compromise!

    Originally posted by mikemindel
    Here's an interesting issue regarding the use of the database abstraction layer:

    Take the following description of a small object oriented system from The Object Primer:
    Mike, there was a great example of something like that in my SQL Smarties book (by Joe Celko). It's at the office or I'd write a bit of it in. More or less you can write a single SQL query to align everyone with their requested rooms and classes. It's a very large SQL query, but nothing like 20-30 objects.


    I think... that with PHP and objects we have to find a 'compromise' solution for object-oriented programming in large projects. We simply can't be pure OO. We can be OO like.

    Mike Mindel
    Wordtracker
    uts his finger on his nose: That is what I was somewhat trying to say -- originally this thread appeard to say 'OOP will save the world' in theory. However in practice you can see how OOP falls flat on it's face with performance issues and general complexity in certain cases. I'm not saying OOP is bad or no one should use it, but it is certainly not the savior of the world that many OOP authors write about (I've read some of those books, trust me!). It's almost as if they invent problems that they can magically solve with OOP but out here in the real world it is very, very different.

    In theory, I can write SQL on one RDBMS which will work on any SQL driven RDBMS. However, that is very much not the case unless it is a trivial SQL statement.

  3. #28
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Congrats

    Well Mike,

    Actually I do not have much to add to your last post, except congratulations and hats-off to you.

    Re-reading the complete thread, seeing where you started from and then seeing where you arrived at with your last re-design, I would conclude you are on your way to become quite fluent in OO style quite fast.

    And very important, at least in my opinion, you recognise there are pitfalls and take the time to think about them.

    The steps you outline are good rules-of-thumb when going thru the design-phase (or to check for if you are reviewing the design of someone else), keep them in mind (and in time you'll probably add some more based on new experience gained).

    On your last point (your edit), this is also a question of how much value does it add to create a class for it and persist the objects.
    Always remember that classes are basicly about DATA and FUNCTIONALITY rolled into one.
    If you only have a piece of data to consider, a 'normal' variable is more than enough. If you only have functionality, use a 'normal' function.
    When using OO always watch out that you don't go overboard on it.
    So if in your application there are no responsibilities assigned to REMOTE_USER and the only thing you do is reading the content of it, stick with the global provided by PHP.
    If however you have to check on every page for things like is this user logged-in, is he not spoofed, does he have the right authorisations, what's in his basket, .... then you're talking about a different and more complex entity, for instance a Page_Visitor where the content of REMOTE_USER is just one of the properties a Page_Visitor has.

    Now a little remark about persistance. Persistance, like most things, comes in different levels (has a 'scope' if you will).
    Some things are persistent only for the duration of one page view (in this case there is no need to store it on disk or whatever). Others only for the duration of a visit to your website (viewing multiple pages in a certain sequence), in this case the session-mechanism provided by PHP is a good place to store the information (or client-side cookies).
    Other information might be persistant for the duration of the up-time of your site (across multiple visits), maybe use client-side cookies, maybe store it on the server in a db or a file.
    And lastly some objects are just always there (logically that is), e.g. product-information, in which case you would store it using a db or a file on the server.

    My point is, persistance of information is also something to consider carefully during design.

    Say that on your e-commerce site people have to register first before they can buy something (become a member, so let's call it a MEMBER-class).
    Now that you would like to store details such as name, address, telephone, age, ... for later reference speaks for itself, after all that is the point of registration.

    But you wouldn't (well I wouldn't anyway) assign the same level of persistance to the IP-address of the member. If you did, that would mean (logically) that your member is forced to always using the same computer to use your site. I, as a member, wouldn't be able to order something from my workplace and check up on the order in the evening from my home.

    But I would assign visit-level persistance to the REMOTE_IP, because this gives me a way to check if the 'session' is not hijacked.

    Actually as a result you suddenly have two classes in your app, a MEMBER-class representing a registered person and a VISITOR-class, representing a visitor to your site, who may or may not be a registered member.

    Hope this doesn't confuse it too much .....

  4. #29
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Ta!

    >Actually I do not have much to add to your last post,
    >except congratulations and hats-off to you.

    Thanx. I just figured someone had to go there or some people are going to get really.... confused. This object oriented thing seems simple enough at first glance, but is subtly confusing in many different ways.

    >Re-reading the complete thread, seeing where you
    >started from and then seeing where you arrived at
    >with your last re-design, I would conclude you are on
    >your way to become quite fluent in OO style quite
    >fast.

    I certainly hope so. I've got a month to finish this project!

    > On your last point (your edit), this is also a
    > question of how much value does it add to create a
    > class for it and persist the objects.
    > Always remember that classes are basicly about DATA
    > and FUNCTIONALITY rolled into one.

    Yes. This was the main point about the high coupling of the database abstraction layer and each class. But I take it we're now talking about just the persistence of variables and/or objects using sessions.

    > If you only have a piece of data to consider,
    > a 'normal' variable is more than enough. If you only
    > have functionality, use a 'normal' function.
    > When using OO always watch out that you don't go
    > overboard on it.

    Yes exactly.

    > So if in your application there are no
    > responsibilities assigned to REMOTE_USER and the
    > only thing you do is reading the content of it,
    > stick with the global provided by PHP.
    > If however you have to check on every page for
    > things like is this user logged-in, is he not
    > spoofed, does he have the right authorisations,
    > what's in his basket, .... then you're talking about
    > a different and more complex entity, for instance a
    > Page_Visitor where the content of REMOTE_USER is
    > just one of the properties a Page_Visitor has.

    Yes. This is a really, really good point.

    To add to this: With the web, we are not dealing with many users. We are dealing with one user at a time. There may be many using the system at once, but we code as if it is one user at a time. So it's not like a university system where we have say a 'person' object and we inherit the 'person' object to make a 'professor' object or a 'student' object.

    If we start creating a persistent object in our sessions for simple variables - such as the persistence of the REMOTE_USER variable, then this is probably overkill. But as you say, if there are a collection of methods that act upon that REMOTE_USER or REMOTE_IP variable then it should probably go into a class.

    I'm actually looking into user authentication at the moment, so a class is probably going to be necessary.

    > Now a little remark about persistance. Persistance,
    > like most things, comes in different levels (has
    > a 'scope' if you will).
    > Some things are persistent only for the duration of
    > one page view (in this case there is no need to
    > store it on disk or whatever). Others only for the
    > duration of a visit to your website (viewing
    > multiple pages in a certain sequence), in this case
    > the session-mechanism provided by PHP is a good
    > place to store the information (or client-side
    > cookies).
    > Other information might be persistant for the
    > duration of the up-time of your site (across
    > multiple visits), maybe use client-side cookies,
    > maybe store it on the server in a db or a file.
    > And lastly some objects are just always there
    > (logically that is), e.g. product-information, in
    > which case you would store it using a db or a file
    > on the server.

    > My point is, persistance of information is also
    > something to consider carefully during design.

    Yes. Again. Really good points about persistence. My only personal worry is that I use frames at one point in the site and I think they play havoc with sessions. I.e. each frame tries to instantiate the session and ends up confusing PHP. Which is really annoying.

    Mike Mindel
    Wordtracker

  5. #30
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Some OO, but mostly sidetracking

    Hi Mattr,

    So we were more or less saying the same things.

    The only thing that that still bothers me (this is not a remark about your statements, it is just something I still find hard to explain to people who started of by coding 'single' user apps) is what exactly is the defintion of a 'regular' app.

    (Sorry Mike, this will digress a bit from the original subject, but indulge me, please, pretty please).

    When I started out coding I was working on a good old Commodore C64 (ha, those were the days my friend).
    Life was simple, the C64 was single tasking, applications were single user.
    Then I switched to Amiga (whatever happened to it), suddenly I had to deal with multi-tasking computers and although in my opinion no one in his right mind would want to start the same application twice to edit the same document at the same time, the typical user did do these kind of things.

    Then I got my first real job, programming COBOL in a BULL DPS7000 multi-tasking multi-user mainframe environment. Reading about the thing I learned that several instances of the same code were running more or less simultaneously, working on the same dataset. And most importantly (or frightening me at least) that you released control to the user when you required input from him. Or in other words, the code would execute to a certain point where user interaction was needed, there the execution would be halted, the code would be swapped out of memory to allow other apps (or another instance of the same app) to run, and when the user provided some input, your code would be swapped in again, and execution would just start from the top of the code, not from the point were you left off. In essence the thing was STATELESS.

    Well, I can tell you I was a bit nervous, because I remembered the trouble it took me to switch from a single-tasking mindset to multi-tasking (and how much extra coding I had to do for that).

    But it turned out pretty OK, because on the BULL they had this nifty little thing called a transaction monitor (not database transactions, but user transactions). All you had to do was code a switch at the beginning of your code to detect where you were in the code before you released to the user and then jump to that point. And ofcourse before you released to the user, you stored a variable to indicate where you left of. This would be stored in shared-memory, disk, whatever you wished.
    And what you were doing was dealing with oone user at the time (just added this after reading Mike's post, he makes a similar observation there). Which actually simplified my life instead of creating new hassles.

    And then I left the mainframe environment, ending up in places where I needed multitasking and multi-user and all I had were (admittedly pretty powerful) things like threading, multi-apartments, mutex-locking, .... Powerful, but takes a lot of thinking and coding to get it right (and headaches, tantrums, ....). And I had to learn OO style along the way not so much because I liked it, but because this was one of the approaches to use these complex things in a more or less simple way.

    And now I'm into web-apps.
    Actually, when you come to think about it, web-apps work exactly like the good old DPS7000 (and other similar environments ofcourse).
    At a certain point in your script you set an indicator and you turn over control to the user for input (say, fill in a form) and your script ends. When the user submits the form, the same script (or another) is started from the top of the code and you use your indicator to see where you have to go to continue execution. And in the meantime several other instances of the same scripts run happily along next to that one. Each script(-sequence) execution deals with only one user at the time.

    BTW in PHP a GLOBAL variable can only be considered GLOBAL to a certain instance of a script (or sequence of scripts you intended to be executed one after another), not to all instances of the script. It is actually the content of a variable that is global, not the defintion of it.
    So if you declare a GLOBAL at the start of your script, this means that when five 'simultaneous' executions of your scripts are started, the thing will reserve five times the necessary memory for the global, and each memory-location can contain different content.

    Sadly PHP (currently) has no such thing as a full blown user transaction monitor that can use shared memory and the likes (other web-environments do, like CF for instance), but it has the session-mechanism. And that gives you all you need to implement user transactions (you could also use client-side cookies ofcourse, but I absolutely hate storing things on the client when logically it should be on the server).

    And yes, some environments also provide you with ways to have truly GLOBAL variables, global not only within a particular instance of a script-execution sequence, but where all execution-instances are looking at the same content at the same memory location. And this can be very handy to have at certain tiles.

    But to be absolutely honest, I seldom find myself in a situation where I cannot solve a problem if they are not there.

    Well I hope this didn't digress too much (or if it did, you found it at least mildly interesting).
    Last edited by spoorw8er; Oct 16, 2001 at 04:56.

  6. #31
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Keeping objects around in memory

    >However the nature of a 'regular' application is that
    >it is a single executable which can keep an object
    >around in memory as you call different functions and
    >perform different operations.

    >Unless you wish to cram all of your PHP into one
    >document and somehow figure out a way to not call a
    >PHP file each time you perform an operation you're
    >going to have (perhaps) many objects created,
    >manipulated, and destroyed.

    I must say that this is news to me. I had no idea that regular object oriented languages set aside memory to persist the objects so that they would not have to be continually created and destroyed a la php. Oh I get it now. This is why the whole thing has been so confusing.

    Are we also talking about other object oriented web based languages such as Python and Perl?

    This continual creation and destruction of objects in php on each web page causes me a real headache - and is probably why I'm going on so much about this highly coupled abstraction layer/php classes.

    This also must be the reason why a lot of people hiring out there insist that you code in php classes but tend not to go on about coding using object oriented programming. I guess they're expecting us to figure out this weird hybrid OO that is just not quite OO in a stateless environment (the web), using php.

    Mike Mindel
    Wordtracker

  7. #32
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Re: Ta!

    Originally posted by mikemindel
    My only personal worry is that I use frames at one point in the site and I think they play havoc with sessions. I.e. each frame tries to instantiate the session and ends up confusing PHP. Which is really annoying.

    Mike Mindel
    Wordtracker
    Yep, I heard several stories that indicate this.
    As I have never used frames myself (not out of principle, just because when I started coding web-apps our web-designer was absolutely nuts about CCS and could not be bothered with anything else), I cannot offer much help there.
    I can say however the CCS thing is pretty powerful stuff too, until now we have not really missed frames.

  8. #33
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Re: Keeping objects around in memory

    EDIT 16/10/2001 14:15 : Some confusing explanations on my part that I made somewhat clearer (I hope).

    Originally posted by mikemindel
    I must say that this is news to me. I had no idea that regular object oriented languages set aside memory to persist the objects so that they would not have to be continually created and destroyed a la php. Oh I get it now. This is why the whole thing has been so confusing.

    Are we also talking about other object oriented web based languages such as Python and Perl?

    This continual creation and destruction of objects in php on each web page causes me a real headache <snip>
    Well, what a lot of environments do, is handle the context switching (user transactions) for you (swapping an idle thing out of memory and swapping another thing in). They take a memory-image snapshot (a snapshot of the STATE that is) of your app-instance and store it somewhere (some calls this process 'freeze'), and load that image back in (thaw) when needed.
    Actually in this case, your code does not stop and does not release control to a user, it is simply not aware of the swap, when swapped back in the thing just continues from the point where it was swapped out. Your code is thinking it is in a STATEFUL environment.

    Actually the origin of this type of context switching was not the need to handle multiple users running multiple instances of one application, but handling multiple tasks in a resource-limited environment (task context switching) and processes like these also exist in mainframe environments.

    It just so happened that over time on certain environments (PC, Unix, ...) extra functionality was added so that it became possible to use it for user context switching too.
    But even on those environments over time there were products that offered mainframe style user context switching services (e.g. TUXEDO), because of the added benefit.

    (NOTE: although TUXEDO was called a transaction monitor, please to not confuse this with DATABASE transactions. The first type of transactions are there to enable STATEFUL execution, database transactions are there to garantuee that a manipulation of multiple tables that should logically be considered a undividable, only succeed if all the individual table manipulations succeed).

    PHP provides only the basis of such a transaction mechanism (sessions), but it is your responsibility as a developer to handle user context switching, storing an indicator when you release control and then when your code starts executing again, retrieving the indicator to find out where you left off (and again ofcourse setting up the objects etc .... before continuing execution). You as a developer are responsible to provide a way to handle STATE.

    That is what I was talking about in my previous post about my travels thru different programming environments. That DPS was in essence STATELESS, you as the developer had to code some things to be enable STATEFUL execution.

    When working with PHP you should always remember that (although a pretty fun language with many powerful features) it is not (yet?) on the same level as full-blown application server (e.g. Silverstream, Oracle App Server, the BEA product range, hell even the Microsoft MTS, ....) that provides you with task-context switching, user-context switching, object-brokering, database transaction, .... services.

    At this time PHP is still nothing more than a language-interpreter with some added features. And as far as I can tell, the PERL situation is pretty similar.

    I feel these type of services are not really part of the language either, but part of the environment. For instance you can code COM+ components in VB, C++, C#, etc ... and run them on the Microsoft MTS.

    And (to paraphrase) make no mistake about it, full-blown app-servers also require some hefty hardware to run on, even for small-scale applications.
    Where you can run an Apache/PHP site for expected average concurrent usage of 10 persons pretty smoothly on a Pentium I using Linux, you would need more powerful hardware (and thus a bigger investment) to do the same with e.g. Silverstream.
    Ofcourse the flipside is that the Silverstream setup may then very well handle up to 1000 concurrent users without any problems (if your application is well-designed ofcourse ;^), where as your PHP-environment, however well-designed it may be, struggles to reach 500 (or even less).

    The hardware-required aspect also depends on the requirements you have for the project, just as the choice for a certain develomment philosophy does and the application design decisions you take do.
    I wouldn't advise running a super-critical ('make or break' your clients business) e-commerce site on a simple one server setup. You might need several servers to provide redundancy, you'll need some good backup and recovery strategies, you might need load-balancing, you might need two or more independant connections to your ISP (maybe even more than one ISP),you might have to spread servers around different locations, ....

    Contrary to popular belief (mostly found in the minds of managers and clients) web-applications that are directly tied to the clients or your company's core-business need a well thought-out (and often complex) infrastructure, a well thought-out design and do not come cheap (not in acquiring it and not in maintaining/running it).
    Ofcourse giving this message as the first one in a sales-meeting with the client will prolly not win you much business, but at some point (rather sooner than later) he should be made aware of it. Doing things on the cheap is just asking for trouble.

    So to conclude:
    I my opinion the only thing you could blame on PHP is that is does not provide support for full-blown OO: it has no concept of encapsulation (no private members), the way it supports references is scary at times, and so on.
    Actually, using an interpreted language where every developer using your class can actually read (and god forbid modify) your code is in itself already strange in an OO environment. Bit hard to hide the details of your class and force others to use it in the correct way by only calling the methods you felt necessary to make public, don't you think (OK, I know there is a PHP encoder product too, but you catch my drift I hope).

    The fact that you are also required to handle user context switching and STATE, is due to the fact we have no true application server for PHP. This has nothing to do with using OO or procedural (and by the way, using procedural you would also need to implement STATE handling).

    Such an application server should in my opinion be a seperate product and not tied in to the language-interpreter itself (after all using C++ you can code perfectly good applications for Windows without ever using COM+ or MTS or whatever).
    Last edited by spoorw8er; Oct 16, 2001 at 07:06.

  9. #34
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    On (database) abstraction layers

    Edit 17/10/2001 14:20
    Found the explanation still a bit unclear (well my collegue did anyway), so extended and changed it a bit


    Rereading the thread, I feel the whole database abstraction issue is not really clear and might lead to more confusion instead of less.

    Everyone that has read this thread upto this point will have grasped (or knew already) that using OO leads to a layered application-design where you have several layers of abstraction.

    At the top level you have class/objects that represent real-life things, at the bottom level you have 'small' (watch out: the internals can be complex) classes/objects that provide very specific services for multiple classes above it.
    So you have for instance a member class, a visitor class and an administrator-class, where the visitor and administrator class use the member class.
    This abstraction view is driven (in my view) mostly by the functional requirements for the application.

    When reading OO literature you also will find many references to multi-tier application architectures. Here to we speak of several layers of abstraction.
    Typically you'll find something like this:
    - User Interface Layer
    - Business Rule Layer
    - Data Storage Layer
    (Other terms can be used, some people even introduce more layers, but the basic point here is that we move away from the 2-tier client/server towards a more complex setup).
    Each of these layers can (not must) be living on a seperate machine. A layer can even consist of more than one machine.
    This abstraction view is driven (in my view) mostly by technical requirements (notice how there is no talk about 'real-life' objects such as member, visitor, ...)

    To me, the two views on abstraction do not exclude one another but exist in parallel to eachother (or super-imposed if you will).
    For many people starting of with OO this can be very confusing, indeed even people using OO on a regular basis often struggle with it.

    What does it mean?

    Well, that your 'functional' member class in itself can be decomposed into
    - a visual part: this groups the knowledge (data + methods) needed to display the business-level data (e.g. use a checkbox to display the members gender)
    - a business rule part: this groups the busienss-knowledge (data + methods) to make sure your member complies with the rules your business has (e.g. minimum age of a member),
    - a storage part: this groups the knowledge (data + methods) needed to store or retrieve the business-data from a permanent storage system (be it a db, a file, whatever) (e.g. store the name, age and gender in a table called 'members').

    Note that this might mean you have three (or more) different classes that will need to interact with eachother to provide the full 'functional' member. And this is where I think most of the confusion is created: if you decide to seperate your functional class into three seprate classes, each one handling a technical layer, this does not mean the storage class is the db abstraction class.

    Good side of things is that you could also implement the storage part itself in two seperate entities: some methods in the actual member-class and stored procedures that reside in your DB and that are called by those methods.


    So where does the database abstraction class come into the game?

    Using the n-tier approach, it might mean you have completely different environments on each layer. The User Interface might be living on one (or more) Windows PC('s), The Business Rule on one or more Unix machines and the database on high-performance high-redundancy mainframes. The environments are ofcourse connected to eachother by some form of a network (LAN, WAN, Internet, Wireless, whatever), but on top of that you also need something to make sure they understand eachother when communicating.
    You can do this using protocols (such as TCP/IP for instance), but these types of protocols are not really concerned with the content of your message. You still need something on top of that to 'translate' that content.
    And that's where the DB Abstraction Class is needed.

    To me a 'DB Abstraction Class' is a technical class that is used to hide the details of how a database engine (or more general the data storage layer) operates, not how the database (or more general the data) is structured. Or in other words HOW the 'business'-data will be stored, not WHAT 'business'-data will be stored (that is still and always will be a responsibility of the storage part of a 'member').

    You should consider it a a GATEWAY to a different world, enabling communication between two worlds. It's responsibility is nothing more than implementing a protocol (or translation) between two worlds.

    So the (functional oriented) class handling the storage of a MEMBER will use the (technical oriented) class that is abstracting your database engine passing in the data it wants stored in the method call.
    And another (functional oriented) class handling the storage of an ADMINISTRATOR, will also be using the same (technical oriented) database abstraction class, but it will ofcourse pass other data to it.
    The DB Abstraction Class will take that data and activate the right commands in the database (Which can be 'INSERT....', but also 'EXEC my_stored_procedure ....'), passing (and maybe doing conversions e.g. ASCII to EBCDIC) the data along to the commands.

    As Mattr points out, it is not always worthwhile to develop the super-abstracted, works with any database type of DB Abstraction Class. It depends on how your application will be used.
    Like I said: when you are almost 100% sure only Oracle will be used on the data storage layer, make the most of it and don't spend time writing DB methods that can use any type of data storage.
    Furthermore, if next to the above you are also 100% that the Business Rule layer and Data Storage Layer will alwasy be living on the same host, you might even consider forgetting about the DB Abstraction Class al together (I wouldn't advise it as it does make your life easier but is conceivable of doing)


    Other 'technical' classes are for instance user-windows, pages (found on the User Interface layer), classes that implement network-protocls, ....

    And to tie in to my previous post: one of the reasons why so-called application-servers exist is to support this hopping from one machine to another, either from (technical) layer to layer or from machine to machine within a (technical) layer.

    Well, I hope this clarifies the issue....
    Last edited by spoorw8er; Oct 17, 2001 at 06:37.

  10. #35
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    An array of objects

    Here's an idea for those of you who don't like the idea of using globals or passing around 10 objects at a time! It's called having a hash (could also be an array) of objects.

    Say you instantiate a couple of classes:

    $database = new database_class();
    $email = new email_class();

    You can just stick a reference to them inside a hash (associative array).

    $object[database] = &$database;
    $object[email] = &$email;

    Then you can just pass $object around by reference in any new class. E.g.

    $class = new class($object);

    with
    $this->object = &$object;
    in the constructor.

    Now you access methods in the new class that has been passed this hash of objects using:

    $this->object[database]->some_method


    Just an idea.

    Mike

  11. #36
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Taking encapsulation to the extreme...

    A question:

    I think it's now been established that we treat the public object methods in a highly encapsulated way. We give the public method an input and we get an output.

    Just like pressing a button on an ATM machine. We press the button and it goes and accesses the internal methods (get customer details, check if account has cash, etc.), and brings back the result. We donít care what it's internal methods are, so long as we get a result.

    Fine.

    But does the same hold true for private methods? Private methods are methods which only the object knows about and cannot be accessed from outside that object. Look at the following to examples. Taking encapsulation to the extreme we would get:

    PHP Code:
    class some_class() {

        function 
    some_public_method($some_variable) {

            
    $some_variable some_private_method($some_variable);    
            
    $some_variable some_other_private_method($some_variable);
            
    // The value of some_variable changes
            // depending on the result from each
            // internal function.

            
    return $some_variable;
        }

        function 
    some_private_method($some_variable) {

            
    $local_variable some_function($some_variable);
            
    $local_variable += 100;
            return 
    $local_variable;
        }

        function 
    another_private_method($some_variable) {

            
    $another_local_variable some_function($some_variable);
            
    $another_local_variable += 100;
            return 
    $another_local_variable;
        }

    or is it better to relax when it comes to private methods and use something like:

    PHP Code:
    class some_class() {

        var 
    $object_scope_variable;

        function 
    some_public_method($some_variable) {

            
    $this->object_scope_variable some_variable;
            
    some_private_method();
            
    another_private_method();
            
    // Each function acts on the globally
            // defined variable $this->object_scope_variable.

            
    return $this->object_scope_variable;
        }

            function 
    some_private_method() {

                
    $local_variable some_function($this->object_scope_variable);
                
    $local_variable += 100;
                
    $this->object_scope_variable $local_variable;
            }

            function 
    another_private_method() {

                
    $another_local_variable some_function($this->object_scope_variable);
                
    $another_local_variable += 100;
                
    $this->object_scope_variable $another_local_variable;
            }

    Notice that the first example is actually more encapsulated and de-coupled than the second example and is more in tune with a procedural approach when dealing with private methods.

    The second method exposes variables which are global to the object (only) which can then be used by any of the functions inside the object. Each function can then act directly on this global object using something like $this->object_scope_variable = value.

    So the question. How far do we push encapsulation? Which of these two methods do you use?

    Mike Mindel
    Wordtracker
    Last edited by mikemindel; Oct 19, 2001 at 13:50.

  12. #37
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Mike,

    I tend to go for the second approach.

    I view private methods as being nothing more than helper functions but with a scope limited to the object they reside in, and (private) datamembers as 'global' within the scope of the object they belong too. Therefore I find it logical that the (public and or private) methods of an object can access the datamembers of that object directly.
    So inside an object I use a more procedural approach.

    There are people that disagree and argue that your first approach is the only PURE approach in OO, but I find this leads to a lot of typing and passing variables around and bloated (and inefficent) code.

  13. #38
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Yup. Have to agree with you.

    Thanx spoor8wfer,

    I think that's exactly the way to approach it.

    I tend to go for the second approach too, simply because you can get lost when passing around all these variables. I find it much easier to approach the class as an engine. There are plenty of methods to do what needs doing and they all have access to the same parts.

    I am actually taking the following approach to designing classes now:

    PHP Code:

    class some_class {

        
    // Declare public variables
        
    var $public_var1$public_var2;

        
    // Declare private variables
        
    var $private_var1$private_vat2;

        
    // ------ PUBLIC METHODS ------
        
    function some_class($input1$input2) {
            
            
    // The constructor
            // Check for objects that have been passed.
            // If not passed properly then exit script.
            // Decide all variables that are now
            // available to all private methods.
            
    $this->some_variable $input1;
            
    $this->some_other_variable $input2;
        }

        function 
    some_public_method($input1$input2) {
            
    // Highly encapsulated public method

            
    return $answer;
        }

        
    // ------- PRIVATE METHODS ------

        
    function _some_private_method() {
            
    $some_local_variable $this->some_variable 2;
        }

        function 
    _some_other_private_method() {
            
    $some_local_varibale $this->some_other_variable 3;
        }


    Mike
    Wordtracker

  14. #39
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    News from the front line...

    Here's some news from the front line. I made a leap inunderstanding today which I'd like to share with you all. Probably obvious to some... but hopefuly it will help others stuck in the procedural mind set.

    I had a php script which consisted of this kind of this kind of spaghetti:

    PHP Code:
    if ($comprehensive_search) {
        
    // Do something
        
    if (another annoying condition) {
            
    // Do something
        
    } elseif (yet another annoying condition) {
            
    // Yawn....
        
    }
    } elseif (
    $exact_search) {
        
    // Do something else
    } elseif ($compressed_search) {
        
    // Do something else still


    This is a simplified version. But basically these kind of nested if conditions were littered all over my scripts. If I added another search then that would mean a rewrite of all the scripts. And it was just so messy!

    But then I remembered an important rule:

    'If it's not generic then you did it wrong!'
    And then it suddenly occurred to me. All the searches should be objects themselves! But how to avoid repeating yourself in each search_object.

    Answer: I should have an abstract class called search where I define some basic values. Then create a new class for comprehensive_search, exact_search and compressed_search which inherit from the abstract class using 'comprehensive_class extends search_class' and make changes that were unique to each class in there. So:

    PHP Code:
    class search_class {
        
        function 
    search_class() {
            
    $this->template "standard.tpl";
            
    $this->user_input[] = "";
            
    $this->pluralise true;
        }

    }

    and then for the comprehensive_search:

    PHP Code:
    class comprehensive_search_class extends search_class {

        function 
    comprehensive_search_class() {
            
    $this->template "comprehensive.tpl";
            
    $this->pluralise false;
        }

    So the abstract search class defines all the defaults, and you can make your specific changes using the inherited class.

    PHP Code:
    // $select holds the name of the particular search selected by the user. 
    // e.g. $select could hold 'comprehensive', or 'simple'
    // or 'exact' for example.


    include_once ($code_base "classes/search_class/{$select}_search_class.inc");
    $select .= "_search_class";
    $search = new $select(&$user_input); 
    This might now be simple_search_class for example. The user_input comes straight from the html form where each input field has $user_input[nos_responses] or $user_input[query] as its name. E.g.:

    PHP Code:
    <select name="user_input[nos_responses]">
        <
    option value="100" selected>100</option>
        <
    option value="500">500</option>
    </
    select
    Since the new search object can do nothing unless it has values to work with, we simply pass these values by reference with $select(&user_input). No user_input, no search object!

    Then you simply pass this object to the Texis object (where you previously had all those if statements). The Texis object in this case is an object for handling connections to the Texis database:

    PHP Code:
    // Create the texis object
    include_once ($code_base "classes/texis_class.inc");

    // Instantiate the new texis object
    $texis = new texis_class(hostportetc....);
        
    // Send the search object to the Texis object and retrieve the results
    $texis->query(&$search); 
    I'm now sending the search object by reference to the Texis object which can now handle 'any' search request in a generic fashion.

    No more convoluted if/elseif/endif statements all over the place and a reusable Texis object that I can use in any other project. And another thing. If I want a new type of search, then I create a new search object e.g. new_search_object_class which extends (inherits) from the search_class like the others. Then I can just plug it into the Texis object without any code rewrites.

    We're getting there!

    Mike Mindel
    Wordtracker
    Last edited by mikemindel; Oct 22, 2001 at 07:05.

  15. #40
    Your daddy. WALoeIII's Avatar
    Join Date
    Apr 2001
    Location
    USA
    Posts
    526
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    WOOSH!

    Right over my head.... DAMN!

    So much to learn too little time.

  16. #41
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Inheritance and abstraction

    Hi Mike,

    Well, not much to add actually except that you made the right observation about the different searches being just a bunch of classes that all inherit from the base search_class.

    People starting with OO style are often a bit dazzled by the examples and the talk about real-life entities and such that they overlook these sort of abstractions.
    And it is for these sort of things OO was originally 'invented' and can add a lot towards effectiveness and efficiency when developing and maintaining applications.

    Another important thing in your example is that you take into account the organisation of the code into the different files etc ....
    When writing efficient OO style code the grouping into packages and the likes is every bit as important as important as the design of the classes themselves (it is an integral part of the design).
    This is how you avoid to have to include every script on every page.

    This ofcourse also means that things like naming-conventions are important not only for the classes themselves, but also for the files etc ....
    Especially when working with a team of developers, make sure you have defined and communicated these conventions before everybody goes of to their own corner to start spitting out code.

    In short, I like the way you're implementing it. Even though your examples are only simplified snippets of code, they do give a insight into the bigger picture, the (useful) concepts of OO are clearly recognisable and the code looks quite elegant.
    Last edited by spoorw8er; Oct 22, 2001 at 05:33.

  17. #42
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    On trying to be too clever...

    (Thx spoorw8er)

    Here's an interesting one... With objects, you can actually shoot yourself in the foot by being too clever. Take the following example:

    I have a number of objects that I instantiate in a globals file. Namely:

    PHP Code:
    $database = new database_object();
    $system_details = new system_object();
    $user_details = new user_object();
    $debug = new debug_object();
    $timer = new php_timer_object();
    $display = new display_object();
    $fetch = new fetch_object(); 
    So I thought to myself. Hmm.. That's a lot of objects that I need all the time. Why don't I cache them? So the above became:

    PHP Code:
    $cache[database] = new database_object();
    $cache[system_details] = new system_object();
    $cache[user_details] = new user_object();
    $cache[debug] = new debug_object();
    $cache[timer] = new php_timer_object();
    $cache[display] = new display_object();
    $cache[fetch] = new fetch_object(); 
    Then on each new class I developed, I wrote:

    PHP Code:
    function constructor() {
        
    $this->cache = &$GLOBALS["cache"];
    }

    function 
    some_function($sql) {
        
    $this->cache[database]->query("select username from some table");
        
    $this->cache[database]->retrieve_results();

        return 
    $this->cache[database]->username;

    Then I thought. Well.. Since I have active sessions, why don't I just session_register("cache") and then check to see if cache is available on each script.



    PHP Code:
    session_start;
    session_register($cache);

    if (!
    $cache) {
        
    $cache[database] = new database_object();
        
    $cache[system_details] = new system_object();
        
    $cache[user_details] = new user_object();
        
    $cache[debug] = new debug_object();
        
    $cache[timer] = new php_timer_object();
        
    $cache[display] = new display_object();
        
    $cache[fetch] = new fetch_object();
    } else {
        
    // We assume that the cache (now an array of objects
        // is loaded from the MySql table

    Clever? For sure.
    A good idea? Probably not.


    First of all. I don't think PHP knows how to handle an array of objects very well. I took one look at my mysql table and on one script call, all looked well. Then I loaded another page and the mysql field turned to gunk. Perhaps they've fixed this problem with the latest 4.0.6, I don't know.

    Anyway... the above seems like a really good idea... but for some reason it just doesn't feel right. Let's look at the reasons against doing this:

    1. Extra key strokes when accessing these objects e.g. $this->cache[database]-> rather than just $this->database->. This can get confusing, and really add up to a lot of extra keystrokes.

    2. Encourages highly coupled code. Instead of just accessing the database object in a highly coupled way, we would be encouraged to do the same for the other objects in the cache. This is really not a good idea for reasons explained in a previous post (re: coupling).

    I think we should be forced to declare all highly coupled objects e.g. database abstraction layer with $this->database = &$GLOBALS["database"] in the constructor rather than $this->cache = &$GLOBALS["cache"].

    3. We can always cache groups of objects together in a separate function by reference and store them if we need to.

    It's not very difficult to create a function that loops through your objects and stores them in a cache variable by reference. E.g. $cache[object_name] = &$object_name;

    The point is that the caching may be all well and good, but it should not be instrinsic to the object definitions or you start to fence yourself in. In fact, the caching of objects should perhaps itself be an object! An object whose job it is to cache other objects. Otherwise we might be in big trouble later on if we decide to turn caching off!

    4. Canít really trust PHP to store an array of objects and retrieve them using the session handler correctly.

    5. Easy access to variables. Itís also much easier to just reference $this->system[time]-> rather than $this->cache[system]->time-> which is an extra step.

    So you could.... store all your user variables in the user object, and system variables (date, time, number of keywords in database) in the system object, all bundled together in a cache.

    But I think it makes much more sense to have two global associative arrays which are easily accessed:

    e.g.

    PHP Code:
    $system[date] = .......
    $system[nos_records] = 234092309428

    and

    $user[pathway] = .....
    $user[some_other_variable] = ..... 
    Then you have easy access to the two most important variables without crowding out your global name space and throwing user/system objects all over the place.

    This also means that when you have html forms with:

    Code:
    <input type="hidden" name="user_input[search_type]" value="comprehensive">
    that you get another global variable called $user_input that can be referenced the same way. So ultimately you end up with three associative arrays:

    $system for things like date, time etc.
    $user for things like the pathway through site, username, password etc.
    $user_input for anything the user enters into an html form.

    Actually.... in my session registration scripts, I even have the username and password fields set up as $user[username] and $user[password] to keep everything nice and compact. I don't then want to have to assign these to the user object with $user->username = $user[username] and then pass the user object around as that would be overkill.

    In fact, since I have to use session_register on user anyway, it stores all the other user globals in the mysql database which can be very useful if you have scripts to check how many users are online, what path they are taking through the site (frames, no frames, using the trial etc.) My globals script actually needs to make about 5 database calls to get basic information about the user. Now that I store it in a session, it means just one mysql call and saves time.

    So... I think once again (just like the issue with private/public methods), the key to classes/objects in PHP is to be OO-like and not an OO purist.

    Mike Mindel
    Wordtracker
    Last edited by mikemindel; Oct 23, 2001 at 06:17.

  18. #43
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    When only one instance of an object will do!

    As always,

    There is an exception to every rule. Reading about OO patterns on the web, I consistently came across the 'Singleton' pattern. Basically, sometimes you only want a single object of a class ever created and creating two or more would cause problems. E.g. if you had one object accessing a file on your disk and you accidentally created another one, you might end up losing all your data!

    So. Here's a way of storing all your objects in a registry. If you try and call an object for a second time, then you get a reference to the one that was stored instead. So no object ever gets created twice.

    PHP Code:
    <?php
    // ------------------------------------------------------------------------------ 
    // v1.1 of the singleton_class.inc. This is a general class for the singleton
    // object creation approach. The idea is that only one instance of an object
    // should ever be created in the system.
    // ------------------------------------------------------------------------------ 

    if (!defined("SINGLETON_CLASS")) {
    define("SINGLETON_CLASS"1);

    class 
    singleton_object {
        
        function 
    singleton_obect() {
            
    $this->registry[] = array();
            
    $this->class_base "";
        }
        
        function &
    initialize_object($class_name$object_name) {

            
    // Build the arguments for the constructor. Remove the first
            // two as we need these to get the class name and the object name
            
    $args func_get_args();
            
    array_shift($args);
            
    array_shift($args);
            
            
    // We assume that any other arguments are meant for the new object's
            // constructor. Put the remaining arguments into an array and then create
            // a code string that we will use as arguments for the new object's 
            // constructor.
            
    $arg_labels = array();
            for (
    $i 0$i func_num_args() - 2$i++) {
                
    $arg_labels[$i] = "\$args[${i}]";
            }
            
    $new_args implode(", "$arg_labels);
            
            include_once(
    $this->class_base "/" $class_name);
            
            if (isset (
    $this->registry[$object_name])) {
                print 
    "Warning: $object_name is already set <br>";
            } else {
                eval(
    "\$this->registry[\$object_name] = new $object_name(${new_args});");
                print 
    "New instance of $object_name created<br>";
            }

            
    $return_object = &$this->registry[$object_name];
            return 
    $return_object;
        }
        
        function 
    print_registry() {

            print 
    "<br><br><u>List of all objects stored in the registry:</u><br><br>";    
            
            
    $i 1;
            while (list(
    $object) = each ($this->registry)) {
                print 
    "$i. " $object "<br>";
            
    $i++;
            }
            
            print 
    "<br><br>";
        }
    }
    }
    you assuming your $system array held all the important information, you would call singleton with something like:

    PHP Code:
    $singleton = new singleton_object();
    $singleton->class_base $system[code_base] . "classes";

    $database = &$singleton->initialize_object("database_class.inc""database_object"
    $system[mysql_host], $system[mysql_username], $system[mysql_password], $system[mysql_port], 
    $system[debug_database], $system[mysql_close], $system[mysql_redirect]);

    $display = &$singleton->initialize_object("display_class.inc""display_object"
    $system[template_directory]); 
    I'm aware that this conflicts with the post about the cache before but at the end of the day you still end up with $system and $user and not $cache[system] or $cache[user]. It also means that you can throw around the $singleton object as much as you like and it's a one stop shop for all the objects in your system.

    Mike Mindel
    Wordtracker

    P.S. Does anyone know if the above function I wrote will pass an object as an argument by reference. I.e. if I have &$object as one of the arguments, will it also get passed to the constructor as &$object. I suspect it does but could do with the confirmation.
    Last edited by mikemindel; Oct 25, 2001 at 12:08.

  19. #44
    SitePoint Evangelist
    Join Date
    Oct 2001
    Posts
    592
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    OO and PHP

    Wow, what a thread this is! I never knew of the existence of this community, but
    now I do I don't think I'll be leaving very soon! At least people here seem to
    know that they're talking about (contrary to many other forums).

    Because I feel I know what I'm talking about as well :-), I'd like to add some
    comments to this great thread.

    1. On Object Oriented Programming

    As soon as I discovered OO, I abandoned structural programming, and vowed never
    to return. For the last 7 or 8 years, this has worked out just fine. I'm
    originally a C++ fan, and these two books help me a lot:

    - Design Patterns, by Gamma, Helm, Johnson and Vlissides, Addison-Wesley

    This great book represents a catalogue of often recurring problems in Object
    Oriented Programming and gives them a name and a solution. Examples: Singleton
    (as mentioned earlier), Iterator, Factory... I could go on for hours. I'd
    recommend this book to ANYONE who is in OO. Of course, there are other books
    which try to do more or less the same, but this is my personal bible.

    - Large-Scale C++ Software Design, by Lakos, Addison-Wesley

    Although this book is about C++ and all its peculiarities, it is also a valuable
    resource in that it gives lots and lots of real-world examples and their
    solutions. Also, it introduces the next step in Object Oriented Programming (note:
    my opinion), and that is component-based programming.

    Basically what this book does is the following:
    1. It shows how a design should be according to OO rules
    2. It points out why the design doesn't work, because we're living in the real
    world.
    3. It shows how the original design can be modified to work in the real world,
    while keeping the design nice and clean.

    If you don't know C++, this book might be difficult. Still, I'd recommend
    looking into it.

    2. Components

    I hope John Lakos (author of Large-Scale C++ Software Design) doesn't mind if I
    insert some quotations here. Also, I hope I don't do him any unjustice by
    explaining things the wrong way. That will be all my fault, and not his!
    Quotations from the book are between parentheses.

    There are two sides to each design process: first there's the logical design,
    and secondly the physical design. "From a purely logical point of view, a design
    can be thought of as a sea of classes and functions where no physical partitions
    exist - every class and free function resides in a single seamless space."
    "Physical design focuses on the physical entities in the system and how they are
    interrelated. In most conventional C++ programming environments, the source code
    for every logical entity in the system must reside in a physical entity,
    commonly referred to as a 'file'. Ultimately, the physical structure of every
    C++ program can be described as a collection of files. Some of these files will
    be header files and some of them will be implementation files."
    "For larger programs, we need to impose additional structure in order to create
    maintainable, testable, and reusable subsystems."

    I hope you're still with me, because here it comes: "A component is the smallest
    unit of physical design. A component is not a class and vice versa.
    Conceptually, a component embodies a subset of the logical design that makes
    sense to exist in an independent, cohesive unit. Classes, functions,
    enumerations and so on are the logical entities that make up these components.
    In particular, every class definition resides in exactly one component.
    Structurally, a component is an indivisible, physical unit, none of whose parts
    can be used independently of the others."

    Wow! Now what does all this mean? Let's illustrate by example:

    After I run a SELECT-query on a database, I get a number of rows. I want to
    iterate over these rows. My first design could be something like this:

    class Query {
    function executeQuery(&$db, $sql) {
    // Execute $sql on $db
    }

    function isDone() {
    // return if we're at the last row
    }

    function jumpToFirst() {
    // go to the first row in the result
    }

    function moveToNext() {
    // move to the next row
    }

    function getRow() {
    // return the current row
    }
    }

    This design isn't completely wrong, but it's not completely right either. If I
    wanted to print each row, I could do something like:

    for ($query->jumpToFirst(); !$query->isDone(); $query->moveToNext()) {
    $row = $query->getRow();
    // Print the fields in $row
    }

    This looks pretty good. However, now I have some code that needs to run two
    separate loops over the query-rows simultaneously... (This may seem far-fetched,
    but believe me, it happens!) The only way to do that now is in one of two ways:
    1. At every step, make sure I'm at the right row. That is: each loop must make
    sure it's looking at the right row before accessing it. This is SLOW, because it
    uses a 'jumpToFirst' and many 'moveToNext' calls at each iteration.
    2. I execute the same query twice, so I have two Query-objects.

    The second solution is slightly better, because it is faster. It does use more
    memory however.

    Looking at the Query class, I note that it tries to do too many things:
    1. It runs a query on the database
    2. It stores the results of the query
    3. It allows the rows in the result to be traversed from start to finish.

    Many designers will immediately say: "You need to use an iterator here", and
    that's absolutely the right way to go. We now end up with two classes:

    class Query {
    function Query(&$db, $sql) {
    // Execute $sql on $db
    }

    function getRowCount() {
    // Return the number of rows in the result
    }

    function getRow($index) {
    // Return the row at the given index
    }
    }

    class QueryIterator {
    var $query;
    var $index;
    var $size;

    function QueryIterator(&$query) {
    $this->query = &$query;
    $this->reset();
    }

    function reset() {
    $this->index = 0;
    $this->size = $this->query->getRowCount();
    }

    function next() {
    $this->index++;
    }

    function isValid() {
    return ($this->index < $this->size);
    }

    function getRow() {
    return $query->getRow($this->index);
    }
    }

    This design is a lot better, in more than one way. Iteration can now be done as
    follows:

    for ($it = new QueryIterator($query); $it->isValid(); $it->next()) {
    $row = $it->getRow();
    // Do something with $row
    }

    Running multiple loops on the same query is easy: just create a second
    QueryIterator object. This object uses very little memory, and has very simple
    methods. There are more advantages to this design I won't go into here.

    The point I'm trying to make is this: classes Query and QueryIterator are
    closely related: class QueryIterator cannot exist without class Query. This
    implies two things:
    1. Class QueryIterator may use 'private' methods of class Query. (In C++ terms:
    class QueryIterator is a 'friend' class of class Query).
    2. Class QueryIterator should be in the same physical location as class Query.
    That is: in the same file.

    Together, the classes Query and QueryIterator form a single component. By
    putting them in the same file you ensure that:
    - Someone cannot use a different (malicious) QueryIterator class for iterating
    over the rows in a Query.
    - Being a 'friend' (having the right to call private methods of another class)
    doesn't breach OO-rules. Because they are the same component and thus in the
    same file, there is no way this relaxation of OO rules can lead to disaster.

    In my opinion, component based programming leads to compact and fast classes,
    while using the basic OO-rules might still result in big, slow classes.

    I hope I've clarified the idea of components a bit. If not (which is likely
    because I've only given a single - possibly bad - example) please take a look
    at the Lakos-book.

    3. And now to PHP

    As many have pointed out in this thread: PHP is not pure OO. This means that
    some principles have to be applied differently, or maybe not at all. Also, PHP
    is an interpreted language, which has some important differences with a compiled
    language:
    1. A program is 'compiled' on every run, instead of just once.
    2. Code can be evaluated and executed at run-time.

    Finally, PHP has no typechecking. This also has an important consequence, mainly
    for polymorphism. I'll dive a bit deeper into every point mentioned:

    - Compiling over and over again

    If you write classes in a compiled language like Java or C++, it doesn't matter
    how 'deep' the inheritance is. A class can have 20 superclasses, and it won't
    matter. In C++ for example, you do have a little overhead if you use inheritance
    (because a table with virtual method pointers must be maintained), but this
    overhead is always exactly the same.

    In PHP, classes must be 'compiled' each time a program is executed. If a class
    has many superclasses this will probably lead to very slow code. I haven't
    actually tested this, but it seems very likely. Thus, in PHP, it's best not to
    go to deep when using inheritance.

    - Evaluating and executing code at run-time

    Because PHP is an interpreted language, you can evaluate code at run-time. This
    can be very useful. For example, there's the Factory design pattern. This
    pattern returns an object of some class, depending on the description it
    receives. It is often implemented with a switch statement. In C++ for example,
    it could be like this:

    // For simplicity, I've omitted constructors and destructors, and I've
    // made createDistribution method inline. Also, in real life this class
    // would probably be a Singleton
    class DistributionFactory {
    public:
    enum DISTRIBUTION_TYPE {NORMAL, GAUSSIAN, LOGARITHMIC};

    Distribution createDistribution(DISTRIBUTION_TYPE type) {
    switch (type) {
    case NORMAL:
    return new NormalDistribution();
    case GAUSSIAN:
    return new GaussianDistribution();
    case LOGARITHMIC:
    return new LogarithmicDistribution();
    }
    };
    };

    // Create a normal distribution
    DistributionFactory df;
    Distribution d = df.createDistribution(DistributionFactor::NORMAL);

    The one well-known major drawback of this pattern is that the factory class must
    be extended every time a new distribution is added. However, in PHP, a factory
    can be implemented as follows:

    class DistributionFactory {
    function createDistribution($name) {
    $className = $name . 'Distribution';
    if (class_exists($className)) {
    return new $className;
    }
    return false;
    }
    }

    Now, creating a GaussianDistribution-object is as simple as:

    $df = new DistributionFactory;
    if (!$d = $df->createDistribution('Gaussian');
    // Oh oh! Class GaussianDistribution doesn't exist!
    }

    Apart from being very compact, this factory class has the nice advantage that it
    doesn't need to be extended if a new Distribution-class becomes available. PHP
    isn't pure OO, but if you use it the right way, it can be very powerful, compact
    and subtle, indeed!

    - Polymorphism

    Polymorphism is a nice feature of object-oriented languages that does the
    following: if two classes have the same superclass (not necessarily direct!)
    you can write code that runs on both classes:

    class Car {
    function getMaximumSpeed() {
    // return maximum speed in kph
    }
    }

    class FormulaOne extends Car {
    function getMaximumSpeed() {
    return 300;
    }
    }

    class Sedan extends Car {
    function getMaximumSpeed() {
    return 160;
    }
    }

    function printMaximumSpeed($car) {
    print $car->getMaximumSpeed();
    }

    $racer = new FormulaOne;
    printMaximumSpeed($racer);
    $sedan = new Sedan;
    printMaximumSpeed($sedan);

    Thanks to polymorphism this works. In PHP however, polymorphism is defined
    differently than what is generally assumed. In PHP you should use this
    definition: two classes are polymorph if they define the same (sub)set of
    methods.

    For example:

    class Boat {
    function getMaximumSpeed() {
    return 60;
    }
    }

    $boat = new Boat;
    printMaximumSpeed($boat);

    Even though class Boat doesn't have class Car as a superclass, it can be used
    with the function printMaximumSpeed, which expects a Car. There is both an
    advantage and a danger in this. The advantage is that you don't have to write
    thin superclasses just to support polymorphism (the well-known 'Object'-class
    in many language). This saves in compilation time and makes code run faster.
    The danger is that you could run code on some object by mistake, which doesn't
    result in an error. Instead of deleting a single node in some tree, you could
    end up deleting all records in a table, just because the method that does the
    trick is named exactly the same.

    To conclude: PHP isn't pure OO, but on the other hand it has some features
    compiled languages do not. If you use these features wisely, you can end up
    writing perfectly good OO-code. However, there are many pitfalls (like using
    global variables) that seem like features, but actually are not. It's only
    experience that tells you what's right and wrong!

    4. Some notes on this thread

    I'd like to end with some comments on the things I've read in this thread:

    - Error handling

    As an example of encapsulation, a class 'mysql_class' is given by Mike, that has
    a method 'query'. This method:
    - connects to a database
    - runs a query
    - returns the result

    Yes, this is encapsulation, as multiple classes are used to achieve this. But
    it's also an example of bad design (IMHO!). The reason: error handling.

    It's very difficult to handle errors in a class: if some method in a class
    fails, it normally doesn't know what to do about it, because it knows nothing
    about the environment it is in. It is therefore the responsibility of the code
    that uses the class to handle the error. Or, if that code doesn't know how to
    handle it either, to pass it on even further. (This is why exception handling
    exists in languages like C++, Java and Delphi).

    The problem with Mikes example is that the query-method both opens a connection
    to a database, AND runs a query. What if the connection couldn't be established?
    Or what if the connection was established, but the query is incorrect? The
    calling code gets only one result, so it cannot distinguish between these cases.
    Thus, these two actions shouldn't be in the same method.

    Where does this lead to? I always design and write by the rule that something
    should only do what it is supposed to do, and nothing more. This results in
    compact, fast methods. Yes, you may need to call more methods, but it gives you
    more control as well: if something goes wrong after calling the first method,
    you can do something about it immediately.

    - Global variables

    Every programming book tells you this: using global variables is wrong. If used
    in Object Oriented Programming, it leads to classes that are all more or less
    dependent on each other, while the aim should always be to write independent
    components.

    The funny thing is, Mike knows this, but does it anyway:
    - In one post, he quotes a book that says that the design should be in layers,
    and that each layer should use at most 9 objects. (I don't know where this
    number comes from; I usually go with about 4.)
    - In a later posts, he transforms all global variables to class variables with a
    nice trick (which is cheating, by the way :-)), giving as a reason that he has
    to pass too many objects in the constructor otherwise.

    Conclusion: the design is wrong! (Note that I'm simply stating a conlusion made
    by all OO-books here; I haven't actually seen all Mike's code, so I might be
    wrong :-))

    When I implement a class, it should clearly state how it uses object of other
    classes. Thus, if there's a HAS-relation, the class includes the definition of
    the used class, and may use it internally. If there's a USE-relation, it should
    be seen in the interface (by supplying get- and set-methods for example). Global
    variables violate this because:
    - The class-definition of the class the global variable is from isn't
    necessarily included.
    - There is no occurence of the class in the interface.

    For example:

    class Query() {
    function execute($sql) {
    return $GLOBALS['database']->query($sql);
    }
    }

    This is bad, because there's no clear HAS- or USE-relationship. What type is
    $database of anyway? Two possible solutions arise:

    // Solution 1: a HAS-relation

    require_once('Database.php');

    class Query() {
    var $database;

    function Query() {
    $this->database = new Database();
    }

    function execute($sql) {
    return $this->database->query($sql);
    }
    }

    // Solution 2: a USE-relation

    class Query() {
    var $database;

    function Query(&$database) {
    $this->database = &$database;
    }

    function execute($sql) {
    return $this->database->query($sql);
    }
    }

    Of course, it depends on the problem at hand which solution to use. In this
    case, the second is probably best. A Query doesn't know how to connect to a
    database (host, username, password and so on), and it's more efficient to use an
    already existent database connection.

    If, using either of these solutions, you find that you need to pass many
    objects, you should like at your design again.

    - Database abstraction

    Some people pointed out that it's not necessary to use a database abstraction
    layer, as porting a web-application to a different RDBMS virtually never
    happens. This is true, but this view is too narrow-minded. Why? Well, I've
    developed four or five large web-applications, most running on PostgreSQL, but
    some on MySQL. Because I have an abstraction layer in a component library, I
    can use the exact same code for each site, even though they use different
    RDBMS's. This view is broader, as it doesn't focus on a single application, but
    on a whole range of them.

    Please note that I do not use classes that create SQL queries for me. As
    pointed out correctly, every RDBMS uses his own SQL dialect. Due to the
    complexity of SQL, it's virtually impossible to put this in an abstraction
    layer. Also, why would you want to write an abstraction for SQL in PHP? That's
    like writing a book in Dutch to make writing English books easier! If you
    ever wrote an SQL abstraction layer, it should probably be in SQL. For example:
    in PostgreSQL I make extensive use of stored procedures and triggers, putting
    all database logic in the database, and making it possible to do a lot of work
    with simple queries executed from within PHP. In my opinion, that's the right
    way to go.

    Well, that's about it. I don't know about the quality of this post, but at least
    I can say it has substantial quantity! :-)

    Vincent

  20. #45
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Hi there!

    Hi there!

    Glad to have you over here! I'd like to point out to the thread that Vincent has an excellent set of articles/php classes of his own over at http://www.students.cs.uu.nl/people/voostind/index.php called Eclipse.

    Every programming book tells you this: using global variables is wrong. If used
    in Object Oriented Programming, it leads to classes that are all more or less
    dependent on each other, while the aim should always be to write independent
    components.

    The funny thing is, Mike knows this, but does it anyway:
    - In one post, he quotes a book that says that the design should be in layers,
    and that each layer should use at most 9 objects. (I don't know where this
    number comes from; I usually go with about 4.)
    - In a later posts, he transforms all global variables to class variables with a
    nice trick (which is cheating, by the way :-)), giving as a reason that he has
    to pass too many objects in the constructor otherwise.

    Conclusion: the design is wrong! (Note that I'm simply stating a conlusion made
    by all OO-books here; I haven't actually seen all Mike's code, so I might be
    wrong :-))
    I'm glad somebody pointed this out! I've had second thoughts about all this globalising anyway. Like you say, it seems a bit of a cheat and doesn't really encapsulate your classes.

    The main reason for this thread however was to show how a functional programmer makes it through to OOP programming. So... it has all the mistakes I make along the way too! I think this is probably a good thing overall. When it all works out, people can see how I went down all the wrong roads, and hopefully they won't do the same.

    Mike Mindel

  21. #46
    SitePoint Evangelist
    Join Date
    Oct 2001
    Posts
    592
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Global variables and stuff

    Hello there,

    Well about those global variables: I must admit I tend to fall in that trap myself often enough. It usually goes like this:

    - I'm implementing a class, and I'm pretty far already.
    - I find out I need to access an object (that happens to be 'global')
    - I don't want to rewrite my interface just now, and access the object the 'wrong' way.
    - I never change the interface again.

    Every now and then I revamp all my code and change the interface. However, it's often 'then' and almost never 'now' :-)

    Indeed, it's nice to follow the progress of at least one developer who wants to use OO. And I must say: you're going pretty fast, Mike!

  22. #47
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Welcome

    Hi Vincent,

    Glad you joined the thread, your post indeed has quantity and the quality is pretty good too.

    Originally posted by voostind
    1. On Object Oriented Programming
    <snip>
    Aha, Patterns. I love those...
    Mike already discovered one ("The Singleton"), but there are countless others. The great thing about these is that it enables you to re-use design work done numerous times before.
    But it can also be (in my view) a bit dangerous if you're just starting out with OO for the same reasons it is dangerous to just grab code of the net. When you're just starting OO and you're pressed for time, you might not always have the time to study WHY the pattern is as it is and in which situations it is useful (and in which it is not).
    As always, take time and care to learn and understand what you are using.

    Originally posted by voostind
    2. Components
    <snip>
    In my opinion, component based programming leads to compact and fast classes,
    while using the basic OO-rules might still result in big, slow classes.
    <snip>
    True, the component-concept adds to the basic OO-rules and can help you alot. I tend to see it as the next level of OO.
    But be careful to have the right mindset from the start, I've seen components that suffer from the same diseases as 'traditional' OO: too much responsibilities, bloated code, slow, ....

    Originally posted by voostind
    3. And now to PHP
    <snip>
    ... in PHP, it's best not to go to deep when using inheritance.
    I can tell you from experience this statement is true. The more files a script has to include and evaluate, the slower the thing gets, even if most of the code in the included file is not executed. And there is ofcourse also the memory overhead to consider.
    In PHP the balancing act between good OO and performant code can be very tricky to perform.

    Originally posted by voostind
    - Evaluating and executing code at run-time
    <snip>
    PHP isn't pure OO, but if you use it the right way, it can be very powerful, compact and subtle, indeed!
    True, thanks to the nature of interpreted languages (and the possibilities this offers at run-time) I find it much easier (or more elegant at least) to implement the Factory pattern. And this also helps in balancing good design and performance.
    In fact, Mike used these capabilities in one of his examples (the one where he abstracted the general search-functionality).

    Originally posted by voostind
    To conclude: PHP isn't pure OO, but on the other hand it has some features compiled languages do not. If you use these features wisely, you can end up
    writing perfectly good OO-code. However, there are many pitfalls (like using global variables) that seem like features, but actually are not. It's only
    experience that tells you what's right and wrong!
    As I remarked in the beginning: always take time to learn and understand concepts, methods and technologies instead of blindly rushing in and copy-paste stuff (code, pattern, advice, ....).
    Do not be afraid to make mistakes either. It is a normal part of a learning process and offers you a chance to become better.

    Originally posted by voostind
    4. Some notes on this thread
    - Error handling
    <snip>
    - Global variables
    <snip>
    Error handling:
    True, it was overlooked a bit. But also note that Mikes example you refer to is not what he finally went with. He brought it up during the interface discussion to illustrate the different ways he could interprete the interface concept.

    Global variables:
    Yep, I agree this is an indication the design is not yet quite mature. Funny thing is that that example was also what gave birth to this thread.
    So it seems that although we covered a lot of ground and introduced many concepts, some of us (by that I mean me) failed to make everything as clear as it should have been. Mea culpa....

    On the other hand, it does keep the thread going...

  23. #48
    SitePoint Enthusiast
    Join Date
    Oct 2001
    Location
    London
    Posts
    26
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    On not using globals.

    I've looked around deja and found some comments regarding globals. I can't remember who the original authors are and have just cut and paste a few together to illustate the point. I hope they don't mind.

    Right. OOP is the best way to think out of the 'globals' box. All data should belong to an object. It's your job to decide which object has the responsibility for it. If something goes wrong, you look to the object responsible first. It's a matter of accountability, and damage control. It's much harder to be find a bug if data is accessible everywhere.

    Your most valuable asset, as a programmer, is your attention span. Globals reduce this considerably. Globals are not bad in themselves. You merely become sub-optimal when you use them.
    and

    And the point is, that even if you work alone, you will eventually work on a larger project, and then you will have forgotten about some of the globals. As time goes on, you become a different person, so if you work for any length of time, you are not working alone. (This is not just philosophical claptrap. You really do forget, and very quickly. If you encapsulate your code and data, then you only need to look in one place to get up to date.
    and

    Global variables destroy the notion of encapsulation, an important concept for object oriented programming. At some point in the future you may decide that a variable must be changed in type.

    A bool might become an int which holds an error code for why it may be false, or an int becomes a floating point value to be more efficient in a calculation. A structure might be modified. If you access the
    variable from all over, you have to hunt down every use and make sure it stays correct. Or you may decide you really have to guard against invalid values for the variable, a technique that is easily performed if you have a function or a class method to execute the change.

    Finally, you have a very difficult time debugging because it becomes very difficult to tell just when and why the variable might have changed
    its value.

    So using global variables is usually bad programming practice, although perfectly correct language usage.
    and

    > I dislike the GLOBAL statement in that many of the bugs that get me scratching my head are to do with when I have forgotten to use it.

    Then you're probably using it way too often.

    Global scope variable are inherently dangerous and cause more problems than they're worth. I've been in this business well nigh 30 years now and have had more problems with globals than I can shake a stick at. Passing parameters ain't a bad thing, you know, even when what you're passing is a variable declared in global scope (like variables set via post or get).
    Global is inelegant by definition ...

    I worked in Fortran 77 for 10 of those years; we were really careful with common blocks and included them only where absolutely necessary. Passing array blocks was in almost all cases a "better thing" and was not any slower.

    Ok. I get it. Don't use globals. Need to encapsulate. Might get changed by other programmers. Hard to debug. etc. etc.

    Ummm... so does that mean that all those (urkkkkkk.. global ) system variables I er... need should be in a system class. E.g. previously they were in a globals file:


    // Miscellaneous date variables
    $system[years_active] = "1998 - 2001";
    $system[todays_date] = date("Y-m-d");
    $system[template_directory] = "/path/to/template/directory/";

    // Texis engine
    $system[texis_host] = "host";
    $system[texis_port] = 81;
    $system[texis_get] = ".../path/";
    $system[texis_time_out] = 20;

    // Mysql database
    $system[mysql_host] = "localhost";
    $system[mysql_username] = "username";
    $system[mysql_password] = "password";
    $system[mysql_port] = 3306;

    Do these all go into a system class now? But then won't I have to pass that system class to any object I call. And what about the user variables, and the database. It all ends up becoming:

    $new = new object(&$system, &$user, &$database, ... who knows how many others!);



    Mike
    Last edited by mikemindel; Oct 26, 2001 at 08:50.

  24. #49
    SitePoint Enthusiast spoorw8er's Avatar
    Join Date
    Oct 2001
    Posts
    56
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    .... (speechless) ....

    Originally posted by mikemindel
    I'd like to point out to the thread that Vincent has an excellent set of articles/php classes of his own over at http://www.students.cs.uu.nl/people/voostind/index.php called Eclipse.
    Wow, just had a quick look at that link and I'm already impressed big-time. Well-written articles, clearly explaining general OO concepts and relating them to PHP.

    But what really caught my eye was ECLIPSE.

    Being a lazy person that does want to re-invent the wheel I'm a big fan a using class-libraries. I have been using PEAR (and PHPlib) on and off for a while now, but to be honest, I never felt comfortable with those. Main reason is for this is that they both suffer quite badly from the 'all singing all dancing magic can do everything' class syndrome, which leads to bloated code and bad performance (and classes that are difficult to use for the basic functionality they were intended to provide).

    I'm gonna download ECLIPSE and have a good look at it, seems a much cleaner and more to the point implementation of what a good class-library should be.

    Go Vince Go

  25. #50
    SitePoint Evangelist
    Join Date
    Oct 2001
    Posts
    592
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Calm down people!

    Well, I think you're very nice and all by saying I've written some nice articles and implemented a nice library, but it's all far from perfect, believe me!

    Concerning ECLIPSE: I'm currently working on a new version (as stated on the website). In fact, it has been up and running for two weeks now, without any problem. It's the documentation that's not finished yet.

    The new version (2.0), will differ a bit from the old one in a few respects:
    - methods are named differently. At first I like the concept of 'doThis' and 'doThat', but as it turns out, it's rather ugly. Now I use just 'this' and 'that'. Of course 'get'- and 'set'-method remain the same.
    - some classes have been renamed.
    - inheritance is used a bit more. All iterators - for example - now have class Iterator as their superclass. Main reason for this is to make the API more clear.
    - The Database and Query classes are no longer just for PostgreSQL, MySQL is supported now too. This was achieved by adding a few extra classes and - again - using inheritance.

    Although I'm a bit against using inheritance in PHP, the layer is still very thin, and extensive testing (by me) has pointed out that the code doesn't run any slower.

    Anyway, what I'm trying to say is: don't rush off and use Eclipse 1.3 right away. Instead, wait for 2.0 (to be released in a day or two). It'll be even better. I promise :-)

    Vincent


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
  •