Forum Moderators: coopster

Message Too Old, No Replies

Understanding Custom Functions and Variable Scope in PHP

         

MatthewHSE

9:11 pm on Jan 7, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



So I've been into PHP for awhile, wrote a couple extremely basic scripts, and am now getting into some of the more complex types of scripts. I'm finding that custom functions will come in very useful - but I'm having a terrible time understanding the things! I do okay with the ones that don't take parameters, but I have need of a few that will involve parameters and I'm just lost. I keep feeling like I almost have a grasp on the concepts involved, only to find that I haven't quite gotten around it yet. In particular, I don't get how to set what parameters a function can take, then how to use them in different instances when variables - either "inside" or "outside" the function - need to come into play.

Could somebody take pity on a poor distraught soul and share a few parameter-including, custom functions with some comments on how and why they work, and an example or two of how they work in context? I learn best through examples with explanations, and my books aren't quite cutting the mustard in this respect.

Any and all help will be greatly appreciated.

Thanks,

Matthew

ergophobe

10:07 pm on Jan 7, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I'll bite. I'll start things off with some super basics and everyone can start adding in from there. If it's too basic to start with - sorry, we'll get there.

function definition: the function script that you write
function call: an instance of the function being used

A function without arguments


function myfunc()
{
return "this doesn't do anything";
}

...

echo myfunc(); // returns "this doesn't do anything"

So far so good.

Now, if I add arguments, they must be of the same type and number in the function call as in the function definition.



function myfunc($myarray, $myvar)
{
$return_val = "";
if ($myvar)
{
foreach($myvar as $val)
{
$return_val .= $val;
}
}
return $return_val;
}

$myvar = true;
$myarray = array("three plus ", "one ", "equals four");

echo myfunc($myarray, $myvar); // returns "three plus one equals four"

If $myarray were set to a simple string rather than an array, it would crash. If one of the two arguments were missing, it would also crash. so sometimes we want to make the arguments optional.



function myfunc($myarray, $myvar=false)
{
$return_val = "";
if ($myvar)
{
foreach($myvar as $val)
{
$return_val .= $val;
}
return $return_val;
}
else
{
return "I don't work for free";
}
}

.....

$myarray = array("three plus ", "one ", "equals four");

echo myfunc($myarray); // returns "I don't work for free"

Now $myvar is undefined, but it doesn't crash because it has a default value.

Next lesson: SCOPE and returning values...

[edited by: ergophobe at 3:27 pm (utc) on Jan. 8, 2005]

ergophobe

3:16 pm on Jan 8, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Sorry, I didn't get back to "scope" yesterday and can't today... anyone can join in. Otherwise, scope on Sunday.

MatthewHSE

4:40 pm on Jan 8, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Hi, I appreciate the explanation above and am looking forward to some further insight on scope. (Although I think I already have a pretty good grasp on that after banging my head against the wall all last night . . . )

Even with your concise explanation above, though, I find I'm still having difficulty creating functions that "do" things. For instance, in a script I'm working on now, I'll frequently need to remove underscores from variables and replace them with spaces. I've tried several things, including this:

<?php

$string = "string_string";

echo "<p>$string</p>";

function remove_underscores($string)
{
global $string;
eregi_replace('_', ' ', $string);
}

echo remove_underscores($string);

$string = remove_underscores($string);

echo "<p>$string</p>";

?>

I would have expected this to replace underscores with spaces in any variable named $string. But, it doesn't. Even more useful would be a function that would replace underscores with spaces in any variable, regardless of what it's named. How might I go about doing this?

Someday I'll be the one to answer basic questions like this one, eh? ;)

jshpro2

5:21 pm on Jan 8, 2005 (gmt 0)

10+ Year Member



<?
$string="hello_world";
echo (spaceformat($string));

function spaceformat($str) {
$str=str_replace("_", " ", $str);
return($str);
}

echo (spaceformat($str));
?>

In most programing langauges, things have values, for instance the integer 5 has the value of 5, the string "hello" has the value of "hello", the variable $test has whatever value you tell it to have, these 'values' are what the compiler, or parser "returns"

For instance the function:
float sqrt ( float arg)

Takes a float, and returns the square root of it. When you write your own functions you have to let the compiler know what to return, that is if you want to return anything. This is achieved with the return(); function I used above.

Check out [us3.php.net...]

You might want to stay away from globals for now, classes are better for that in my opinion, but before you learn classes you should learn functions.

Elijah

5:31 pm on Jan 8, 2005 (gmt 0)

10+ Year Member



Hi MatthewHSE,

The reason your script was not working was that your custom function "remove_underscores" was not returning a value.

To have a function return a value you need to have a "return" statement which returns a string or variable.

Also the "global $string" statement is not needed because the function already has access to $string because it is passed as a parameter.

The code below should do what you were trying to do:


<?php

$string = "string_string";

echo "<p>$string</p>";

function remove_underscores($string)
{
// global $string;
[b]return[/b] eregi_replace('_', ' ', $string);
}

echo remove_underscores($string);

$string = remove_underscores($string);

echo "<p>$string</p>";

?>

Hope this helps,

Elijah

ergophobe

1:35 am on Jan 10, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



This has sort of been dealt with, but actually, where you are coming up short is, in fact, in understanding scope. I know it's been explained by the previous posters, but since it's giving you trouble, I'm going to try to take you through it so you'll really understand it, trying to be as clear as I know how. If it sounds a bit redundant or long-winded in places, apologies in advance. I want you to really get it, though, because not understanding scope will really hold you up.

Rather than links throughout, I have a list of relevant links to the PHP Manual at the end.

Variables in PHP are either local or global in scope. Knowing the "scope" of a variable essentially tells us from where we can access a variable.

- variables declared in the main script and not encapsulated in a function of class, are global and are avaiable everywhere in the main script.

- variables declared in functions or classes are local to that function or class, and are only available within the function where they are declared.

This last point is fundamental - the most important concept with respect to functions and scope is to understand that variables within functions are local unless otherwise declared. What does this mean? Essentially, it means that the problem you came up against is entirely predictable if you understand variable scope.


function myfunc($a)
{
$a++;
}

$a = 10;
myfunc($a);
echo $a;

Q: What's the output?
A: 10

Why? Because the $a within the function and the $a outside the function are different variables. They have nothing to do with each other. As far as the PHP processor is concerned, the fact that the have the same name is entirely coincidental. To put it figuratively, the fact that I tell a secret to one guy named Matthew out in the lobby does not mean that every guy named Matthew in each and every office now knows that secret.

So what do you do? As the previous posters mentioned, you must both pass and return a value. Note, in the previously example I did not pass a variable to the function, I passed a value that was then assigned to the local variable, coincidentally named "$a" within the function. However, no value was returned, so the function does nothing. Once I leave the function, the local variables are thrown away never to be seen again. That is fundamental to understanding functions and scope.

It's also a great thing for creating trouble-free scripts. What this means is that if you and I are working together, I can write a main script that needs a few functions. I tell you what arguments the functions takes and what values it needs to return. You can then use any variable names you want within the function without having to see, know or understand my script. There will be no conflict.

If I want the variable outside the function to be modified, I must assign a new value to it based on the return value of the function.


function myfunc($b)
{
$b++;
return $b;
}

$a = 10;
$a = myfunc($a);

echo $a;

Q: What's the output?
A: 11

This is because we passed the value of $a into the function, and the passed the value of the local variable $b back out to the main script and then assigned it to the original variable $a.

This mechanism gives us excellent control of how our functions interact with the rest of the program and allows us to avoid unintended consequences.

Now, what if you actually do want the function to interact with values from the main script without having to pass every one as an argument? I try to use one of two mechanisms:

1. if the value will be constant, define a constant in the main script. Because a constant can't be changed, there are fewer worries about unintended consequences and therefore constants declared in the main script are available to all functions. I use constants a lot for configuration values. If possible, I always use constants rather than variables.

2. Use the so-called super global arrays where appropriate. These are the $_GET, $_POST, $_COOKIE, $_SESSION arrays. If you had been at this a bit longer, you would remember when PHP required you to pass $HTTP_*_VARS to every function. If you play around with a lot of scripts that have been around for a while, you'll still see these $HTTP_*_VARS. These arrays have been deprecated and that saves us passing get and post vars all around.

If neither of those do it for you, there are still two more methods that allow you to act directly on a variable from within a function

- pass by reference
- global declaration.

Remember how I said that normally you are passing the value of a variable to the function? Well, it is also possible to pass a reference to the variable. Essentially, you can think of it like a phone number A that rings through to a given phone, which also rings when someone dials B. Two separate numbers, one phone. In PHP, B does not point to A and vice-versa when one "references" the other. Rather, they both point to the same place. Whether you call A or B, you interact with the same person. whether you call it $a or $b, they both point to the same data (not the same value, but the same actual data). In PHP, we accomplish this by adding an "&" in front of a function argument in the function definition, not in the function call.


function myfunc(&$b)
{
$b++;
}

$a = 10;
myfunc($a);

echo $a;

Q: What's the output?
A: 11

Why, because $b is the "ring through" number and even though we've called it something else, it's acting on $a.


function myfunc(&$b)
{
$b++;
}

$a = 10;
myfunc($a);

echo $a;

Q: What's the output?
A: 11

Remember I said that variables in functions are local unless otherwise declared? There is another mechanism that allows a function to interact with a variable. Within a function we can also declare a variable to be global, meaning that though it appears in the function, it's really the same variable as the variable of the same name that exists in the global scope. In other words, the fact that the variable has the same name within the function and outside it is no longer a coincidence, as far as the PHP processor is concerned. Rather it is the same variable. This time when I tell a secret to Matthew, it's always just one guy.

I use pass by reference primarily for passing large arrays or class objects to functions, largely to save the overhead of creating a copy (this is no longer necessary with classes in PHP 5).


function myfunc()
{
global $a;
$a++;
}

$a = 10;
myfunc($a);

echo $a;

Q: What's the output?
A: 11

Why, because this time we are in fact modifying the same $a inside the function as the one we're echoing outside the function. Notice that I do not pass the variable as a parameter if I am also declaring it as global as Elijah already advised.

Honestly, I almost never use "global" if I can avoid it; I never use it just to save on typing. It's just one of those things - when I took my first FORTRAN and Pascal courses years ago, the profs always said to avoid "global" and "goto" or your programs would get hard to follow, and it still seems like good advice to me, but that's just my opinion.

Finally, I said that the value of a local variable within a funciton was thrown away when the function was done. What if, however, you want the value to persist? In that case, you declare a static variable;


function myfunc()
{
static $a=1;
return $a++;
}

echo myfunc() . ", " . myfunc() . ", " . $myfunc();

ouptut: 1, 2, 3

Because we've told the PHP processor not to throw the value away, it persists between function calls and the persistent value overrides the default value assigned the first time the function is called.

FROM THE MANUAL

Variable Scope [php.net] - includes information on the use of "global", "static" and references.

Superglobals [us3.php.net]

Passing by reference [us3.php.net]

mcibor

10:31 pm on Jan 10, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Thanks ergophobe for telling about myfunction(&$var)
I didn't know how to use that. (I knew i C++, but not in PHP)

There's one more nice thing about functions. Function can call another function, or be recursive: call herself.

To count 7! you can use function:

<?PHP

if(!function_exists('f'))
{
function f($number)
{
$number = (int)$number; //conversion to int
if($number) return $number * f($number - 1);
else return 1;
}
}

echo "A ".f(0)."<br>"; //A: 1
echo "A ".f(1)."<br>"; //A: 1
echo "A ".f(3)."<br>"; //A: 6
echo "A ".f(bla)."<br>"; //A: 1
echo "A ".f(5bla)."<br>"; //A: 120
?>

I always use function function_exists, because you can't declare one function twice in PHP.
Also here is important to place (int)$number for the function to be foolproof.

However ergophope? How to do this:
function f((int)$number)
{}
?

It's not working with me.

Best regards. Hope it was a nice example with recurency. But beware! Endless loop will cause your php to stop working - you'll never get result. I did that once and had to restart php on the server :)

Michal Cibor

ergophobe

11:53 pm on Jan 10, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



PHP is such a loosely typed language I don't think you cna do that directly in a function definition. If you want it to actually throw an error if an argument is of the wrong type, you have to test for type within the function and then return an error code of your own making.

If you just want to make sure the argument is an integer, you have to cast it as such within the function. Usually this is done automatically. Your recursive function, for example, would work fine in PHP without the explicit casting as an integer. As soon as you do an operation like

$number -1

you have effectively cast $number as an integer.

As for recursion, you have to watch out not just for endless loops, but for eating up huge amounts of memory. I was pretty good at solving all sorts of things through recursion after doing some Scheme programming (which has no looping constructs other than recusrion - so no for or while loops). I don't find I use it much in PHP.

MatthewHSE

12:08 am on Jan 11, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Wow, ergophobe, thanks for that extremely detailed explanation! There's a bit more to scope than I realized, I guess, and I see what you mean about how important it is. I haven't had time to totally absorb everything you wrote above, but I'm printing it out and will read as much as I can this evening. (I tend to learn something well if I read it just before bedtime -- go figure!)

I'm sure I'll have more questions later, but I'll wait until I understand the issues discussed so far. Thanks again for helping out so much! ;)

twist

8:08 am on Jan 11, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



One nice thing about learning programming in C is that you have to define and declare everything (it's been awhile but I am pretty sure that is how it was, feel free to correct me if i'm wrong).

For example, in C you had to define a variable as global and you always had to type cast variables. Nice thing about php is they cut all the redundecy out by eliminating the need to do these things, which is why I could see if you have never used a programming language like C, or possibly Java, it could become very confusing.

You might think about backtracking and picking up a beginner C or C++ book and going through the motions. It will just make more sense because it forces you to define and declare everything. You may be able to get through functions but you might run into much more confusion when you start trying to build classes in php.

ergophobe

8:36 pm on Jan 11, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Matthew. You're welcome... does this mean you forgive me for dissing Firefox ;-)

[edited by: ergophobe at 9:35 pm (utc) on Jan. 11, 2005]

sonjay

8:41 pm on Jan 11, 2005 (gmt 0)

10+ Year Member



Yes, thank you, ergophobe. Your explanations were better, I think, than any I've read anywhere else. This one's a keeper.

MatthewHSE

9:17 pm on Jan 11, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Well, after reading your post a few times last night and this morning, ergophobe, I'd have to say it's going to be taped into the back of one of my PHP books. I spent part of today writing a few meaningless functions and working with them just to get the hang of things, and I think I understand them pretty well now. Doubtless the experience I'll gain in the coming weeks, months, and years will improve my understanding, but thanks to you I now am comfortable using functions that "do" something.

So yes, I guess I'll have to forgive you for slamming my favorite browser! ;) Actually I know very well you weren't really slamming it; I always love a good FireFox discussion anyway. ;)

On another note, I am having a little difficulty with one of my custom functions. I don't understand its behavior at all. More details here [webmasterworld.com].

Again, many thanks for your help!

Matthew