THU.JUN.18
2026
22:21:39
← back to modules MODULE · 02 · PHP PART 2
0 / 10 chapters complete · 0%

Syntax — How PHP Reads Your File

Now that the server is up and running from Part 1, it's time to learn what PHP actually is. This first chapter is the bare grammar — the punctuation marks, the file shape, the rules about how PHP reads the words you write. None of it is hard, and once it's in your fingers, every PHP file you ever see stops feeling like an alien language.
Files open with <?php and (usually) don't close. Statements end with semicolons. Variables start with $. Always turn on strict types. Always use __DIR__ for requires. That's basically the whole chapter.

The opening tag

Every PHP file starts with <?php. That's the switch that tells the parser "okay, treat what follows as code, not just text." Without it, the server happily serves your file as plain text, and your beautiful PHP gets dumped into the browser as-is — usually right after the first time you've connected via SSH at 1am and you're convinced you've broken something fundamental. You haven't. You just forgot the tag.

<?php
echo "Hello.";

You'll notice there's no closing ?> at the bottom of that example, and that's on purpose. For files that are pure PHP — meaning no trailing HTML — leave the closing tag off entirely. Sounds weird until you've been bitten by it once. Here's the gotcha: if you write ?> at the end and then accidentally leave a single newline character after it, that newline becomes output. The first time you try to send an HTTP redirect with header() later, PHP will yell at you with the dreaded "headers already sent" error, because technically you already sent a byte of body (that newline). Just omit the closing tag in pure-PHP files. Industry standard, prevents the bug, saves you Googling.

🐍 Python: Python doesn't have an "enter code mode" tag because the whole file is code by default. PHP needs the tag because the language was originally designed for sprinkling code into HTML templates — and that "drop in and out of code mode" capability is still one of PHP's signature features. We'll see it in action next.

Mixing PHP into HTML

PHP started life as a templating language, and that history still shows in a really useful way. You can write a mostly-HTML file with PHP sprinkled in, jumping in and out of code mode wherever you need a dynamic value. It looks like this:

<h1>Hello, <?= $name ?>.</h1>
<ul>
<?php foreach ($items as $item): ?>
  <li><?= htmlspecialchars($item) ?></li>
<?php endforeach; ?>
</ul>

Two new things to notice. The first is <?= $x ?> — that's shorthand for <?php echo $x; ?>. Same behavior, less typing. Use it for output inside templates and the code feels way cleaner. The second is the foreach ... : ... endforeach; syntax. PHP supports both curly braces AND this colon/keyword syntax for control flow. When you're interleaving with HTML, the keyword version reads way better because you can tell at a glance what's PHP control flow and what's just markup. Convention: use curly braces in pure-PHP files, alternate syntax in templates. Both are valid PHP, both compile to the same thing, just different shapes for different jobs.

Statements end with a semicolon

$x = 5;
echo $x;

Every statement in PHP ends with a semicolon. Forget one and you get a parse error — but here's the cruel twist: PHP usually points at the next line as the problem, not the line where you actually missed the semicolon. The parser was happily looking for the end of your statement and tripped on the start of the next one. So when you see "Parse error: unexpected ..." just check the line above the one PHP is complaining about. After getting bitten three or four times, it becomes muscle memory.

🐍 Python: Python uses newlines instead of semicolons. PHP is in the C-family tradition — semicolon at the end of every statement, like JavaScript, Java, C, etc. Slightly more typing, but the upside is you can put a statement on multiple lines without backslash-continuation hacks.

Comments

Three flavors of comment, but you'll really only use two:

// single line — the most common

# also single line (rare nowadays, kept around for shell-script compatibility)

/* multi
   line — when you need more than one line */

Default to //. A small piece of mentoring advice that took me too long to absorb: don't write comments that just restate what the code obviously does. // loop through the users right next to a foreach ($users as $user) is noise. Good comments explain the why — the workaround you had to put in for a weird browser, the reason you chose this approach over another, the constraint that's not visible in the code. The code already tells you what; comments are for everything else.

Case sensitivity — PHP is inconsistent here

This is one of those quirks that catches people coming from other languages. PHP is case-sensitive in some places and case-insensitive in others, and there's no consistent rule. Brace yourself:

  • Variable names ARE case-sensitive. $Name and $name are two completely different variables. PHP won't even warn you if you typo — it'll silently create a new variable.
  • Function and class names are case-INsensitive. strlen() and STRLEN() both call the same function. Historical artifact from PHP's early years.
  • Constants are case-sensitive by default (since PHP 8.0; older versions allowed optional case-insensitivity).

The practical fix is "just always write things in consistent lowercase" and you'll never bump into the weirdness. Stick to snake_case for variables and functions and you're good.

Variables — the dollar sign rule

Every variable in PHP starts with a $. This is one of the loudest "PHP" things about PHP. You don't declare variables ahead of time — the first time you assign a value, the variable comes into existence:

$count = 0;
$name = "Eric";
$is_admin = true;
$tags = ['php', 'web', 'lamp'];

That's all there is to it. No var, no let, no type declaration. Just $name = "Eric" and now $name exists and holds the string "Eric". Naming convention is snake_case for variables and functions, PascalCase for classes, SCREAMING_SNAKE for constants. PHP doesn't enforce these rules — your future self does. Pick a style and stay consistent or you'll spend hours wondering whether you named something userName or user_name.

🐍 Python: Python writes count = 0 with no prefix. PHP makes you put $ on every variable. Annoying for a day, automatic forever. Same snake_case convention. The dollar prefix actually has a small upside: variables are visually distinct from function calls and constants when you skim code.

The two quote types

PHP has two ways to write strings, and they behave differently. This is one of the few "you have to know this" details:

$name = "Eric";

// Double quotes interpolate variables:
echo "Hello, $name.";       // Output: Hello, Eric.

// Single quotes are literal:
echo 'Hello, $name.';       // Output: Hello, $name.

"Interpolation" is the fancy word for "substitute variables into the string." Double quotes do it; single quotes don't. The convention is to default to single quotes (they're slightly faster because PHP doesn't have to scan for variables, and there are zero surprises with special characters) and switch to double quotes only when you actually want interpolation. For complex interpolation — like array elements or object properties — wrap them in curly braces so PHP knows where the variable expression ends:

echo "Welcome, {$user['name']}!";

Concatenation uses . (a literal dot), not + like JavaScript:

$greeting = "Hello, " . $name . ".";

That dot-vs-plus thing trips up everyone coming from JavaScript exactly once. After that, your brain rewires and it's fine.

🐍 Python: PHP double quotes ≈ Python f-strings (f"Hello {name}"). PHP single quotes ≈ Python raw strings — literal, no interpolation. PHP uses . for concat, Python uses +. Different symbol, same idea.

Echo, print, and the debug trio

You've already seen echo in action. There are a few related ways to output things, and they do slightly different jobs:

  • echo $x; — output to the response. The everyday workhorse. Can take multiple args separated by commas: echo "a", "b", "c";.
  • print $x; — basically the same as echo but returns 1 (which you'll never use). Just use echo.
  • var_dump($x); — the debug print. Shows type AND value AND structure. For arrays, it recursively dumps the whole thing. Your absolute best friend when you're trying to figure out what's actually in a variable.
  • print_r($x); — friendlier formatting for arrays. No type info, but more readable for big nested structures.

One quality-of-life tip when debugging — wrap var_dump in <pre> tags so the formatting actually shows up readably in your browser. Otherwise it gets smushed onto one line:

echo "<pre>"; var_dump($x); echo "</pre>";

One file, one purpose — and __DIR__

Resist the temptation to dump everything into index.php. Even at this early stage, build the habit of splitting code by purpose: database connection setup in one file, config in another, helper functions in a third, page-specific logic in its own file. Then glue them together with require:

<?php
require __DIR__ . '/config.php';
require __DIR__ . '/lib/helpers.php';

// page logic goes here

That __DIR__ is critical and worth a paragraph. It's a magic constant that PHP automatically replaces with the absolute path of the folder this file lives in. Always use it for requires. Why? Because relative paths like require 'config.php' work fine until the day a script is called from a different working directory — cron jobs, includes from subfolders, you name it. Then suddenly your require fails because PHP looks in the wrong folder. __DIR__ always resolves correctly regardless of where the script was invoked from. Just always use it; you'll never have to debug "why can't it find config.php?" at 2am.

Strict types — turn it on, never turn it off

One last thing for this chapter, and it's a small line that has an outsized effect on your sanity. At the very top of every PHP file you write, before any other code, put:

<?php
declare(strict_types=1);

By default, PHP does "type juggling" — if a function expects an int and you pass a string like "42", PHP silently converts it. That sounds convenient but it's actually a major source of bugs. Pass "42abc" by mistake and PHP converts it to 42 without complaint, even though you probably meant to catch that as an error. Strict types says: "no silent conversions. If the function says it wants an int, give it an actual int. Otherwise throw an error."

This single line removes a whole category of "wait, why is my number suddenly a string?" bugs. Turn it on at the top of every file. Trust me on this one — every experienced PHP developer eventually adopts strict types as a hard rule. Save yourself the years of bug-hunting and start with it on.

🐍 Python: This is closer to mypy + Python's runtime than Python's stdlib by itself. Python's type hints are advisory; PHP with strict types actually enforces them at runtime. Like having mypy permanently baked into the interpreter.

Build: ASCII Name Banner — Your First Real PHP Page

Time to put all the syntax pieces into a single working page. We're going to build a tiny "banner" page that greets you by name, shows the current time, picks a "vibe" message based on what time of day it is, and lets you pass your name via the URL. By the end you'll have used opening tags, strict types, null coalescing, type casting, if/elseif, mixed PHP+HTML, and output escaping — all in about 30 lines. Real PHP.

Why this project? Because the moment you see a page that actually responds to URL parameters and shows different content based on the time of day, something clicks. You stop thinking of PHP as "PHP" and start thinking of it as "the language I use to make pages do things." That's the shift we're after.

  1. Create /home/erictey/server/banner.php. Make sure you're in VS Code connected to Lubuntu via Remote-SSH — file lives on the server, not your Windows machine.
  2. Paste this in:
    <?php
    declare(strict_types=1);
    
    $name = $_GET['name'] ?? 'Eric';
    $hours = (int) date('H');
    
    if ($hours < 12) {
        $vibe = "morning grind";
    } elseif ($hours < 18) {
        $vibe = "afternoon flow";
    } else {
        $vibe = "late night code";
    }
    ?>
    <!DOCTYPE html>
    <html>
    <head>
      <title>Banner</title>
      <style>
        body { background: #07050d; color: #5bf0ff; font-family: monospace; padding: 40px; }
        .box { border: 1px solid #ff2e88; padding: 20px; display: inline-block; }
        h1 { color: #ff2e88; margin: 0; }
        .meta { color: #b9adcf; margin-top: 8px; }
      </style>
    </head>
    <body>
      <div class="box">
        <h1>>> <?= htmlspecialchars($name) ?> <<<</h1>
        <div class="meta">
          Time: <?= date('H:i') ?><br>
          Vibe: <?= $vibe ?>
        </div>
      </div>
    </body>
    </html>
  3. Visit http://192.168.0.19/banner.php in your browser. You should see a stylized banner with your name and the current time.
  4. Now try http://192.168.0.19/banner.php?name=YourActualName and watch the name change. That's a real query parameter being read by PHP and rendered into the page. Tiny but feel it land for a sec.
  5. Check the vibe line — it should change based on what time it is. If it's morning where you are, it'll say "morning grind."

Stretch goals if you're feeling extra:

  • Add a "days until next year" line using date() arithmetic.
  • Add another query param ?mood=happy and have it change a third line.
  • Add three different color schemes triggered by ?theme=cyan|pink|green. Use a match expression (we'll cover them in chapter 3) or an if/elseif chain.

What you flexed: Opening PHP tag, declare(strict_types=1), the null coalescing operator ??, type casting with (int), the if/elseif chain, mixed PHP+HTML with the short echo tag <?=, output escaping with htmlspecialchars. Six syntax features in one real working page. Not bad for the first chapter.