PHP

Chapter 2: Variables, Data Types, and the Power of Strict Typing

By Ali Naqi | November 06, 2026

Chapter 2: Variables, Data Types, and the Power of Strict Typing

# Modern PHP Mastery: From First Steps to Production


## Chapter 2: Variables, Data Types, and the Power of Strict Typing


In Chapter 1, we spun up a local development environment, walked through the client-server request cycle, and executed our first dynamic script. Now it is time to look at the engine's building blocks: how PHP holds data in memory, evaluates expressions, and handles types.


Historically, PHP’s loose typing system was both its greatest selling point and its most dangerous pitfall. It allowed beginners to build functioning sites quickly without worrying about types, but it also opened the door to silent bugs, strange conversion quirks, and security vulnerabilities.


Modern PHP changes this dynamic entirely. While it retains backward compatibility and dynamic flexibility, it introduces a robust, expressive type system that allows you to write clean, predictable, production-grade code.


---


### Understanding Variables in PHP


In PHP, a variable is a named pointer to a memory location that holds a value. Every variable in PHP begins with a dollar sign (`$`), followed by the variable name.


#### Naming Conventions and Rules


PHP variable names must follow these rules:


1. Must start with a letter (`a-z`, `A-Z`) or an underscore (`_`).

2. Cannot start with a number.

3. Can contain only alphanumeric characters and underscores (`a-z`, `A-Z`, `0-9`, and `_`).

4. **Variables are case-sensitive**. `$userCount` and `$usercount` are two completely distinct variables in memory.


```php

<?php

// Valid variable declarations

$greeting = "Hello, World!";

$_internalId = 4092;

$total_revenue = 1599.50; // snake_case

$userAccountBalance = 250.00; // camelCase (Standard PSR-12 recommendation)


// Invalid variable declarations

// $9items = 9; // Syntax error: starts with a number

// $first-name = 'Ali'; // Syntax error: hyphens are interpreted as subtraction operators


```


In the modern PHP ecosystem, community standards known as **PSRs** (PHP Standard Recommendations, maintained by the PHP-FIG) define best practices. Under **PSR-12**, variables and class properties should consistently use `camelCase`, while constants use `UPPER_SNAKE_CASE`.


---


### The PHP Type System: An Overview


PHP has eight primitive types, which are categorized into three groups:


1. **Scalar Types** (hold a single value):

* `bool` (Boolean: `true` or `false`)

* `int` (Integer: whole numbers like `-42`, `0`, `108`)

* `float` (Floating-point numbers/doubles: `3.14159`, `-0.005`)

* `string` (Text: `"hello"`, `'modern php'`)



2. **Compound Types** (hold multiple values):

* `array` (Ordered maps of key-value pairs)

* `object` (Instances of classes)



3. **Special Types**:

* `null` (Represents a variable with no assigned value)

* `resource` (Holds an external resource reference, such as a database connection handle or open file stream)




---


### Deep Dive into Scalar Types


Let’s examine how scalar types behave under the hood and where edge cases hide.


#### 1. Integers (`int`)


Integers are whole numbers without decimals. On 64-bit platforms (which virtually all production servers run today), PHP integers span from $-9,223,372,036,854,775,808$ to $9,223,372,036,854,775,807$.


PHP supports numeric literal separators (underscores) to make large numbers readable:


```php

<?php

$subscribers = 1_500_000; // PHP reads this as 1500000

$hexCode = 0x1A; // Hexadecimal (26 in base 10)

$binaryMask = 0b1101; // Binary (13 in base 10)


```


If an integer calculation exceeds the maximum allowed integer bounds (`PHP_INT_MAX`), PHP automatically overflows the result into a `float`.


#### 2. Floating-Point Numbers (`float`)


Floats represent real numbers with fractional components:


```php

<?php

$price = 19.99;

$scientific = 1.2e3; // 1.2 * 10^3 = 1200


```


> **Warning on Financial Data:** Floats are stored internally as binary approximations (IEEE 754 standard). Never use floats for currency calculations. For example:

> ```php

> <?php

> $a = 0.1 + 0.2;

> $b = 0.3;

> var_dump($a === $b); // Output: bool(false)!

> 

> ```

> 

> 

> For monetary balances or precision bookkeeping, either store amounts as integers in the lowest denomination (e.g., cents: `$1999` for `$19.99`) or use PHP's arbitrary precision math extension (**BCMath**).


#### 3. Booleans (`bool`)


A boolean represents a truth value: `true` or `false` (case-insensitive, though lowercase is preferred).


When PHP casts values to boolean in logical contexts (like `if` statements), the following values evaluate to **`false`** (known as *falsy* values):


* The boolean `false` itself

* The integer `0` and float `0.0`

* The empty string `""` and the string `"0"`

* An empty array `[]`

* The special value `null`


Every other value—including negative numbers (`-1`), non-empty strings (`"false"`), and arrays with elements—evaluates to **`true`**.


#### 4. Strings (`string`)


Strings are sequences of characters. In PHP, a string is a byte array accompanied by an integer length. PHP does not natively enforce an encoding at the language level; it treats strings as 8-bit bytes (making functions from the `mbstring` extension vital when dealing with multi-byte UTF-8 text).


PHP provides two primary ways to declare string literals:


* **Single Quotes (`'...'`)**: Evaluates strings literally. Variables and escape sequences (except `\'` and `\\`) are not parsed.

* **Double Quotes (`"..."`)**: Enables string interpolation and parses escape characters like `\n` (newline) and `\t` (tab).


```php

<?php

$author = "Ali";


// Single quotes: raw output

echo 'Hello, $author\n'; 

// Prints: Hello, $author\n


// Double quotes: variable interpolation

echo "Hello, $author\n"; 

// Prints: Hello, Ali (followed by a newline)


// Complex interpolation using curly braces

$item = "car";

echo "He owns three {$item}s."; 

// Prints: He owns three cars.


```


---


### Type Juggling vs. Strict Typing


PHP is historically a **dynamically and weakly typed** language. This means you do not need to declare a variable's type when initializing it, and PHP will automatically convert (juggle) types behind the scenes based on the operation.


#### The Dangers of Type Juggling


Consider this classic example:


```php

<?php

$sum = "50" + 25; 

// Result: 75 (int) - PHP coerced the string "50" into an integer.


```


While convenient, type juggling can introduce dangerous bugs when combined with loose equality (`==`):


```php

<?php

// Loose comparison quirks (Prior to PHP 8.0, 0 == "foo" evaluated to true!)

$input = "0";


if ($input == false) {

    // This executes because "0" is falsy!

}


```


#### The Modern Solution: Strict Comparison and `declare(strict_types=1)`


To prevent subtle coercion bugs, modern PHP development relies on two practices:


1. **Always use strict equality operators (`===` and `!==`)**:

Strict equality checks both the **value** and the **type**.

```php

<?php

$id = "42";


var_dump($id == 42); // bool(true) -> Types coerced

var_dump($id === 42); // bool(false) -> String does not match Integer


```



2. **Enable Strict Types**:

By default, PHP attempts to coerce arguments passed into typed functions. If a function expects an `int` and you pass `"10"`, PHP will quietly convert it.

Placing `declare(strict_types=1);` at the very top of your PHP file disables this behavior for function calls within that file, forcing PHP to throw a `TypeError` if types do not match exactly.


```php

<?php

declare(strict_types=1);


function calculateTax(float $subtotal, float $rate): float 

{

    return $subtotal * $rate;

}


// Valid call:

echo calculateTax(100.0, 0.15); // Output: 15


// Fatal Error with strict_types enabled:

// echo calculateTax("100", 0.15); 

// Fatal error: Uncaught TypeError: calculateTax(): Argument #1 ($subtotal) must be of type float, string given


```


*Note: The only automatic conversion permitted under strict types is widening an `int` to a `float`.*


---


### Constants: Values That Never Change


When you have values that must remain fixed throughout the execution lifecycle—such as database configuration flags, API endpoints, or status codes—use constants.


PHP provides two ways to declare constants:


#### 1. The `const` Keyword (Preferred in Modern PHP)


Defined at compile time. It can be used in global scope as well as inside classes.


```php

<?php

const MAX_UPLOAD_LIMIT_MB = 25;

const APP_ENV = 'production';


```


#### 2. The `define()` Function


Defined at runtime. Useful when the constant name or value depends on a runtime condition.


```php

<?php

define('DB_HOST', getenv('DB_HOST') ?: '127.0.0.1');


```


Unlike regular variables, constants:


* Do not use a leading dollar sign (`$`).

* Are globally accessible across functions and classes without needing global scope keywords.

* Cannot be reassigned once defined.


---


### Debugging Tools: `var_dump()`, `print_r()`, and Beyond


When developing applications, inspecting what variables look like inside memory is an essential part of the workflow.


#### `var_dump($var)`


Outputs detailed technical information about an expression, including its type, length, and internal contents. This is the gold standard for debugging raw data.


```php

<?php

$data = [

    'username' => 'alex_dev',

    'verified' => true,

    'points' => 450

];


var_dump($data);


```


Output:


```text

array(3) {

  ["username"]=>

  string(8) "alex_dev"

  ["verified"]=>

  bool(true)

  ["points"]=>

  int(450)

}


```


#### `print_r($var)`


Displays data in a human-readable format. It prints arrays and objects cleanly, but does not explicitly show data types or string lengths (e.g., it will not distinguish between `false` and an empty string easily).


#### Browser Debugging Tip


When viewing the output of `var_dump()` or `print_r()` directly in a web browser, the raw text can collapse into a single line because HTML compresses whitespace. Wrap your dump calls in an HTML `<pre>` tag to retain formatting:


```php

<?php

echo '<pre>';

var_dump($data);

echo '</pre>';


```


---


### Practical Example: Building a Safe Price Formatter


Let’s apply what we've covered by building a dedicated script that calculates line items for a digital checkout, using strict typing and explicit type casting.


Create a file named `checkout.php`:


```php

<?php

declare(strict_types=1);


// Configuration constants

const TAX_RATE = 0.0825; // 8.25% sales tax

const CURRENCY_SYMBOL = '$';


// Input data

$productName = "Mechanical Keyboard";

$unitPriceInCents = 12999; // $129.99 stored as an integer to prevent float drift

$quantity = 2;

$isDiscountEligible = true;


// Business logic

$grossTotalCents = $unitPriceInCents * $quantity;


// Apply a 10% coupon if eligible

$discountCents = 0;

if ($isDiscountEligible) {

    $discountCents = (int) round($grossTotalCents * 0.10);

}


$netSubtotalCents = $grossTotalCents - $discountCents;

$taxAmountCents = (int) round($netSubtotalCents * TAX_RATE);

$grandTotalCents = $netSubtotalCents + $taxAmountCents;


// Formatting functions

function formatMoney(int $amountInCents): string 

{

    $dollars = $amountInCents / 100;

    return CURRENCY_SYMBOL . number_format($dollars, 2);

}

?>

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>Order Receipt</title>

    <style>

        body { font-family: monospace; background: #111827; color: #f3f4f6; padding: 2rem; }

        .receipt { max-width: 420px; margin: 0 auto; background: #1f2937; padding: 1.5rem; border-radius: 8px; }

        .row { display: flex; justify-content: space-between; margin-bottom: 0.5rem; }

        .divider { border-bottom: 1px dashed #4b5563; margin: 1rem 0; }

        .bold { font-weight: bold; }

        .total { color: #10b981; font-size: 1.2rem; }

    </style>

</head>

<body>


<div class="receipt">

    <h2>Invoice Summary</h2>

    <div class="row">

        <span>Item:</span>

        <span><?= htmlspecialchars($productName, ENT_QUOTES, 'UTF-8') ?></span>

    </div>

    <div class="row">

        <span>Qty x Unit:</span>

        <span><?= $quantity ?> &times; <?= formatMoney($unitPriceInCents) ?></span>

    </div>

    <div class="divider"></div>

    <div class="row">

        <span>Gross Subtotal:</span>

        <span><?= formatMoney($grossTotalCents) ?></span>

    </div>

    <div class="row">

        <span>Discount:</span>

        <span>-<?= formatMoney($discountCents) ?></span>

    </div>

    <div class="row">

        <span>Estimated Tax:</span>

        <span><?= formatMoney($taxAmountCents) ?></span>

    </div>

    <div class="divider"></div>

    <div class="row total bold">

        <span>Final Total:</span>

        <span><?= formatMoney($grandTotalCents) ?></span>

    </div>

</div>


</body>

</html>


```


Run this file using your development server (`php -S localhost:8000`) and navigate to `http://localhost:8000/checkout.php`.


Notice how integers handle every computation step without precision loss, while `formatMoney()` handles conversion to currency display strings at the boundary layer.


---


### Chapter 2 Key Takeaways


1. **Variables are case-sensitive** and must follow PSR-12 conventions (`$camelCase`) for clarity and consistency across teams.

2. **Floats introduce rounding inaccuracies**; store currency as integers (cents) to protect financial data integrity.

3. **Double quotes parse variables**; single quotes treat text as raw characters.

4. **Use `declare(strict_types=1);**` at the top of your files to catch invalid arguments before they hit production.

5. **Always prefer strict comparisons (`===` / `!==`)** over loose equality (`==` / `!=`) to eliminate type-coercion bugs.


In **Chapter 3**, we will look at decision-making and flow control: conditional statements (`if`, `else`, `elseif`), the `match` expression introduced in modern PHP, loops, and avoiding deeply nested code.