NightFox

NightFox

PHP Syntax, Types, and Data Handling

A surprising number of production bugs come down to fundamentals: a string that was secretly a number, a float used for money, a date in the wrong time zone. Modern PHP gives you real tools to say what you mean, and it's still an easy language to pick up.

Start new files with strict types

<?php
declare(strict_types=1);

function calculateTax(float $subtotal, float $rate): float
{
    return round($subtotal * $rate, 2);
}

echo calculateTax(125.00, 0.06);

Strict mode stops PHP from quietly converting types when you call a function, like turning "5 apples" into 5. It won't validate user input for you, but it makes your own code a lot more predictable. I put it at the top of every new file.

Common types

TypeUseExample
stringText and identifiers that are not calculated$invoiceNumber = 'INV-2026-0042';
intWhole numbers$seatCount = 28;
floatApproximate decimal values$distanceMiles = 17.4;
boolTrue/false state$isActive = true;
arrayOrdered maps and lists$roles = ['admin', 'dispatcher'];
objectState plus behavior$customer = new Customer(...);
Money: don't use floats for currency. 0.1 + 0.2 is not 0.3 in floating point, and eventually someone's invoice will be off by a penny. Store integer cents, or use a decimal/money library.

Nullable and union types

<?php
declare(strict_types=1);

function normalizePhone(string|null $phone): ?string
{
    if ($phone === null || trim($phone) === '') {
        return null;
    }

    return preg_replace('/\D+/', '', $phone) ?: null;
}

function displayId(int|string $id): string
{
    return (string) $id;
}

Use nullable types when "no value" actually means something. Don't reach for a big union type just to avoid deciding what a function should accept. That's usually a sign the function is doing too much.

Strings and where they end up

<?php
declare(strict_types=1);

$name = trim($_POST['name'] ?? '');
$safeName = htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

echo "<p>Welcome, {$safeName}.</p>";

Escape when you output, not when you take input, because the right escaping depends on where the string is going. HTML text, an HTML attribute, JSON, SQL, a shell command, and a URL all have different rules.

Dates and time zones

<?php
declare(strict_types=1);

$displayZone = new DateTimeZone('America/New_York');
$storedUtc = new DateTimeImmutable('2026-07-16 13:30:00', new DateTimeZone('UTC'));
$local = $storedUtc->setTimezone($displayZone);

echo $local->format('F j, Y g:i A T');

My rule: store timestamps in UTC, keep the time zone name (like America/New_York) alongside anything scheduled in local time, and only convert when showing it to a person. Daylight saving time will find every shortcut you take.

Clean up input once, at the edge

Turn raw request data into predictable values once, as soon as it comes in. Trim strings, validate IDs, turn checkboxes into booleans, parse dates explicitly, and reject anything ambiguous. Then the rest of your code can trust what it's working with.

<?php
declare(strict_types=1);

$rawQuantity = $_POST['quantity'] ?? null;
$quantity = filter_var(
    $rawQuantity,
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1, 'max_range' => 100]]
);

if ($quantity === false) {
    throw new InvalidArgumentException('Quantity must be between 1 and 100.');
}

Last Modified: October 12, 2026, 11:46 am