Accessing class instances

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Accessing class instances

Post by Benjamin »

This is split from: viewtopic.php?f=19&t=83441 so we don't hijack onions thread.
Mordred wrote:
astions wrote:Yeah either way. You just need to get an instance of it over the other class somehow. In my framework I use a kernel that in addition to a number of other things, holds an object of every class. So I can call any method of any class from any place and it saves a ton of memory PLUS it's very quick.
So, basically you've invented global functions with lots of -> thrown in.

I'm all for the global functions to access "kernel" functionality, but I hate typing useless syntax sugar around, just because PHP doesn't have any reasonable scope modifiers (and no, 'global' is not reasonable).

For example instead of $this->kernel->db->query("blah") I do DBQuery("blah"), you do the math on how much typing it saves and how much readability it adds. I do have a DB object that I can pass around should I ever want to use two DB connections simultaneously, but I've yet to come to this need. Instead, with this wrapper of the "default" db connection, I cover 99.999% of use cases with significantly less typing and maintenance code. I would not bear to type a hundred -> which will always have the same object on the left.
I can see how you would like that better, however I would rather type $this->kernel->blah than create tons of individual functions for every class or common task. I'm a very fast typer so it really doesn't bother me too much. Besides, it's less typing that having to create new instances of each class all over the place.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Accessing class instances

Post by Mordred »

Ah, but you do create tons of functions, only in the form of methods. If only would PHP allow us to define superglobals, I would happily write $g_db->Query("foo") or $g_Log->Log("bar"). Alas, the only superglobal thing in our control is defining functions.

Don't get me wrong, I think your method is the next best thing (or the prev best thing? :) ), I use something similar in one or two cases. The point is, there are things, global in nature, that PHP would not allow us to handle easily, so we have to work around the limitation:

1. Use 'global'
2. Use registries, etc. (your method is essentially this, only in a nicer wrapping)
3. Use members, passed to the object at construct time
4. Use functions

Deffects:
1: Cumbersome to use, fails invisibly (i.e. with delayed effect) if we forget the global. Major no-no.
2: The problem is ofset to only one object - the registry, kernel, etc.
3: Treating globals like members. Could be a positive effect in unit testing, and nowhere else (is this correct?)
4: Possible namespace clashes

2 also has this workaround: the thing which you do with "kernel" and I do with my "response" object is to assign it to a member by reference in the constructor, and access it as $this->kernel->foo. Only one place to use a 'global', and the $this->, while a bit awkward to type is bearable.

All of these (except arguably 3) are not the best solution, I would prefer that PHP had:
- user-defined superglobals (what the other languages define as 'global')
- Namespaces

Then 1, 2 and 4 would had 'proper' and easy ways to use, and 3 would be the alternative if you wanted unit testing.

The reason I prefer 4, is that I don't do unit testing - there's little in web coding that could use some IMO, mostly related libraries like Swift or HTMLPurifier. Oh, how did I want to be able to unit test some of my projects, I had to write custom debugging solutions for a proxy server, because you had to be able to test and examine it while it is active. In the end I wrote a second server in the same process, that I connected to with telnet and which would interpret commands to examine the internals of the proxy. (Yeah, I know, uphill both ways in the snow, etc...)
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Accessing class instances

Post by Benjamin »

I think I understand what your saying. Let me tell you where I am at and then I will respond.

See this line:

Code: Select all

 
if (false === ($record = $_KERNEL->db->turbo_row("select user_id from auth where username = 'sample' and MD5('password') = 'test'")))
{
    throw new Exception('Invalid username or password');
}
 
I think I'm about as lazy as any other programmer. My goal is to write the least amount of code to do the most amount of work. Your right that the functions are there, but they are organized into classes. This is what that line does in my framework.

1. If the database class doesn't exist it is autoloaded (ie the file containing it is located and included) and instantiated
2. Depending on the configuration, it will either load a mysql or mysqli version of the database class
3. If their is no database connection, a database connection is established
4. The turbo_row method is called
5. If the query fails or no records are returned false is returned
6. The first record is placed into a variable and returned, which is then assigned to the $record variable
7. The mysql result is freed
8. If $record === false an exception is thrown.

I automate a lot of common tasks from within my framework, which is something I wouldn't be able to do with functions, unless each function explicitly performed a specific task.

This line does the same thing, except it will get 50 article titles from starting from page 1 and return the total results, total number of pages etc, all with a single database call and single line of code.

Code: Select all

 
if (false === ($records = $_KERNEL->pagination->get_page("select article_title from articles", 1, 50)))
{
    throw new Exception('There are no articles to display.');
}
 
Now don't get me wrong, if I could make $_KERNEL a superglobal I would, but the way I lay things out I don't need to. The $_KERNEL variable is available everywhere it is needed automatically. To save typing I could change it to $_k or something but I'm not too concerned as it's already saving me loads of typing.

If there was a way to capture undefined function calls before php throws a fatal error I could probably do some neat stuff with that, such as create the function on the fly based on some sort of naming convention.

I think in the end user defined superglobals would come in handy for experienced programmers, but for beginners it would be a major step backwards. I once saw a CMS system where the guy who wrote it used $_SESSION for EVERY single variable, and yes, some very difficult to diagnose bugs cropped up. In the end I rewrote it from scratch.

As far as unit testing goes, I don't unit test at all. I learned PHP at a young enough age that I feel like it's a second language. I can read code and I do a pretty good job at processing it in my head as I'm reading it. There are unique situations however. Last week I wrote a bitmapper class, which probably isn't over 15 lines of code that I wrote tests for and tested for about 5 hours. I tested a variety of input, took it and put it in the database, pulled it back out and made sure everything came back out ok. This lead me to find an issue with number_format that I posted here the other day.

Going back to my framework however, one thing that used to drive me nuts with other frameworks is that I would see new classname() every where. Even nested, new classname(new plugin()). These frameworks were slow and used tons of memory. I think even PHPBB needs something like 8mb of memory or something? With my framework nothing is loaded (included) unless and until it's needed, it won't even connect to the database until the first query is executed. No query = No need to connect. Classes are reused and there really isn't bloat. I just can't do this with functions.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Re: Accessing class instances

Post by John Cartwright »

Going back to my framework however, one thing that used to drive me nuts with other frameworks is that I would see new classname() every where. Even nested, new classname(new plugin()).
So you are suggesting that creating objects, and passing objects to other objects is bad practice? Small, compact, concise, testable, and loosely coupled are what makes excellent code -- not the other way around :banghead:. You are basically arguing against PHP being used in an OOP context, which is not an argument at all, but if you want to argue that procedural code will be faster than OOP code then sure (but you are losing many benefits).
These frameworks were slow and used tons of memory. I think even PHPBB needs something like 8mb of memory or something?
I would not use phpBB or phpBB2 as examples of well written code. It's a procedural mess, and it's memory usage is a joke. Although, their bottlenecks do not come from their usage of OOP though.
With my framework nothing is loaded (included) unless and until it's needed, it won't even connect to the database until the first query is executed. No query = No need to connect. Classes are reused and there really isn't bloat. I just can't do this with functions.
Mine too :D, except we don't use any functions.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Accessing class instances

Post by Benjamin »

Jcart wrote:So you are suggesting that creating objects, and passing objects to other objects is bad practice? Small, compact, concise, testable, and loosely coupled are what makes excellent code -- not the other way around :banghead:. You are basically arguing against PHP being used in an OOP context, which is not an argument at all, but if you want to argue that procedural code will be faster than OOP code then sure (but you are losing many benefits).
No not at all. I'm saying that creating 5 instances of 10 classes all over the place (50 instantiated classes) is bad practice. Sure the code might be organized, there's nothing wrong with that. The problem for me lies in the efficiency. I've found a way to keep my code organized while maintaining a high level of efficiency (cpu & ram) and increased productivity. I disagree that I am arguing against OOP. I believe I am arguing against a style of OOP which happens to be mainstream.

EDIT: I'm not sure you read what I wrote. I'm not the one advocating functions here.
User avatar
Eran
DevNet Master
Posts: 3549
Joined: Fri Jan 18, 2008 12:36 am
Location: Israel, ME

Re: Accessing class instances

Post by Eran »

Efficiency is having the luxury to optimize comfortably when needed. Maintainability is a far more common need than optimization, and leads to much easier optimization when the need arises.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Accessing class instances

Post by Benjamin »

I'm having my cake and eating it too. Nom Nom Nom.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Accessing class instances

Post by Christopher »

astions wrote:I've found a way to keep my code organized while maintaining a high level of efficiency (cpu & ram) and increased productivity.
You actually mean, "... in my opinion." The thing about a solo programmer building a code base is that you certainly believe in your code, but there is no reality as to whether you could be more efficient or productive. There is a saying:

"Infinite intellectual brilliance is achieved as the size of the audience approaches zero." ;)

Working on a framework with others (and trying to get my head around all of the new and interesting ideas that other programmers have) has show me repeatedly that my designs are not always so efficient or productive. And I think my design sense is pretty good! :) I have come to the point where if I don't regularly discard one of my closely held design ideas for something better -- I start to worry...
(#10850)
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Accessing class instances

Post by Benjamin »

Well that's a way to look at it. I think that you can't prove and I cannot disprove that theory. I certainly do believe in my code, but I don't think that I think it's better than anything else just because I wrote it. We are getting a bit off topic though. Mordred and I were discussing functions vs a centralized method of storing class objects.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Accessing class instances

Post by Christopher »

Yeah ... I read that. I really don't see the functional difference between your $_KERNEL-> and the $this-> Helpers and or registry of an Action Controller. I do see a number of other differences though ...
(#10850)
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Re: Accessing class instances

Post by Maugrim_The_Reaper »

These frameworks were slow and used tons of memory. I think even PHPBB needs something like 8mb of memory or something?
phpBB needs a lot more than 8MB ;). I think on the framework issue you're missing the point though. People don't adopt frameworks to make faster, more efficient, mean applications - they adopt frameworks to make maintainable, predictable, stable applications as fast as possible. Development time is expensive, processing time is cheap. By the time you apply opcode cache, file cache, fix xdebug detected bottlenecks, nginx/lighttpd for file serving, blah, blah, blah, hosting the towering monster with more hardware is cheaper than becoming obsessed with how much RAM/CPU it's eating.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Accessing class instances

Post by Benjamin »

Yeah there is a fine line there. I generally stay on the right side of it. At least I try to.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Accessing class instances

Post by Benjamin »

Hey Mordred, why not just create some get & set functions that pull variables from a static class. Something like:

Code: Select all

 
function get($var)
{
    static $ram = ram::getInstance();
    return $ram->get_variable($var);
}
 
function set($var, $value)
{
    static $ram = ram::getInstance();
    return $ram->set_variable($var, $value);
}
 
That'd be an easy way to have global vars.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Accessing class instances

Post by Mordred »

I don't want just any var to be easily accessible as a global, I want those that are global in nature, which are really just a handful, but are used all the time.
That's why I propose something very similar to this, but in a more limited way. Your example above is a registry with global access functions. But we actually want to get only several things out of it. So, instead of accessing a global registry and get a global var from it - access the var directly! Have a set of functions for database, a set for logging, a set for profiling, aaand that's about it. If you want another, have another set. I actually only do it for the database and the profiler (which I don't really use anymore).

Here's a real example from my database class (I purposely cose the simplest function, so not do distract the reader from the topic of global access to the database class intrinsics):

Code: Select all

//We have a class:
class CMysqlDatabase {
//...
    function GetAffectedRows() {
        return mysql_affected_rows($this->m_connection);
    }
//...
};
 
//A global variable:
$g_pDatabase =& new CMysqlDatabase(); //supposing we want MySQL
 
//And an accessor function:
function DBGetAffectedRows() {
    global $g_pDatabase;
    return $g_pDatabase->GetAffectedRows();
}
You use it by just calling

Code: Select all

$nRows = DBGetAffectedRows();
What helps us keep the code short here is allowing the assumption that we only have one database connection - which is true almost always. The same applies to doing logging, profiling, etc.
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Re: Accessing class instances

Post by superdezign »

But in the concepts that OOP was built on, objects are created and the only objects with access to them are the ones that need access. It's the whole issue of "uses" and "has." If an object only outputs HTML, and it doesn't use the database to do that, then the object should not have access. Also, the objects that do have access won't be easily identified from the objects that don't, as any object can simply add that call.

Using include() for the necessary classes is considered better practice than the autoload capability created by PHP. Also, relying on a superglobal assumes that it has had a chance to be created. $_SESSION doesn't exist until session_start() is called. Building code which relies on another piece of code being run beforehand means that the code in question will need to do a check to make sure that the needed code has been run, which is an unnecessary overhead than running include_once().
Post Reply