I'm a huge fan of code re-use. But in PHP, some of the things I want to re-use by putting them into functions just won't work because I global variables are not accessible. I wonder if I'm simply not thinking of things the right way, and I'm hoping some of you PHP super-gurus can help straighten me out so I can write cleaner, more professional code.
I'll give two examples of how the situation with globals frustrates me. The first has an easy workaround. The second doesn't.
EXAMPLE 1: database querying inside functions.
normally, I have a file (commoncode.php), which contains:
$dbname = "my_database";
$loginuser = "my_username";
$loginpass = "my_passwd";
$id_link = mysql_pconnect($dbhost, $loginname, $loginpass) or die("Could not connect");
Now, every file just says include_once(commoncode.php), and from then on I can just say:
$result=mysql_db_query($dbname,"SELECT etc..",$id_link);.
And that's great. Except I can't do it in a function, because if I try to query from inside a function, I suddenly can't access $dbname and $id_link anymore!
Which means almost every single function in my entire application has to start with:
global $dbname, $id_link;
Before I can query anything. To me that's frustrating ... having to retype the same line at the beginning of every function defeats the purpose of code reuse. The solution here is obviously to wrap mysql_db_query:
function MyQuery($querystring)
{ global $dbname, $id_link;
return(mysql_db_query($dbname,$querystring,$id_link));
}
Which isn't so bad, but it feels strange that it's necessary. I'm wrapping a function just so I can declare two variables global. I wonder if I'm missing something.
EXAMPLE 2: classes and variable variables
It's very useful to me to have variables with the same names as my database fields. That's easy to do: I create a class called "tableClass" with an array called "columns". Then I make subclasses, one for each of my tables, where the column names get defined in the constructor. (say it's "rolodexClass", then $columns in that class might be an array containing "FirstName", "LastName", "phoneNumber" etc., where those are the names of my DB columns). Then I can do something like this:
$row = mysql_fetch_row($result);
$table = new rolodexClass;
for($col=0; $field = each($table->columns); $col++)
{
$$field=StripSlashes($row[$col]);
}
echo("Name is $FirstName $LastName");
That's extremely convenient; now I have variables $LastName, $FirstName, with the same names as my database columns. Much easier than keeping track of $row[0], $row[3], etc.
But I use it constantly (like after every query), and my decoding function is more complex than just "stripslashes"; it depends on the column type. The inside of the for loop in the previous example is actually about 8 lines long, and I tweak it from time to time. So to save myself typing, and to do good code reuse, I'd like to put it in a function.
But now I'm stuck. Because that code creates a bunch of variables with new names. So if I put it in another function, the calling function won't have access to those variables. Moreover, there's no good way to return those variables, because the generic code above doesn't know how many there are or what names they have!
The best solution I've found is for both functions to use a loop to declare all those variables global:
class tableClass
{
function loadVariablesFromRow($row)
{
for($col=0; $field = each($this->columns); $col++)
{ global $$field;
$$field=StripSlashes($row[$col]));
// actually a much more complex decoding of the query content here
}
}
}
function someOtherFunction()
{
$table = new rolodexClass();
$row = MyQuery("SOME QUERY");
while($field=each($table))
global $$field;
$table->loadVariablesFromRow($row);
// now I finally have my variables
}
So basically, having to jump through the hoops of putting that while ... global loop in every function seems to defeat the whole point of code reuse. How useful is code re-use if I end up with another set of code that I have to type, exactly the same, every time I call loadVariablesFromRow()?
So is there something missing here? It feels like if I want to use functions to re-use code, then suddenly all my variables become inaccessible. Because I want to use variable variables in a reusable, generic way, there seems to be no good, generic solution.
Any suggestion from the PHP gods out there? I've thought of putting that little while ... global $$field loop into a separate file and using include() ... but that feels somewhat clunky. I've also thought of just storing my results in an associative array, with the column names being the keys. Then the function loadVariablesFromRow could return an array, solving the problem. But that means I have to use $data_array('LastName') instead of just $LastName in my code, which means an awful lot more typing in a huge application.
Thoughts appreciated!