PHP Type Juggling — How == Unlocks Any Account
PHP's loose comparison (==) treats 0 == '0' as true, allowing login bypass.
20+ years shipping production PHP systems at scale. Written from production experience, not tutorials.
- ✓Basic programming fundamentals
- ✓A computer with internet access
- ✓Willingness to follow along with examples
- PHP variables start with $ and can hold any data type without prior declaration
- Scalar types: string, int, float, bool — each holds one value
- Arrays: indexed (numbered) or associative (key-value) for multiple values
- Type juggling: PHP auto-converts types in expressions, which can produce surprising results
- Performance: loose comparisons (==) cause implicit conversions — always prefer === for speed and correctness
- Production trap: using == on "0" or empty strings gives wrong boolean logic — use strict equality
PHP type juggling is the language's automatic conversion of values between different data types during comparison or operation, driven by the loose equality operator ==. Unlike strict languages that throw errors or require explicit casting, PHP silently coerces types to make comparisons 'work' — which is why "0" == false returns true and "admin" == 0 also returns true.
This is not a bug; it's a deliberate design choice from PHP's early days as a template language, but it's the root cause of countless authentication bypasses. For example, if a login endpoint compares a password hash using == instead of ===, an attacker can send an empty string or 0 and potentially match a hash starting with 0e... (like 0e462097431907509062920833), because PHP interprets both as scientific notation floats equal to zero.
This is why every production PHP codebase should default to === for comparisons and use for timing-safe string comparisons.hash_equals()
Variables in PHP are declared with a $ prefix and are dynamically typed — you don't specify whether a variable holds an integer, string, or array; PHP figures it out at runtime. The eight primitive types are: boolean, integer, float (double), string, array, object, resource, and NULL.
There's also the callable pseudo-type and the mixed type hint in PHP 8+. The key insight: a variable's type can change based on context. Assign $x = "42" and it's a string; use it in arithmetic ($x + 8) and it becomes an integer 50. This is type juggling in action, and it's why var_dump($x) after that operation shows int(50), not string(2).
Type casting gives you explicit control: (int) $string, (bool) $number, (array) $object. But even explicit casts have gotchas — casting a float to int truncates toward zero, and casting an object to array converts properties to keys. Variable scope in PHP is famously simple: functions don't see global variables unless you declare global $var or use the $GLOBALS array.
This is a common source of bugs when developers assume closure-like behavior. Constants, defined with define('MAX_LOGIN_ATTEMPTS', 5) or the const keyword (PHP 7+), are immutable and global by default — they're the safest way to store configuration values that must never change, like API endpoints or encryption keys.
Use them over variables for anything that shouldn't be reassigned, and prefer const over for better performance and namespace support in classes.define()
Think of a variable like a labelled box in your bedroom. You write a name on the outside — say 'favourite colour' — and you put something inside, like the word 'blue'. Later, you can open that box, peek inside, change its contents, or use what's in it. PHP variables work exactly the same way: you give the box a name starting with a dollar sign ($), and you put a value inside it. Data types are just the category of thing you're storing — words go in one kind of box, numbers in another, true/false answers in another.
Every website you've ever used stores and moves information around — your username, the price of a product, whether you're logged in or not. PHP is the language running behind the scenes on millions of web servers doing exactly that work. To do any of it, PHP needs a way to hold pieces of information temporarily while it processes them. That's where variables come in. Without them, you couldn't build a login page, a shopping cart, or even a simple 'Hello, [your name]!' greeting.
The problem variables solve is simple: you don't always know the exact value you'll be working with when you write your code. A user's name could be 'Alice' or 'Zhang Wei' or anything in between. Rather than hardcoding a specific value, you use a variable as a placeholder — a named slot that holds whatever value arrives at runtime. Data types tell PHP what kind of value is sitting in that slot, which determines what operations are valid. You can multiply two numbers, but you can't multiply two names — and PHP needs to know the difference.
By the end of this article you'll be able to declare PHP variables with confidence, understand why PHP has eight built-in data types and when each one is appropriate, read and write real PHP code that stores and outputs meaningful data, and spot the beginner mistakes that trip up even developers who've been coding for a while. Let's build this knowledge from the ground up.
How PHP Type Juggling Opens Every Door
PHP's type juggling is the automatic coercion of values when using loose comparison (==). Instead of comparing both value and type, PHP converts operands to a common type before checking equality. This is not a bug — it's a language design choice that trades strictness for convenience, but it creates a critical attack surface when used in authentication or authorization logic.
The core mechanic: when you compare a string to an integer with ==, PHP converts the string to an integer. If the string starts with numeric characters, it takes that value; otherwise it becomes 0. So '0e12345' == 0 is true because '0e...' is interpreted as scientific notation for zero. More dangerously, 'abc' == 0 is also true — any non-numeric string becomes 0. This means a password hash like '0e462097431907509062922748828' (a common MD5 hash) equals 0 in loose comparison, and if your code checks if ($hash == $input), an attacker can submit 0 as the password and bypass authentication.
Use type juggling only when you explicitly want coercion — for example, when reading form inputs that are always strings but need numeric comparison. In all security-sensitive contexts (password verification, access control, token validation), always use === (strict comparison) to avoid unintended matches. The rule: if the comparison outcome affects authorization, use ===. If you're just sorting numbers from a form, == is fine.
password_verify() was not used and the stored hash was compared with ==. An attacker sent password=0 and matched any hash starting with '0e' (common with MD5). The symptom: users with specific password hashes could log in with password '0'. Rule: never use == for password comparison — use password_verify() or === with a constant-time comparison.Declaring PHP Variables — The Dollar Sign Rule and Naming Your Boxes
In PHP, every variable starts with a dollar sign ($). That's not optional — it's how PHP recognises 'this is a variable, not a keyword or a function name'. Immediately after the dollar sign comes your chosen name. You assign a value using a single equals sign (=), which in programming is called the assignment operator — it doesn't mean 'equal to', it means 'put this value into this box'.
Naming rules matter. Variable names must start with a letter or an underscore, never a number. They're case-sensitive, so $userAge and $userage are two completely different variables — a common source of bugs. Use descriptive names. $a means nothing to the next developer (or future you). $customerAge, $orderTotal, and $isLoggedIn all tell a story at a glance.
PHP is a loosely typed language, which means you don't have to declare what type of data a variable will hold before you use it. You just assign a value and PHP figures out the type automatically. This is very beginner-friendly, but it comes with traps we'll cover in the gotchas section. For now, know that PHP's flexibility is a feature — used carefully.
<?php // Declaring a string variable — text always goes inside quotes $customerName = "Maria Santos"; // Declaring an integer variable — whole numbers, no quotes needed $customerAge = 28; // Declaring a float variable — numbers with decimal points $accountBalance = 1042.75; // Declaring a boolean variable — only two possible values: true or false $isSubscribed = true; // Outputting variables using echo // The dot (.) is PHP's string concatenation operator — it joins strings together echo "Customer: " . $customerName . "\n"; // Prints the customer's name echo "Age: " . $customerAge . "\n"; // Prints the age as text echo "Balance: $" . $accountBalance . "\n"; // Prints the balance echo "Subscribed: "; // Starts the subscribed line echo $isSubscribed ? "Yes" : "No"; // Ternary: prints Yes if true, No if false echo "\n"; // PHP also lets you embed variables directly inside double-quoted strings // This is called variable interpolation — PHP replaces the variable with its value echo "Hello, $customerName! Your balance is $$accountBalance.\n"; // Checking what type PHP assigned using gettype() echo "Type of customerAge: " . gettype($customerAge) . "\n"; // integer echo "Type of accountBalance: " . gettype($accountBalance) . "\n"; // double (PHP calls floats 'double') echo "Type of customerName: " . gettype($customerName) . "\n"; // string ?>
var_dump() instead of echo when debugging — it shows both the value AND the data type in one shot. Try var_dump($customerAge) and you'll see int(28), which confirms exactly what PHP is storing. It's your best friend when a variable isn't behaving as expected.PHP's Eight Data Types — What Can You Actually Store?
PHP has eight built-in data types, split into three families. Scalar types hold a single value: strings (text), integers (whole numbers), floats (decimal numbers), and booleans (true/false). Compound types hold multiple values: arrays (ordered lists or key-value maps) and objects (custom structured data). Special types cover two edge cases: NULL (a variable that deliberately holds nothing) and resource (a reference to an external resource like a database connection).
As a beginner you'll spend 90% of your time with the four scalar types and arrays. Objects come into play when you learn object-oriented PHP later. NULL is more common than you'd think — it's the default state of a variable that's been declared but not assigned, and it's also useful for signalling 'no result found'.
Arrays deserve special attention because they're incredibly powerful. An indexed array works like a numbered list — items are stored at positions 0, 1, 2 and so on. An associative array works like a dictionary — each item has a named key ('email', 'age', 'city') that you use to look it up. You'll use arrays constantly in real PHP work: storing database results, handling form submissions, building configuration settings.
<?php // ── SCALAR TYPES ────────────────────────────────────────────── // STRING — any text wrapped in quotes $productName = "Wireless Noise-Cancelling Headphones"; // INTEGER — whole numbers, positive or negative, no decimal point $stockCount = 142; $temperatureInCelsius = -5; // Integers can be negative // FLOAT (also called double) — numbers with a decimal point $productPrice = 89.99; $taxRate = 0.08; // 8% tax stored as a decimal // BOOLEAN — exactly two possible values: true or false (lowercase in PHP) $isInStock = true; $isDiscounted = false; // ── COMPOUND TYPES ──────────────────────────────────────────── // INDEXED ARRAY — like a numbered shopping list, positions start at 0 $shoppingCart = ["Headphones", "Phone Case", "USB Cable"]; // ASSOCIATIVE ARRAY — like a form with labelled fields $customerProfile = [ "firstName" => "James", // key => value "lastName" => "Okonkwo", "email" => "james@example.com", "age" => 34 ]; // ── SPECIAL TYPES ───────────────────────────────────────────── // NULL — the absence of a value; this variable exists but holds nothing $discountCode = null; // ── USING THE DATA ──────────────────────────────────────────── // Calculate final price with tax $finalPrice = $productPrice + ($productPrice * $taxRate); // float arithmetic echo "Product: $productName\n"; echo "Price: $" . number_format($finalPrice, 2) . "\n"; // number_format rounds to 2 decimal places echo "In stock: " . ($isInStock ? "Yes" : "No") . "\n"; echo "Stock count: $stockCount units\n\n"; // Accessing an indexed array by position number echo "First cart item: " . $shoppingCart[0] . "\n"; // Index 0 = first item echo "Second cart item: " . $shoppingCart[1] . "\n"; // Accessing an associative array by key name echo "Customer: " . $customerProfile["firstName"] . " " . $customerProfile["lastName"] . "\n"; echo "Email: " . $customerProfile["email"] . "\n"; // Checking for NULL with is_null() if (is_null($discountCode)) { echo "No discount code applied.\n"; } // var_dump shows type + value — incredibly useful for debugging var_dump($isDiscounted); // Shows: bool(false) var_dump($taxRate); // Shows: float(0.08) ?>
gettype() on a decimal number, PHP returns 'double' not 'float'. This is a historical quirk inherited from C — double refers to double-precision floating point. The two terms mean the same thing in PHP. Don't let it confuse you; is_float() and is_double() are also identical functions.isset() before reading them.Type Juggling and Type Casting — When PHP Changes Types Behind Your Back
PHP's loosely typed nature means it will sometimes change the type of a value automatically to make an operation work. This is called type juggling or type coercion. For example, if you try to add a number to a string that starts with a number, PHP quietly converts the string to an integer and adds them. This is either magic or madness depending on the situation.
Type casting is when YOU deliberately convert one type to another, using cast operators like (int), (float), (string), or (bool). This puts you in control rather than leaving it to PHP's automatic rules, which are notoriously surprising.
The comparison operator == (double equals) performs type juggling before comparing, which causes famous PHP gotchas like 0 == 'hello' evaluating to true in older PHP versions. The strict comparison operator === (triple equals) checks both value AND type without any conversion. Senior PHP developers use === by default and only reach for == when they specifically want type-flexible comparison. As a beginner, train yourself to use === from day one — it'll save you hours of debugging.
<?php // ── TYPE JUGGLING (PHP does this automatically) ──────────────── $quantityAsString = "5"; // This is a STRING — it has quotes $bonusQuantity = 3; // This is an INTEGER // PHP sees you're adding and converts $quantityAsString to int automatically $totalQuantity = $quantityAsString + $bonusQuantity; echo "Total quantity: $totalQuantity\n"; // Outputs: 8 echo "Type: " . gettype($totalQuantity) . "\n"; // Outputs: integer (PHP juggled the type) // A string that DOESN'T start with a number becomes 0 in arithmetic $nonsenseString = "apples"; $result = $nonsenseString + 10; echo "Nonsense arithmetic result: $result\n"; // Outputs: 10 ("apples" became 0) // ── TYPE CASTING (YOU control the conversion) ───────────────── $rawInput = "42.7 degrees"; // Imagine this came from a form submission $asInteger = (int) $rawInput; // Casts to int — stops at the decimal point $asFloat = (float) $rawInput; // Casts to float — grabs 42.7, ignores " degrees" echo "\nRaw input: $rawInput\n"; echo "Cast to int: $asInteger\n"; // Outputs: 42 echo "Cast to float: $asFloat\n"; // Outputs: 42.7 // Casting to boolean — useful to know what PHP considers 'falsy' $emptyString = (bool) ""; // false — empty string is falsy $zeroValue = (bool) 0; // false — zero is falsy $zeroString = (bool) "0"; // false — the string "0" is ALSO falsy (a classic gotcha!) $nonEmptyStr = (bool) "hello"; // true — any non-empty, non-"0" string is truthy echo "\n--- Boolean casts ---\n"; var_dump($emptyString); // bool(false) var_dump($zeroValue); // bool(false) var_dump($zeroString); // bool(false) ← catches people out! var_dump($nonEmptyStr); // bool(true) // ── LOOSE vs STRICT COMPARISON ──────────────────────────────── $formInput = "10"; // String from a form field $dbValue = 10; // Integer from a database // == (loose): converts types before comparing — both become 10, so true $looseResult = ($formInput == $dbValue); echo "\nLoose comparison (==): " . ($looseResult ? "true" : "false") . "\n"; // true // === (strict): checks type AND value — string vs integer, so false $strictResult = ($formInput === $dbValue); echo "Strict comparison (===): " . ($strictResult ? "true" : "false") . "\n"; // false ?>
Variable Scope — Where Your Variables Live and Die
In PHP, not all variables are visible everywhere. The scope of a variable determines where it can be accessed. The main scopes are: local (inside a function), global (outside any function), static (persistent across function calls), and superglobal (available everywhere — like $_POST, $_SESSION).
Variables defined inside a function are local to that function — they don't exist outside it. If you need to use a global variable inside a function, you must explicitly import it with the 'global' keyword. This is different from many other languages and catches beginners off guard. PHP's function scope is intentionally isolated to prevent accidental side effects, but it also means you must think carefully about which variables need to cross boundaries.
Static variables inside functions retain their value between calls — perfect for counters or caches. Superglobals like $_GET, $_POST, $_SERVER are built-in arrays that hold request data and are accessible everywhere without any special declaration.
<?php // Global variable $globalCounter = 10; function increment() { // This is a local variable — it only exists inside this function $localCounter = 0; $localCounter++; echo "Local counter: $localCounter\n"; // Each call: 1 // To use the global $globalCounter, we need the 'global' keyword global $globalCounter; $globalCounter++; echo "Global counter inside function: $globalCounter\n"; } increment(); // Local: 1, Global: 11 increment(); // Local: 1, Global: 12 increment(); // Local: 1, Global: 13 // Static variable example function counterWithStatic() { static $staticCount = 0; // Initialized only once, persists across calls $staticCount++; echo "Static count: $staticCount\n"; } counterWithStatic(); // 1 counterWithStatic(); // 2 counterWithStatic(); // 3 // Superglobal $_GET example (simulated with a query string) // Assume URL: index.php?name=Maria if (isset($_GET['name'])) { $visitor = $_GET['name']; echo "Hello, $visitor!\n"; } ?> <!-- Note: Superglobals are always available -->
Constants and define() — Values That Never Change
Sometimes you need a value that should never be reassigned — like the tax rate for your store or the API version number. PHP gives you two ways to define constants: the define() function (old style) and the const keyword (new style, available since PHP 5.3). Constants are automatically global — you can use them anywhere in your script without the 'global' keyword.
The main difference: define() can be called inside control structures (like loops or if blocks), while const must be used at the top level of a file. const also works inside classes to define class constants. Both follow the naming convention of all-uppercase with underscores (TAX_RATE, MAX_LOGIN_ATTEMPTS), but this is a convention — not enforced by PHP.
Magic constants like __FILE__, __LINE__, __DIR__ are predefined constants that change based on where they're used. They're incredibly useful for debugging and logging because they tell you exactly where your code is executing.
<?php // Define a constant using define() — old style but still widely used define('TAX_RATE', 0.08); define('APP_NAME', 'MyStore'); // Define a constant using const — only allowed at top level const MAX_LOGIN_ATTEMPTS = 5; // Using constants — note: no dollar sign! echo "Welcome to " . APP_NAME . "\n"; echo "Tax rate: " . (TAX_RATE * 100) . "%\n"; echo "Max login attempts: " . MAX_LOGIN_ATTEMPTS . "\n"; // Magic constants — change depending on context echo "This file: " . __FILE__ . "\n"; echo "This line: " . __LINE__ . "\n"; echo "This function: " . __FUNCTION__ . "\n"; // empty because not inside a function function test() { echo "Inside function: " . __FUNCTION__ . "\n"; } test(); // Constants are case-insensitive by default, but stick to uppercase for clarity // echo Tax_Rate; // Works but don't do this — confusing // Trying to reassign a constant causes an error // define('TAX_RATE', 0.10); // Warning: Constant TAX_RATE already defined ?> <!-- Output: Welcome to MyStore Tax rate: 8% Max login attempts: 5 This file: /var/www/constants.php This line: 15 This function: Inside function: test -->
define() for conditional constants.Strict Typing and Type Declarations — Taming PHP's Loose Nature
PHP 7 introduced optional type declarations for function parameters and return values. PHP 8 expanded them further. With declare(strict_types=1) at the top of a file, PHP enforces that a function receives exactly the declared type — no automatic conversion. Without it, PHP will still try to juggle types even if you declare them.
Strict mode is a game-changer for production code. It turns type-related bugs from silent logic errors into immediate fatal errors. You don't want to find out at 3 AM that a function meant to receive an integer got a string and quietly returned wrong results. Strict mode forces you to be explicit about the types flowing through your system.
For beginners, it's okay to start without strict types, but once you're comfortable, turn them on in every file. They make your code self-documenting and catch a whole class of bugs before they reach users.
<?php declare(strict_types=1); // THIS LINE MUST BE THE FIRST LINE AFTER <?php // Enforced type declarations function calculateTotal(float $price, int $quantity): float { return $price * $quantity; } // Works fine — both arguments are correct types $total = calculateTotal(29.99, 3); echo "Total: $" . number_format($total, 2) . "\n"; // 89.97 // Without strict_types, PHP would convert '3' to int 3 and work silently // With strict_types, this will throw a TypeError: // $total = calculateTotal(29.99, '3'); // TypeError: Argument 2 must be of type int, string given // Strict return type — must match exactly function getPrice(): float { return 19.99; // float is fine } // The following would fail: // function getPrice(): float { // return '19.99'; // TypeError: Return value must be of type float, string returned // } echo "Price: $" . getPrice() . "\n"; ?> <!-- Output: Total: $89.97 Price: $19.99 -->
PHP's Type System: Why You Can't Just Ignore Internally Consistent Behavior
Every variable in PHP has an internal type that PHP determines at runtime. That type isn't a suggestion — it's the engine's truth. When you assign $count = 10, PHP marks it as integer. When you assign $count = '10', it's a string. The language does not complain, but it does track the difference. Understanding this internal typing is what separates a dev who gets surprised by '10' + 5 from one who predicts 15. PHP's eight data types break into three families: scalar (bool, int, float, string), compound (array, object, callable, iterable), and special (null, resource). Each has rules for comparison, arithmetic, and casting. Ignore them, and you'll chase bugs where false == 0 evaluates to true but false === 0 doesn't. Know your types, and you own the behavior.
// io.thecodeforge declare(strict_types=1); $count = 10; // integer $label = '10'; // string var_dump($count == $label); // bool(true) — type juggling var_dump($count === $label); // bool(false) — strict identity check // Production trap: loose comparison in conditionals if ($count == $label) { // This runs, but you probably didn't mean it echo "Values match loosely.\n"; } if ($count === $label) { echo "This will never print.\n"; }
== for value comparisons between strings and integers. A 0 from a form field can match any non-numeric string. Use === or explicit casting like (int)$input.===) unless you deliberately need type juggling. It prevents silent bugs that only appear in production.Null vs Undefined: The Difference That Crashes Your API
null and undefined are not the same in PHP. null is an explicit value meaning 'no value'. An undefined variable means the variable was never set. Accessing an undefined variable triggers a warning and returns null, but the warning is the real problem: it can fill logs, break error handlers, and expose internals. Declare null intentionally using $var = null; or unset($var). Check existence with which returns false for both isset()null values and undefined variables. For strict null checks, use or is_null()$var === null. In PHP 8.x, the nullsafe operator ?-> lets you chain calls without checking each level: $user?->getProfile()?->getEmail() returns null if any call in the chain fails. That's cleaner than wrapping every call in .isset()
// io.thecodeforge declare(strict_types=1); $explicitNull = null; // $implicitUndefined is never defined var_dump(isset($explicitNull)); // bool(false) — null counts as unset var_dump(isset($implicitUndefined)); // bool(false) — also undefined var_dump(is_null($explicitNull)); // bool(true) // var_dump(is_null($implicitUndefined)); // throws undefined variable warning // PHP 8.x nullsafe operator class User { public ?Profile $profile = null; } class Profile { public ?string $email = null; } $user = new User(); $email = $user?->profile?->email; // null, no warnings var_dump($email); // NULL
$var = null explicitly when you lack a value. This eliminates undefined variable warnings and makes your intent clear to the next engineer.isset() to check if a variable exists and is not null, is_null() to check for explicit null, and the nullsafe operator to avoid verbose conditional checks.The PayPal Type Juggling Bug Exposed
- Never use == for security-critical comparisons — always ===.
- PHP's type juggling makes 0 == '0' true, 0 == 'false' false, and 'false' == 0 true — these aren't intuitive.
- Apply declare(strict_types=1) in all production files to prevent unintended type coercion.
var_dump() to inspect the type. If a string like '5 apples' appears as 5, PHP juggled it. Cast explicitly with (int) or use is_numeric() first.isset() or array_key_exists(). Remember array keys start at 0.is_null() check before using the variable. Explicitly handle null cases.var_dump($var);gettype($var);gettype() for quick type string, var_dump() for full inspection.var_dump($stringValue === $numericValue);var_dump(is_numeric($stringValue));isset($array[$key]);array_key_exists($key, $array);var_dump((bool)$value);var_dump($value);| Data Type | Example Value | Use Case | Falsy When | PHP Type Check Function |
|---|---|---|---|---|
| String | "hello@example.com" | Names, emails, messages, HTML | Empty string "" or "0" | is_string() |
| Integer | 42 or -7 | Counts, IDs, ages, quantities | Value is 0 | is_int() |
| Float | 19.99 or 0.075 | Prices, percentages, coordinates | Value is 0.0 | is_float() |
| Boolean | true or false | Flags, toggles, conditions | Value is false | is_bool() |
| Array | ["a", "b"] or ["key"=>"val"] | Lists, records, config settings | Empty array [] | is_array() |
| NULL | null | Missing data, unset variables | Always falsy | is_null() |
| File | Command / Code | Purpose |
|---|---|---|
| variable_basics.php | $customerName = "Maria Santos"; | Declaring PHP Variables |
| data_types_overview.php | $productName = "Wireless Noise-Cancelling Headphones"; | PHP's Eight Data Types |
| type_juggling_and_casting.php | $quantityAsString = "5"; // This is a STRING — it has quotes | Type Juggling and Type Casting |
| variable_scope.php | $globalCounter = 10; | Variable Scope |
| constants.php | define('TAX_RATE', 0.08); | Constants and define() |
| strict_types.php | declare(strict_types=1); // THIS LINE MUST BE THE FIRST LINE AFTER | Strict Typing and Type Declarations |
| type_identity.php | declare(strict_types=1); | PHP's Type System |
| null_vs_undefined.php | declare(strict_types=1); | Null vs Undefined |
Key takeaways
var_dump().define() or const) hold values that never changeCommon mistakes to avoid
5 patternsUsing = instead of == or === in conditions
Forgetting that array indexes start at 0, not 1
end() function to grab the last element safely.Using single quotes when you expect variable interpolation to work
Assuming loose comparison (==) behaves like strict (===)
Not validating array keys before accessing them
isset() or array_key_exists() before reading a key. Use the null coalescing operator (?? ) to provide a default: $username = $_POST['username'] ?? '';Interview Questions on This Topic
What is the difference between == and === in PHP, and when would using == instead of === cause a bug in production code?
PHP is described as loosely typed — what does that mean exactly, and what is the potential downside of loose typing when handling user-submitted form data?
If a variable is declared as $count = '0', what does var_dump((bool) $count) output, and why does that result matter when writing conditional logic?
Explain the difference between define() and const for declaring constants in PHP. When would you use one over the other?
define() is evaluated at runtime. For modern code, prefer const for simple top-level constants, and use define() when you need conditional definition or dynamic constant names (e.g., define('CONFIG_' . $env, $value)).Frequently Asked Questions
No — PHP is dynamically typed, meaning you just assign a value and PHP automatically determines the type. You can write $score = 100 without ever saying 'this is an integer'. However, if you want to enforce strict types (a good practice in larger projects), add declare(strict_types=1) at the very top of your PHP file before any other code.
An empty string ("") is a valid string value that contains zero characters — the variable exists and holds a string type. null means the variable has no value at all — it holds nothing. They're both falsy in a boolean context, but is_null("") returns false while is_null(null) returns true. Use null to signal 'no data' and empty string to signal 'data was provided but it's blank'.
PHP was designed in the early 1990s and borrowed the $ prefix from Perl, which was the dominant server-side scripting language at the time. It serves a practical purpose — it lets PHP instantly identify variables inside strings for interpolation, and it prevents naming conflicts with built-in PHP keywords. It's purely a language design decision that became part of PHP's identity.
No — constants are immutable by design. Once defined with define() or const, their value cannot be changed or undefined. Attempting to redefine a constant will produce a warning and the original value will remain. If you need a value that might change, use a variable instead.
Single quotes treat the string literally — no variable interpolation occurs. Double quotes parse variables and some escape sequences ( , \t, etc.). For most simple strings, single quotes are slightly faster because PHP doesn't parse them. Use double quotes when you want to embed variables directly: echo "Hello, $name"; rather than concatenation.
20+ years shipping production PHP systems at scale. Written from production experience, not tutorials.
That's PHP Basics. Mark it forged?
6 min read · try the examples if you haven't