php is often called a "functional language" because it relies on functions. that doesn't tell us much unless we understand what a function is and does, though.
a function is a block of code that can be called by another piece of code. it can take information in and return information. you can think of a function as the programming equivalent of "remember when i told you that story?". it allows us to write something once and use it as many times as we like. it also allows us to separate the code that does some things from code that does other things for organization.
imagine you're writing a program that processes multiple types of information. you can take all the functions for each type and put them all together, even if they're used all over the place.
before we talk about function organization and best practices, though, let's begin with creating some.
the syntax for a function starts with function then the name of the function (usually a combination of letters and numbers, case-sensitive, just like a variable), its parameters in () and its contents in {}. this probably sounds familiar because it's nearly the same syntax as loops.
we have already used many built-in functions like print_r, count and strlen so we know how to execute them. just the name of the function and its parameters in ().
function myFirstFunction($one, $two, $three)
{
print_r("the first parameter is " . $one . ".\n");
print_r("the second parameter is " . $two . ".\n");
print_r("the third parameter is " . $three . ".\n");
}
myFirstFunction("abc", "def", "ghi");
returns...
the first parameter is abc.
the second parameter is def.
the third parameter is ghi.
remember, php runs things in order. if you haven't created the function yet, you can't run it. it's usually a good idea to create all your functions at the beginning and keep them together.
as we progress into more complex examples, it's a good time to talk about errors. the default settings on apache and nginx are to hide errors. this is a good idea when you're presenting a site to the public and avoids potentially-dangerous information getting out to help hackers find ways into your server. for development, though, that's not great.
if you've been running these examples on the command-line, you might have seen errors already if you've made typos. command-line php interpreters usually show errors by default.
if you want to enable displaying errors on the web, however, you need to add a few lines to the beginning of your php file, right after the <?php.
ini_set("display_errors", "1");
ini_set("display_startup_errors", "1");
error_reporting(E_ALL);
this enables displaying errors, displaying startup errors and reporting all types of errors.
when you're ready to put your php programs out in the public, don't forget to remove these lines. there are other ways to enable error displaying but this is the simplest and the fastest to turn on and off as needed.
here's a hint. if you see an error, check to make sure every command in your program ends with ;. the most common issue by far for both beginners and experienced developers is forgetting a semicolon somewhere. it might not be the line that gives the error. if there's no semicolon, the program tries to run the next line as part of the command. if it fails, it might be on that line. but some commands really can run together -- often with unpredictable results. so you may get through more lines before you hit an error.
sometimes you don't need a function to write anything for the user. what you want is for it to hand the result back to the program. to do that, you just use the function the way you would any other piece of information you want to pass into a variable -- you can use = or do something with the value right away like printing it to the user.
function roundedRoot($number, $root, $rounding = 2)
{
$result = $number ** (1 / $root);
$result = round($result, $rounding);
return $result;
}
print_r(roundedRoot(500, 3));
returns 7.94. php has some built-in math functions but something you may need to do repeatedly in a program is find a particular root of a number and round it to a specific number of decimal places. this is a simple function to do exactly that.
it takes three parameters -- $number, $root and $rounding. we have added a value for $rounding and that means it is a default. if we don't pass that third parameter in, which we haven't in our example, it defaults to 2. if we set it, it is whatever we pass in.
note that these optional parameters with defaults must come at the end of the list. if they came anywhere else, php wouldn't know what was being skipped. putting them anywhere but the end will produce an error. you can have more than one but they all have to be at the end.
the first line of the function raises $number to 1/$root. in other words, it takes the nth-root of the number we passed in. in this case, it takes the cube-root of 500.
the second line of the function uses the built-in round function to round the number to the number of digits in $rounding, in this case 2.
the last line of the function returns the result.
when the print_r function calls roundedRoot, the result shows up and passed into print_r as if it was any other variable.
we use return as a way of exiting functions, not just at their ends. for example, in this function, we can check to make sure our root isn't too big. instead of letting someone perform any root, let's set the maximum to 10 and exit if someone tries to go higher.
function roundedRoot($number, $root, $rounding = 2)
{
if ($root > 10) {
return false;
}
$result = $number ** (1 / $root);
$result = round($result, $rounding);
return $result;
}
$output = roundedRoot(500, 3);
$output !== false ? print_r($output) : print_r("the root was too big.");
still returns 7.94 because $root isn't larger than 10. if we change the second-last line...
$output = roundedRoot(500, 30);
it returns the root was too big..
note that we used !== here instead of !$output or $output != false. you might already have guessed why. when we talked about break and continue, we found that 0 and false are treated as equivalents when we test for things. an empty string is also considered the equivalent of false. in this case, 0 is a real possibility that we might want to display instead of being treated as false. so we use !==, the identical check, instead of just !=, the equivalent check for false. that way, 0 is considered a valid answer but false is considered a failure condition.
you can pass a huge number of parameters if you want to but that doesn't mean it's usually a good idea. a good general guideline is that less than three is a good number of parameters but three or more is starting to get complicated and more than five is definitely heading for a nightmare when it comes time to check for possible problems.
of course, we often need to pass far more than three pieces of information into a function. that doesn't mean we need to use a large number of parameters, however. that's one of the many uses for arrays.
imagine you want to output information on a person -- their first and last names, email, height and weight. you could write a function that starts like this...
function showPerson($firstName, $lastName, $email, $height, $weight)
and that's perfectly valid php but it's messy. what happens when your program wants to display their hair and eye color, too. two more parameters?
function showPerson($firstName, $lastName, $email, $height, $weight, $hairColor, $eyeColor)
now you have to remember the order of seven and the list will only continue to grow. what happens if you pass their height, weight and email in the wrong order? an error, quite possibly lots of them. there's a far easier way, though -- one where things remain clean and order doesn't matter.
function showPerson($person)
{
return $person["firstName"] . " " . $person["lastName"] . " (" . $person["email"] . ") is " . $person["height"] . "cm tall, weighs " . $person["weight"] . "kg and has " . $person["hairColor"] . " hair and " . $person["eyeColor"] . " eyes.";
}
$samplePerson = ["firstName" => "jasmine", "lastName" => "park", "email" => "[email protected]", "height" => 174, "weight" => 51, "hairColor" => "blue", "eyeColor" => "brown"];
print_r(showPerson($samplePerson));
returns jasmine park ([email protected]) is 174cm tall, weighs 51kg and has blue hair and brown eyes.. now we've only passed one parameter into the function but it contains all the information. if we pass the information in in a different order, it still works. if we add new information, it doesn't break anything. and every piece of information is listed with its name instead of just in an order we have to remember or keep checking.
of course, it might feel silly to create an array then pass all that information into a function instead of just writing it. that's because you've only got one person. let's look at what happens when we have more than one using only things we've already seen.
function showPerson($person)
{
return $person["firstName"] . " " . $person["lastName"] . " (" . $person["email"] . ") is " . $person["height"] . "cm tall, weighs " . $person["weight"] . "kg and has " . $person["hairColor"] . " hair and " . $person["eyeColor"] . " eyes.";
}
$people = [];
array_push($people, ["firstName" => "jasmine", "lastName" => "park", "email" => "[email protected]", "height" => 174, "weight" => 51, "hairColor" => "blue", "eyeColor" => "brown"]);
array_push($people, ["firstName" => "katherine", "lastName" => "law", "email" => "[email protected]", "height" => 157, "weight" => 56, "hairColor" => "black", "eyeColor" => "brown"]);
array_push($people, ["firstName" => "susan", "lastName" => "yu", "email" => "[email protected]", "height" => 160.1, "weight" => 60.4, "hairColor" => "red", "eyeColor" => "gray"]);
array_push($people, ["firstName" => "jen", "lastName" => "bull", "email" => "[email protected]", "height" => 165, "weight" => 58, "hairColor" => "blonde", "eyeColor" => "green"]);
array_push($people, ["firstName" => "lori", "lastName" => "bratt", "email" => "[email protected]", "height" => 170.3, "weight" => 67.2, "hairColor" => "pink", "eyeColor" => "blue"]);
array_map(fn($person) => print_r(showPerson($person) . "\n"), $people);
returns...
jasmine park ([email protected]) is 174cm tall, weighs 51kg and has blue hair and brown eyes.
katherine law ([email protected]) is 157cm tall, weighs 56kg and has black hair and brown eyes.
susan yu ([email protected]) is 160.1cm tall, weighs 60.4kg and has red hair and gray eyes.
jen bull ([email protected]) is 165cm tall, weighs 58kg and has blonde hair and green eyes.
lori bratt ([email protected]) is 170.3cm tall, weighs 67.2kg and has pink hair and blue eyes.
now we've built an array of people's information and looped through it, passing each person to the function we wrote earlier to write their details. not that some of this information is integers, some floating-point numbers and some strings. php handles them all automatically and combines them into strings without needing to explicitly convert them.
sometimes it is useful to force the parameters to be specific types. for example, if you are creating a function that will be used many times in a program developed by a team, it might be important that only integers be passed in for some data but strings for others. you can specify the type when you create the function.
function whoAmI(string $name, int $age, string $email)
if someone calls this function with anything other than a string, an integer and a string, it will return an error.
when passing information around, however, it's usually more important to check individual variables inside arrays. you will often see...
function doSomething(array $parameters)
but what types are the values in that array?
we already know how to test for true/false and behave accordingly. all we have to do is find the right built-in function for each type we might want to check for. if we take what we already know and apply those tests to the function we just wrote...
function showPerson($person)
{
if (!is_string($person["firstName"]) || !is_string($person["lastName"])) {
return "sorry, that name is not a valid string.";
}
if (!is_string($person["email"])) {
return "sorry, that email is not a valid string.";
}
if (!is_string($person["hairColor"])) {
return "sorry, that hair color is not a valid string.";
}
if (!is_string($person["eyeColor"])) {
return "sorry, that eye color is not a valid string.";
}
if (!is_numeric($person["height"])) {
return "sorry, that height is not a valid number.";
}
if (!is_numeric($person["weight"])) {
return "sorry, that weight is not a valid number.";
}
return $person["firstName"] . " " . $person["lastName"] . " (" . $person["email"] . ") is " . $person["height"] . "cm tall, weighs " . $person["weight"] . "kg and has " . $person["hairColor"] . " hair and " . $person["eyeColor"] . " eyes.";
}
now we check each item in the array and return an error instead of our built result if something is the wrong type.
is_string checks to see if something is a string and is_numeric checks for a numeric value. this could be an int or a float but it is possible it's a string with only numbers inside.
other possibilities are is_array, is_int, is_float, is_bool, is_null and is_object. checking for strings, numeric values and arrays will likely become very familiar to you as you write more php.
most of the time, functions take information from the outside and the only thing that comes out is what is returned. if you need a function to be able to edit one of the variables passed in, however, that's possible. just add & in front of the parameter.
function roundToThreePlaces(&$number)
{
$number = round($number, 3);
}
$myNumber = 3.14159;
roundToThreePlaces($myNumber);
print_r($myNumber);
returns 3.142. $myNumber, because the parameter is prefixed with & when the function is created, is edited outside the function.
function roundToThreePlaces($number)
{
$number = round($number, 3);
}
$myNumber = 3.14159;
roundToThreePlaces($myNumber);
print_r($myNumber);
returns 3.14159. the $number inside the function never modifies the original variable passed in as a parameter.
if you want to access variables from outside the function without having to pass them back and forth, you can use the global command. this links the outside and inside variables together. remember, any function can access the global variables so it's not a great way to pass information. it is, however, a good way to access something that is set for the whole program -- in other words, globally.
$secretCode = "llamaTIGERfoxSQUIRRELgiraffe";
$secretShift = 6;
function shh($direction = 1, $code = "")
{
global $secretCode, $secretShift;
foreach (str_split($secretCode) as $letter) {
$code .= mb_chr(mb_ord($letter, "UTF-8") + $secretShift * $direction, "UTF-8");
}
return $code;
}
print_r(shh());
returns rrgsgZOMKXlu~YW[OXXKRmoxgllk. this function takes two global variables, $secretCode and $secretShift and loads them using global. it then splits $secretCode into an array of individual letters using str_split (it defaults to single-character blocks). for each of these $letter variables, it adds to $code, which is a parameter passed into shh. this is the optional prefix to the output. mb_ord retrieves the unicode value of the letter. the function then adds $secretShift to this in either the positive or negative direction, depending on $direction, another optional variable. mb_chr does the reverse of mb_ord and turns the unicode number back into a character. the result is a string shifted by $secretShift for each letter. for letters at the end of the alphabet, this can result in punctuation but it's easier to see if you just add 6 to l. six letters after l is r, the first letter of our shifted string.
php allows you to use ... to allow a flexible number of parameters and many guides will talk about this.
don't.