Lesson 2.1: Variables, Constants, and Data Types

The Building Blocks of PHP

Variables are like labeled boxes in your code's warehouse. You store data in them, move them around, combine them, and transform them. Master variables, and you master the foundation of programming.

But here's what most tutorials miss: PHP's type system is both flexible and sophisticated. It's dynamically typed (no need to declare types) yet supports strict typing when you want it. This flexibility makes PHP approachable for beginners while powerful enough for complex applications.

Let's dive deep into PHP's data storage mechanisms and discover features that might surprise even experienced developers.

Variables: Your Data Containers

Variables in PHP start with $. Simple rule, powerful concept:

<?php
// Basic variable assignment
$name = "Phoenix";
$age = 25;
$height = 5.9;
$isStudent = true;

// Variables can change types (dynamic typing)
$flexible = "I'm a string";
echo gettype($flexible);  // string

$flexible = 42;
echo gettype($flexible);  // integer

$flexible = 3.14;
echo gettype($flexible);  // double

$flexible = true;
echo gettype($flexible);  // boolean
?>

But variables aren't just simple containers. They have nuances:

<?php
// Case sensitivity matters
$name = "Alice";
$Name = "Bob";
$NAME = "Charlie";
// These are THREE different variables!

// Variable variables (yes, that's a thing)
$varName = 'greeting';
$$varName = 'Hello World';
echo $greeting;  // Hello World

// Reference assignment
$original = "I'm the original";
$reference = &$original;  // Note the &
$reference = "I'm changed";
echo $original;  // "I'm changed" - both variables point to same data!
?>

Naming Conventions and Best Practices

Your future self will thank you for good variable names:

<?php
// Bad: Unclear, too short
$d = "2025-06-06";
$n = "John Doe";
$t = 150.99;

// Good: Descriptive, clear purpose
$orderDate = "2025-06-06";
$customerName = "John Doe";
$totalPrice = 150.99;

// PHP naming styles
$snake_case = "common in PHP core";          // Traditional PHP
$camelCase = "common in modern PHP";         // Modern frameworks
$PascalCase = "reserved for classes";        // Class names
$SCREAMING_SNAKE_CASE = "for constants";     // Constants

// Valid but weird (don't do this)
$_ΦΦΦ = "Greek letters work";
$٠١٢٣ = "Arabic numerals work";
$좋아 = "Korean works too";
// Stick to ASCII for sanity!
?>

PHP's Data Types: The Complete Guide

PHP supports ten primitive types. Let's explore each with real examples:

1. Boolean (bool)

The simplest type - true or false:

<?php
$isActive = true;
$isDeleted = false;

// Truthy and falsy values
$values = [
    false,      // false
    0,          // false
    0.0,        // false
    "",         // false (empty string)
    "0",        // false (string zero)
    [],         // false (empty array)
    null,       // false

    true,       // true
    1,          // true
    -1,         // true
    "false",    // true (non-empty string)
    " ",        // true (space is not empty)
    [0],        // true (non-empty array)
];

foreach ($values as $value) {
    echo "Value: ";
    var_dump($value);
    echo "Evaluates to: " . ($value ? "TRUE" : "FALSE") . "\n\n";
}
?>

2. Integer (int)

Whole numbers, positive or negative:

<?php
// Different integer formats
$decimal = 255;
$binary = 0b11111111;      // 255 in binary
$octal = 0377;             // 255 in octal
$hex = 0xFF;               // 255 in hexadecimal

echo "All equal: " . ($decimal === $binary && $binary === $octal && $octal === $hex);

// Integer limits
echo "Maximum integer: " . PHP_INT_MAX . "\n";
echo "Minimum integer: " . PHP_INT_MIN . "\n";

// Overflow behavior
$overflow = PHP_INT_MAX + 1;
var_dump($overflow);  // Converts to float!

// Useful integer functions
$negative = -42;
echo abs($negative);           // 42
echo max(10, 20, 30);         // 30
echo min(10, 20, 30);         // 10
echo rand(1, 100);            // Random between 1-100
?>

3. Float (float/double)

Numbers with decimal points:

<?php
// Float representations
$price = 19.99;
$scientific = 1.2e3;        // 1200
$negative = -3.14159;

// Float precision issues (important!)
$a = 0.1;
$b = 0.2;
$c = 0.3;

// This might surprise you
echo ($a + $b === $c) ? "Equal" : "Not equal";  // Not equal!
echo "0.1 + 0.2 = " . ($a + $b) . "\n";         // 0.30000000000000004

// Proper float comparison
$epsilon = 0.00001;
if (abs(($a + $b) - $c) < $epsilon) {
    echo "Equal within tolerance\n";
}

// Float functions
echo round(3.14159, 2);       // 3.14
echo ceil(4.1);               // 5
echo floor(4.9);              // 4
echo number_format(1234.5678, 2);  // 1,234.57
?>

4. String

Text data - PHP's bread and butter:

<?php
// String creation methods
$single = 'Single quotes - variables like $name are NOT parsed';
$double = "Double quotes - variables like $name ARE parsed";
$nowdoc = <<<'EOT'
Nowdoc syntax - like single quotes
Variables $are $not $parsed
Useful for large blocks
EOT;

$heredoc = <<<EOT
Heredoc syntax - like double quotes
Variables $are $parsed
Also useful for large blocks
EOT;

// String manipulation showcase
$text = "  PHP is AWESOME!  ";

// Length and trimming
echo strlen($text);                    // 19
echo strlen(trim($text));              // 15

// Case manipulation
echo strtolower($text);                // "  php is awesome!  "
echo strtoupper(trim($text));          // "PHP IS AWESOME!"
echo ucfirst(strtolower(trim($text))); // "Php is awesome!"
echo ucwords(strtolower(trim($text))); // "Php Is Awesome!"

// Substring operations
$email = "[email protected]";
echo substr($email, 0, 4);             // "user"
echo substr($email, -3);               // "com"
echo strstr($email, '@');              // "@example.com"
echo strpos($email, '@');              // 4

// Modern string functions
$path = "/users/john/documents/file.txt";
echo basename($path);                  // "file.txt"
echo dirname($path);                   // "/users/john/documents"
echo pathinfo($path, PATHINFO_EXTENSION); // "txt"
?>

5. Array

PHP's swiss army knife - incredibly versatile:

<?php
// Indexed arrays
$fruits = ["apple", "banana", "orange"];
$numbers = [10, 20, 30, 40, 50];

// Associative arrays
$person = [
    "name" => "Alex",
    "age" => 28,
    "city" => "Boston"
];

// Multidimensional arrays
$matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

// Mixed arrays (PHP allows this!)
$mixed = [
    "string",
    42,
    3.14,
    true,
    ["nested", "array"],
    "key" => "value"
];

// Array operations
$colors = ["red", "green", "blue"];

// Adding elements
$colors[] = "yellow";                  // Append
array_push($colors, "purple");         // Also append
array_unshift($colors, "white");       // Prepend

// Removing elements
$last = array_pop($colors);            // Remove last
$first = array_shift($colors);         // Remove first

// Searching
echo in_array("green", $colors);       // true
echo array_search("blue", $colors);    // Returns index

// Powerful array functions
$numbers = [3, 1, 4, 1, 5, 9, 2, 6];
sort($numbers);                        // Sorts in place
$unique = array_unique($numbers);      // Remove duplicates
$sum = array_sum($numbers);            // Sum all values
$chunks = array_chunk($numbers, 3);    // Split into chunks

// Functional programming
$squared = array_map(fn($n) => $n ** 2, $numbers);
$evens = array_filter($numbers, fn($n) => $n % 2 === 0);
$product = array_reduce($numbers, fn($carry, $n) => $carry * $n, 1);
?>

6. Object

Objects bring structure to your data:

<?php
// Simple object creation
$obj = new stdClass();
$obj->name = "Dynamic Object";
$obj->value = 42;

// Converting arrays to objects
$array = ["name" => "John", "age" => 30];
$person = (object) $array;
echo $person->name;  // John

// Anonymous classes (PHP 7+)
$logger = new class {
    public function log($message) {
        echo "[" . date('Y-m-d H:i:s') . "] " . $message . "\n";
    }
};
$logger->log("This is a log message");

// Type checking
var_dump($person instanceof stdClass);  // true
?>

7. NULL

The absence of value:

<?php
$notSet = null;
$undefined;  // Also null (with a notice)

// Checking for null
var_dump(is_null($notSet));           // true
var_dump($notSet === null);           // true
var_dump(isset($notSet));             // false
var_dump(empty($notSet));             // true

// Null coalescing operator (PHP 7+)
$username = $_GET['user'] ?? 'guest';  // If not set, use 'guest'

// Null coalescing assignment (PHP 7.4+)
$config['setting'] ??= 'default';      // Set if not exists

// Nullsafe operator (PHP 8+)
$length = $object?->property?->method();  // Won't error if null
?>

8. Resource

External resources like file handles:

<?php
// File resource
$file = fopen('data.txt', 'r');
var_dump($file);  // resource(x) of type (stream)

// Always close resources
fclose($file);

// Database connections are resources too
// $connection = mysqli_connect(...);
// var_dump($connection);  // resource
?>

9. Callable

Functions that can be called:

<?php
// Different callable types
function regularFunction() { return "Regular"; }

$anonymous = function() { return "Anonymous"; };

$arrow = fn() => "Arrow";

class MyClass {
    public function method() { return "Method"; }
    public static function staticMethod() { return "Static"; }
}

// All are callable
var_dump(is_callable('regularFunction'));           // true
var_dump(is_callable($anonymous));                   // true
var_dump(is_callable([$obj, 'method']));           // true
var_dump(is_callable('MyClass::staticMethod'));     // true

// Using callables
$functions = [
    'regularFunction',
    $anonymous,
    fn() => "Inline arrow"
];

foreach ($functions as $func) {
    echo $func() . "\n";
}
?>

10. Iterable

Anything you can loop over:

<?php
function processItems(iterable $items) {
    foreach ($items as $item) {
        echo $item . "\n";
    }
}

// Arrays are iterable
processItems([1, 2, 3]);

// Generators are iterable
function generateNumbers() {
    yield 1;
    yield 2;
    yield 3;
}
processItems(generateNumbers());
?>

Type Juggling and Casting

PHP automatically converts types when needed:

<?php
// Automatic type conversion
$number = "10";
$result = $number + 5;  // "10" becomes 10
echo $result;           // 15

// But watch out for surprises!
echo "10" + "20";       // 30 (arithmetic)
echo "10" . "20";       // 1020 (concatenation)

// Explicit casting
$string = "123.45 items";
$int = (int) $string;        // 123
$float = (float) $string;    // 123.45
$bool = (bool) $string;      // true
$array = (array) $string;    // ["123.45 items"]

// Casting edge cases
var_dump((int) "hello");     // 0
var_dump((int) "10hello");   // 10
var_dump((int) true);        // 1
var_dump((int) false);       // 0
var_dump((int) 3.9);         // 3 (truncates, doesn't round)

// Strict comparisons prevent juggling
$a = 10;
$b = "10";

var_dump($a == $b);   // true (loose comparison)
var_dump($a === $b);  // false (strict comparison)
?>

Constants: Immutable Values

Constants can't change once defined:

<?php
// Old style (still works)
define('SITE_NAME', 'My Awesome Site');
define('MAX_UPLOAD_SIZE', 1024 * 1024 * 5);  // 5MB

// Modern style (PHP 5.3+)
const API_VERSION = '2.0';
const FEATURES = ['auth', 'api', 'dashboard'];

// Case sensitivity (avoid this confusion)
define('Constant', 'value1');
define('CONSTANT', 'value2', true);  // Case-insensitive deprecated in PHP 8

// Magic constants
echo __LINE__;       // Current line number
echo __FILE__;       // Current file path
echo __DIR__;        // Current directory
echo __FUNCTION__;   // Current function name
echo __CLASS__;      // Current class name
echo __METHOD__;     // Current method name
echo __NAMESPACE__;  // Current namespace

// Checking constants
if (defined('SITE_NAME')) {
    echo SITE_NAME;
}

// Getting all constants
print_r(get_defined_constants(true)['user']);
?>

Type Declarations (Modern PHP)

PHP 7+ brought optional type declarations:

<?php
// Scalar type hints
function calculatePrice(float $price, int $quantity): float {
    return $price * $quantity;
}

// Return type declarations
function getName(): string {
    return "John Doe";
}

// Nullable types (PHP 7.1+)
function findUser(?int $id): ?array {
    if ($id === null) {
        return null;
    }
    // Find user logic
    return ['id' => $id, 'name' => 'User'];
}

// Union types (PHP 8+)
function processInput(int|float|string $input): string {
    return "Processed: " . $input;
}

// Mixed type (PHP 8+)
function anything(mixed $input): mixed {
    return $input;
}

// Strict types (must be first statement)
declare(strict_types=1);
// Now type coercion is disabled
?>

Variable Scope

Understanding scope prevents bugs:

<?php
$globalVar = "I'm global";

function demonstrateScope() {
    $localVar = "I'm local";

    // Can't access global without declaration
    // echo $globalVar;  // Error!

    // Accessing global variables
    global $globalVar;
    echo $globalVar;  // Now works

    // Or use $GLOBALS
    echo $GLOBALS['globalVar'];
}

// Static variables maintain state
function counter() {
    static $count = 0;
    return ++$count;
}

echo counter();  // 1
echo counter();  // 2
echo counter();  // 3

// Superglobals are always accessible
function showSuperglobals() {
    echo $_SERVER['REQUEST_METHOD'];  // Works everywhere
    echo $_GET['param'] ?? 'not set';
}
?>

Practical Examples

Let's build something useful with our knowledge:

<?php
// User registration validator
class RegistrationValidator {
    private const MIN_PASSWORD_LENGTH = 8;
    private const ALLOWED_DOMAINS = ['gmail.com', 'yahoo.com', 'outlook.com'];

    public static function validateEmail(string $email): array {
        $errors = [];

        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $errors[] = "Invalid email format";
        }

        $domain = substr(strrchr($email, "@"), 1);
        if (!in_array($domain, self::ALLOWED_DOMAINS)) {
            $errors[] = "Email domain not allowed";
        }

        return $errors;
    }

    public static function validatePassword(string $password): array {
        $errors = [];

        if (strlen($password) < self::MIN_PASSWORD_LENGTH) {
            $errors[] = "Password must be at least " . self::MIN_PASSWORD_LENGTH . " characters";
        }

        if (!preg_match('/[A-Z]/', $password)) {
            $errors[] = "Password must contain uppercase letter";
        }

        if (!preg_match('/[0-9]/', $password)) {
            $errors[] = "Password must contain number";
        }

        return $errors;
    }
}

// Usage
$email = "[email protected]";
$password = "Secret123";

$emailErrors = RegistrationValidator::validateEmail($email);
$passwordErrors = RegistrationValidator::validatePassword($password);

if (empty($emailErrors) && empty($passwordErrors)) {
    echo "Registration valid!";
} else {
    echo "Validation errors:\n";
    foreach (array_merge($emailErrors, $passwordErrors) as $error) {
        echo "- $error\n";
    }
}
?>

Performance Tips

Different types have different performance characteristics:

<?php
// String concatenation benchmark
$iterations = 100000;

// Method 1: Concatenation
$start = microtime(true);
$result = '';
for ($i = 0; $i < $iterations; $i++) {
    $result .= 'a';
}
$time1 = microtime(true) - $start;

// Method 2: Array join
$start = microtime(true);
$array = [];
for ($i = 0; $i < $iterations; $i++) {
    $array[] = 'a';
}
$result = implode('', $array);
$time2 = microtime(true) - $start;

echo "Concatenation: " . round($time1 * 1000, 2) . "ms\n";
echo "Array join: " . round($time2 * 1000, 2) . "ms\n";

// isset() vs array_key_exists()
$array = ['key' => null];

// isset() is faster but returns false for null values
var_dump(isset($array['key']));          // false

// array_key_exists() is slower but accurate
var_dump(array_key_exists('key', $array)); // true
?>

Common Gotchas and Solutions

Watch out for these tricky situations:

<?php
// Gotcha 1: String concatenation vs addition
echo "2" + "2";    // 4 (addition)
echo "2" . "2";    // "22" (concatenation)

// Gotcha 2: Empty vs isset
$var = "";
var_dump(empty($var));   // true (empty string is "empty")
var_dump(isset($var));   // true (variable exists)

// Gotcha 3: Boolean string evaluation
var_dump("false" == true);   // true (non-empty string)
var_dump("0" == false);      // true (string "0" is falsy)

// Gotcha 4: Array addition vs array_merge
$array1 = [1, 2, 3];
$array2 = [4, 5, 6];

print_r($array1 + $array2);        // Preserves keys: [1, 2, 3]
print_r(array_merge($array1, $array2)); // Reindexes: [1, 2, 3, 4, 5, 6]

// Gotcha 5: Reference gotcha
$array = [1, 2, 3];
foreach ($array as &$value) {
    $value *= 2;
}
// $value is still a reference!
$value = 10;  // This changes $array[2] to 10!
print_r($array);  // [2, 4, 10]
?>

Best Practices Summary

  1. Use meaningful variable names - $userEmail not $e
  2. Declare variables before use - Avoid undefined variable notices
  3. Use strict comparisons - === instead of == when possible
  4. Initialize variables - Set default values
  5. Type hint function parameters - Catch errors early
  6. Use constants for fixed values - Don't hardcode
  7. Understand type juggling - Know when PHP converts types
  8. Clean up resources - Close files, database connections
  9. Use appropriate data types - Arrays for lists, objects for structured data
  10. Comment complex type manipulations - Help future you

Practice Exercises

  1. Create a type inspector function that reports detailed information about any variable
  2. Build a configuration class using constants and static properties
  3. Implement a string formatter that handles multiple data types gracefully
  4. Create a type-safe calculator that validates inputs
  5. Build a data validator for different types (email, phone, URL, etc.)

What's Next?

You now understand PHP's type system deeply. These aren't just boring fundamentals - they're the tools you'll use in every line of code you write. Next, we'll explore operators and expressions, learning how to manipulate these data types effectively.

Remember: Every PHP master started by understanding variables. You're building the foundation for everything that follows!


Quick Reference

Variable Rules:

Type Checking Functions:

Type Casting: