Skip to content

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
<?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
<?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
<?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";
text
first: 1
second: 99
outside: 5

Three 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
<?php
$shopName = "Ate Lorna's";

function welcome(string $shop): string {
    return "Welcome to $shop!";
}

echo welcome($shopName), "\n";
text
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
<?php
$visitors = 0;

function addVisitor() {
    global $visitors;
    $visitors++;
}

addVisitor();
addVisitor();
echo "Visitors: $visitors\n";
text
Visitors: 2

It 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
<?php
function addVisitor(int $visitors): int {
    return $visitors + 1;
}

$visitors = 0;
$visitors = addVisitor($visitors);
$visitors = addVisitor($visitors);
echo "Visitors: $visitors\n";
text
Visitors: 2

Constants 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
<?php
const TAX_RATE = 0.12;

function withTax(float $price): float {
    return $price * (1 + TAX_RATE);
}

echo withTax(100), "\n";
text
112

static: 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
<?php
function nextTicketNumber(): int {
    static $number = 0;
    $number++;
    return $number;
}

echo nextTicketNumber(), "\n";
echo nextTicketNumber(), "\n";
echo nextTicketNumber(), "\n";
text
1
2
3

static $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
text
After ignoring the result: 10
After storing the result: 15
Round 1
Round 2
Lives: 3, player: unknown

The 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 ​

  1. Change status so it takes the player's name as a parameter, and pass $player in.
  2. Remove the word static from roundNumber. What do the two Round lines print now, and why?
  3. Write a function countCalls() that returns how many times it's been called, using static.

Check your understanding ​

Can a PHP function read a variable created outside it, without any special keyword?
What does a static variable inside a function do?
Why is passing values as parameters usually better than using global?

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.