PHP Syntax, Types, and Data Handling

Strong fundamentals prevent a surprising number of production bugs. Modern PHP gives you tools to make intent explicit while still keeping the language approachable.

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 reduces silent scalar coercion when calling functions. It does not validate user input for you, but it makes internal contracts more predictable.

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: Avoid binary floating-point math for exact currency calculations. 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” is meaningful. Do not use broad union types merely to avoid deciding what a function should accept.

String Handling and Output Context

<?php
declare(strict_types=1);

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

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

Escaping belongs at output time because the correct escaping depends on context: 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');

Store timestamps in UTC when possible, retain the applicable time-zone identifier for scheduled events, and convert only when displaying or interpreting local wall time.

Normalize at the Edge

Convert raw request data into predictable internal values once. Trim strings, validate identifiers, map checkboxes to booleans, parse dates explicitly, and reject ambiguous values.

<?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.');
}