Page 1 of 2

Accessing class instances

Posted: Sat May 31, 2008 11:55 pm
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.

Re: Accessing class instances

Posted: Mon Jun 02, 2008 1:05 am
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...)

Re: Accessing class instances

Posted: Mon Jun 02, 2008 3:42 pm
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.

Re: Accessing class instances

Posted: Mon Jun 02, 2008 3:58 pm
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.

Re: Accessing class instances

Posted: Mon Jun 02, 2008 4:49 pm
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.

Re: Accessing class instances

Posted: Mon Jun 02, 2008 5:13 pm
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.

Re: Accessing class instances

Posted: Mon Jun 02, 2008 5:19 pm
by Benjamin
I'm having my cake and eating it too. Nom Nom Nom.

Re: Accessing class instances

Posted: Mon Jun 02, 2008 5:28 pm
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...

Re: Accessing class instances

Posted: Mon Jun 02, 2008 5:43 pm
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.

Re: Accessing class instances

Posted: Mon Jun 02, 2008 7:02 pm
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 ...

Re: Accessing class instances

Posted: Wed Jun 04, 2008 3:20 am
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.

Re: Accessing class instances

Posted: Wed Jun 04, 2008 4:04 am
by Benjamin
Yeah there is a fine line there. I generally stay on the right side of it. At least I try to.

Re: Accessing class instances

Posted: Thu Jun 12, 2008 2:40 am
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.

Re: Accessing class instances

Posted: Thu Jun 12, 2008 3:14 am
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.

Re: Accessing class instances

Posted: Thu Jun 12, 2008 5:35 am
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().