1. PHP Syntax & Variables
PHP is a server-side scripting language — code runs on the server, and only the
output it produces (usually HTML) reaches the browser. Every PHP statement lives
inside <?php ... ?> tags, and every statement ends with a
semicolon:
<?php
$name = "Ada";
$age = 30;
$is_admin = true;
echo "Hello, " . $name . "! You are " . $age . " years old.";
Variables start with $ and don't need a declared type — PHP is
dynamically typed: a variable's type is whatever value it currently
holds. The core scalar types are int, float,
string and bool, plus array and
object for compound data, and null for "no value."
<?php
$price = 19.99; // float
$quantity = 3; // int
$sku = "TSHIRT-BLU-M"; // string
$in_stock = true; // bool
$discount = null; // null
var_dump($price); // float(19.99)
echo gettype($quantity); // "integer"
PHP also does implicit type conversion ("type juggling") in many operations —
"5" + 3 evaluates to 8. This is convenient but a common
source of bugs, which is why PHP 8 lets you opt into strict typing per file:
<?php
declare(strict_types=1);
function addTax(float $price, float $rate): float {
return $price + ($price * $rate);
}
addTax(100, 0.18); // fine -- ints widen to float automatically
addTax("100", 0.18); // TypeError: must be of type float, string given
declare(strict_types=1) from day one
It must be the very first statement in a file. This course uses it in every example from here on — it turns silent, surprising coercions into loud TypeErrors at the exact call site that got it wrong, instead of a wrong value surfacing three functions later.
2. Control Flow & Functions
if/elseif/else, while,
for and foreach all work close to how they do in most
C-family languages. PHP 8's match expression is the one worth learning
deliberately — it's a stricter, expression-based upgrade to switch:
<?php
declare(strict_types=1);
function shippingLabel(string $status): string {
return match ($status) {
'pending', 'processing' => 'Preparing your order',
'shipped' => 'On its way',
'delivered' => 'Delivered',
default => 'Unknown status',
};
}
echo shippingLabel('shipped'); // "On its way"
Unlike switch, match compares with strict equality
(===, no type juggling), doesn't fall through between arms, and is
itself an expression — it produces a value you can assign or return directly, as
shown above.
Functions
Functions take typed parameters with optional default values, and can be called with named arguments — useful once a function has several optional parameters:
<?php
declare(strict_types=1);
function formatPrice(float $amount, string $currency = "USD", bool $withSymbol = true): string {
$symbol = $withSymbol ? ($currency === "USD" ? "$" : $currency . " ") : "";
return $symbol . number_format($amount, 2);
}
echo formatPrice(1999.5); // "$1,999.50"
echo formatPrice(1999.5, withSymbol: false); // "1,999.50"
echo formatPrice(amount: 42, currency: "EUR"); // "EUR 42.00"
// Arrow functions -- a short-lived closure that auto-captures outer variables
$taxRate = 0.18;
$withTax = fn(float $price) => $price + ($price * $taxRate);
echo $withTax(100); // 118
fn(...) => expr is an arrow function: a compact
closure for a single expression that automatically captures variables from the
surrounding scope by value — no explicit use (...) clause needed, unlike
a full function () use (...) {} closure.
3. Installing PHP 8.3
This course uses PHP 8.3. Install it for your platform, then confirm it's active:
# macOS (Homebrew)
brew install php@8.3
# Ubuntu/Debian
sudo apt install php8.3-cli php8.3-mbstring php8.3-mysql php8.3-xml
# Windows -- install via https://windows.php.net/download or scoop:
scoop install php
php --version
# PHP 8.3.x (cli) ...
PHP ships with a built-in development server — no Apache or Nginx needed while you're learning. Point it at a folder and it serves any PHP file inside:
mkdir week-01 && cd week-01
echo '<?php echo "PHP is running: " . phpversion();' > index.php
php -S localhost:8000
# PHP 8.3.x Development Server (http://localhost:8000) started
# In another terminal:
curl http://localhost:8000/
# PHP is running: 8.3.x
The built-in server is single-threaded and explicitly documented as unsuitable for production — it's a development convenience only. From Week 6 onward, once MySQL enters the picture, you'll rely on it constantly; Week 28 covers the real Nginx + PHP-FPM setup you'd actually deploy.
4. php.ini Essentials & Xdebug
php.ini is PHP's main configuration file — it controls error
reporting, upload limits, timezone and dozens of other runtime settings. Find your
active one with:
php --ini
# Loaded Configuration File: /usr/local/etc/php/8.3/php.ini
While learning, turn on full error visibility — silent failures are the hardest bugs to chase:
error_reporting = E_ALL
display_errors = On
date.timezone = "UTC"
Xdebug is PHP's step debugger and profiler — it lets your editor
set breakpoints and inspect variables mid-request instead of scattering
var_dump() calls everywhere:
# Install via PECL (works across platforms)
pecl install xdebug
# Then add to php.ini:
zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
Install the Xdebug extension for your editor (PHP Debug for VS Code, or the built-in support in PhpStorm) and you'll have working breakpoints by Week 2, once there's request-handling logic worth stepping through.
5. Your First Dynamic Page & the Request Lifecycle
PHP files can mix HTML and PHP freely — you drop in and out of
<?php ?> tags anywhere in the file:
<?php
declare(strict_types=1);
$products = ["Keyboard", "Mouse", "Monitor"];
$storeName = "CodeVerse Shop";
?>
<!DOCTYPE html>
<html>
<head><title><?= $storeName ?></title></head>
<body>
<h1><?= $storeName ?></h1>
<ul>
<?php foreach ($products as $product): ?>
<li><?= htmlspecialchars($product) ?></li>
<?php endforeach; ?>
</ul>
</body>
</html>
<?= $x ?> is shorthand for <?php echo $x; ?>.
The foreach ... endforeach alternate syntax reads more naturally when
PHP is interleaved with HTML like this, versus curly braces.
htmlspecialchars() escapes special characters before they hit the
page — always wrap any value that might contain user input before printing it into
HTML, to prevent cross-site scripting (covered properly in Week 8).
Here's what actually happens between the request and that output:
- The browser sends an HTTP request for
/index.php. - The server (right now, PHP's built-in one) locates the file and hands it to the PHP interpreter.
- PHP executes top to bottom — every
<?php ?>block runs; everything outside it is passed through as literal output. - The interpreter's combined output (HTML + evaluated PHP) becomes the HTTP response body.
- The browser receives plain HTML — it never sees your PHP source.
Save the file above, run php -S localhost:8000 in that folder, and load http://localhost:8000/. View the page source in your browser — you'll see clean HTML with no trace of the PHP that generated it.
6. Hands-on Exercise
Build a dynamic product listing page
Get the local server running, then apply this week's syntax to render real data.
Requirements:
- Create
index.phpand start it withphp -S localhost:8000, confirming it loads in a browser. - Add
declare(strict_types=1);as the first line. - Define an array of at least 4 associative arrays, each with
name(string),price(float) andinStock(bool) keys. - Write a typed function
formatPrice(float $amount): stringthat returns a string like"$19.99"usingnumber_format(). - Render the products as an HTML list using
foreach, callingformatPrice()for each price and usingmatchto print "In Stock" or "Out of Stock" based oninStock. - Escape every printed string with
htmlspecialchars(), even though this data isn't user-supplied yet — build the habit now.
match (true) { $inStock => 'In Stock', default => 'Out of Stock' } is a common pattern for turning a boolean into one of two strings with match.
7. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What does declare(strict_types=1); actually change?
What does declare(strict_types=1); actually change?
It disables implicit type coercion for scalar type declarations in that file's function calls — passing a string where a float parameter is declared throws a TypeError instead of silently converting. It does not affect files that call into this one without their own declare; the setting is per-file, and only governs calls made from within that file.
Q2
How is PHP's match expression different from switch?
How is PHP's match expression different from switch?
match compares with strict equality (===) instead of switch's loose ==, never falls through between arms (no break needed), throws an UnhandledMatchError if nothing matches and there's no default, and is an expression — it returns a value you can assign directly, rather than a statement you branch inside.
Q3
Why call htmlspecialchars() on values before printing them into HTML, even on data you generated yourself?
Why call htmlspecialchars() on values before printing them into HTML, even on data you generated yourself?
It escapes characters like <, > and & so they render as text instead of being interpreted as HTML/script. Building the habit on trusted data now means it's already there once real user input flows through the same code paths from Week 2 onward — that's exactly the gap that causes cross-site scripting (XSS) vulnerabilities, covered fully in Week 8.
Q4
Does the browser ever see your PHP source code?
Does the browser ever see your PHP source code?
No. PHP executes entirely on the server; the browser only ever receives the interpreter's final output (typically HTML, JSON, or similar). This is what "server-side" means in practice — anything sensitive (database credentials, business logic, unvalidated secrets) is safe from direct exposure precisely because it never leaves the server process.