Variable Scope
Each classroom has its own whiteboard. What's written in Room 5 can't be read from Room 6, and wiping one board doesn't touch the other.
Here's a puzzle that catches almost every PHP beginner:
<?php
$shopName = "Ate Lorna's";
function welcome() {
echo "Welcome to $shopName!\n";
}
welcome();
// error: Warning: Undefined variable $shopName$shopName clearly exists. So why can't the function see it? The answer is scope: the part of your code where a variable can be seen.
Separate whiteboards
In PHP, every function has its own whiteboard:
- Variables created inside a function are local to it. They exist only while the function runs, and nothing outside can see them.
- Variables created outside any function are in the global scope. In PHP, functions can't see them, either.
This is stricter than JavaScript or Python, where a function can read variables from around it. In PHP, a function starts with a blank whiteboard, except for its parameters.
It works the other way too:
<?php
function makeTotal() {
$total = 250;
}
makeTotal();
echo $total;
// error: Warning: Undefined variable $total$total was written on the function's board, which was wiped when the function finished.
Why this is a good thing
At first, this feels like an obstacle. But imagine a big program with hundreds of functions. If every function could read and change every variable, a function called calculateTax could quietly change your $total from somewhere far away, and you'd never find the bug.
With separate scopes, a function can only use what's handed to it, and can only affect the outside by returning something. You can understand each function on its own.
It also means two functions can use the same variable names without clashing:
<?php
function first() {
$count = 1;
echo "first: $count\n";
}
function second() {
$count = 99;
echo "second: $count\n";
}
$count = 5;
first();
second();
echo "outside: $count\n";first: 1
second: 99
outside: 5Three different $count variables, on three different whiteboards.
The right way: in through parameters, out through return
To get a value into a function, pass it as an argument. To get a result out, return it. That's the fix for both puzzles:
<?php
$shopName = "Ate Lorna's";
function welcome(string $shop): string {
return "Welcome to $shop!";
}
echo welcome($shopName), "\n";Welcome to Ate Lorna's!Everything the function needs is listed in its parameters, so anyone reading it can see exactly what it depends on.
The global keyword
PHP does have a way for a function to reach a global variable: the global keyword.
<?php
$visitors = 0;
function addVisitor() {
global $visitors;
$visitors++;
}
addVisitor();
addVisitor();
echo "Visitors: $visitors\n";Visitors: 2It works, and you'll see it in older code, especially WordPress. But it brings back exactly the problem scope protects you from: any function could be changing $visitors, and you can't tell by reading the calls. Prefer parameters and return values. A clean version of the same thing:
<?php
function addVisitor(int $visitors): int {
return $visitors + 1;
}
$visitors = 0;
$visitors = addVisitor($visitors);
$visitors = addVisitor($visitors);
echo "Visitors: $visitors\n";Visitors: 2Constants are everywhere
Constants, from Variables and Constants, are the exception: they can be seen from anywhere, including inside functions. Since they can't change, that's safe:
<?php
const TAX_RATE = 0.12;
function withTax(float $price): float {
return $price * (1 + TAX_RATE);
}
echo withTax(100), "\n";112static: a variable that remembers
Normally, a function's local variables start fresh on every call. A static variable is different: it keeps its value between calls (within the same request):
<?php
function nextTicketNumber(): int {
static $number = 0;
$number++;
return $number;
}
echo nextTicketNumber(), "\n";
echo nextTicketNumber(), "\n";
echo nextTicketNumber(), "\n";1
2
3static $number = 0; sets the starting value only the first time. On later calls, $number still has the value from last time. Remember from How PHP Runs, though: every request starts fresh, so the numbering restarts at 1 on the next page load.
Try it
A game keeps score, with a mix of scopes. Predict each line, and whether any of them causes a warning.
Running PHP here
Running PHP right in the browser is coming to Syntaxia soon. For now, you can read and edit the code here. If you have PHP installed on your computer (see Setting Up), save it as index.php and run it with php index.php.
Check your prediction
After ignoring the result: 10
After storing the result: 15
Round 1
Round 2
Lives: 3, player: unknownThe first addPoints changes only its own $score parameter, and the result is thrown away, so the global $score stays 10. Storing the returned value fixes that. The static $round remembers between calls. Inside status, the constant is visible, but $player isn't, and ?? quietly gives "unknown" instead of a warning.
Try it yourself
- Change
statusso it takes the player's name as a parameter, and pass$playerin. - Remove the word
staticfromroundNumber. What do the two Round lines print now, and why? - Write a function
countCalls()that returns how many times it's been called, usingstatic.
Check your understanding
Up next
Your programs are growing. Soon, one file won't be enough. Learn to split your code across several files and pull them together, in Splitting Code with include and require.
