PHP powers over 75% of websites with a known server-side language. Interviews test your understanding of the language core, OOP, security, PHP 8 features, and modern tooling. This guide covers the 50 most common questions — with concise answers and code examples.
Quick reference
| Topic | Most asked questions |
|---|---|
| Types & Variables | == vs ===, type juggling, casting |
| OOP | Abstract vs interface, traits, visibility |
| PHP 8+ | Match expression, named args, enums, fibers |
| Arrays | Functions, sorting, array_map vs array_walk |
| Strings | Manipulation, regex, multibyte functions |
| Error handling | Exceptions, error levels, try/catch/finally |
| Database | PDO, prepared statements, SQL injection |
| Security | XSS, CSRF, password hashing, filter_input |
| Composer & PSR | Autoloading, PSR-4, PSR-12 |
| Patterns | Singleton, Factory, Repository, DI |
Types & Variables
1. What is the difference between == and === in PHP?
== performs loose comparison with type juggling; === checks value and type.
var_dump(0 == "foo"); // true (PHP 7) / false (PHP 8!)
var_dump(0 === "foo"); // false
var_dump("1" == true); // true
var_dump("1" === true); // false
var_dump(null == false); // true
var_dump(null === false);// false
PHP 8 change:
0 == "foo"now returnsfalse(the string is cast to0only when it looks numeric).
Use === by default — it is unambiguous.
2. What are PHP's scalar types?
| Type | Values | Notes |
|---|---|---|
bool |
true, false |
Case-insensitive |
int |
−2^63 to 2^63−1 |
Platform dependent (64-bit common) |
float |
IEEE 754 double | Use bcmath for money |
string |
Sequence of bytes | Not natively Unicode-aware |
Compound types: array, object, callable, iterable.
Special types: null, void, never (return type only).
3. What is type juggling and how do you avoid it?
PHP automatically converts types when an operation requires it:
echo "5" + 3; // 8 (string cast to int)
echo "5 dogs" + 1; // 6 (leading numeric prefix)
Avoid it by:
- Using strict comparison (
===) - Enabling strict mode:
declare(strict_types=1);at the top of the file - Using typed properties and return types
declare(strict_types=1);
function add(int $a, int $b): int {
return $a + $b;
}
add("5", 3); // TypeError in strict mode
4. What are nullable types and union types?
// Nullable type (PHP 7.1+) — value or null
function findUser(?int $id): ?User { ... }
// Union types (PHP 8.0+)
function processInput(int|string $value): void { ... }
// PHP 8.1: intersection types
function render(Countable&Stringable $obj): string { ... }
// PHP 8.2: true, false, null as standalone types
function isEnabled(): true { return true; }
5. What is the difference between isset(), empty(), and is_null()?
| Expression | null |
0 |
"" |
"0" |
[] |
false |
|---|---|---|---|---|---|---|
isset($x) |
false | true | true | true | true | true |
empty($x) |
true | true | true | true | true | true |
is_null($x) |
true | false | false | false | false | false |
isset() also returns false if the variable is not declared — unlike is_null(), which throws a notice on undefined variables.
OOP
6. What is the difference between abstract class and interface?
| Feature | Abstract class | Interface |
|---|---|---|
| Methods | Can have implemented methods | Only abstract (until PHP 8 + default methods via traits) |
| Properties | Can have properties | Cannot have properties |
| Constructor | Yes | No |
| Inheritance | Single (extends) | Multiple (implements) |
| Constants | Yes | Yes |
| Use when | Shared base behaviour | Contract / capability |
abstract class Animal {
protected string $name;
abstract public function speak(): string;
public function introduce(): string {
return "I'm {$this->name}: " . $this->speak();
}
}
interface Serializable {
public function serialize(): string;
}
class Dog extends Animal implements Serializable {
public function __construct(string $name) {
$this->name = $name;
}
public function speak(): string { return "Woof!"; }
public function serialize(): string { return json_encode(['name' => $this->name]); }
}
7. What are traits and when do you use them?
A trait is a mechanism for code reuse in single-inheritance languages. It is copied into the class at compile time.
trait Timestamps {
private \DateTimeImmutable $createdAt;
private \DateTimeImmutable $updatedAt;
public function touch(): void {
$this->updatedAt = new \DateTimeImmutable();
}
public function getCreatedAt(): \DateTimeImmutable {
return $this->createdAt;
}
}
class Post {
use Timestamps;
public function __construct(private string $title) {
$this->createdAt = new \DateTimeImmutable();
$this->updatedAt = new \DateTimeImmutable();
}
}
Use traits for horizontal reuse — shared behaviour that doesn't fit in an inheritance hierarchy (e.g., logging, timestamps, soft deletes).
Conflict resolution:
class Foo {
use TraitA, TraitB {
TraitA::hello insteadof TraitB;
TraitB::hello as helloB;
}
}
8. Explain PHP visibility modifiers.
| Modifier | Same class | Subclass | Outside |
|---|---|---|---|
public |
✓ | ✓ | ✓ |
protected |
✓ | ✓ | ✗ |
private |
✓ | ✗ | ✗ |
PHP 8.1 added readonly properties (set once in constructor, then immutable):
class Point {
public function __construct(
public readonly float $x,
public readonly float $y,
) {}
}
$p = new Point(1.0, 2.0);
$p->x = 3.0; // Error: Cannot modify readonly property
9. What are magic methods? Name five important ones.
| Method | When called |
|---|---|
__construct() |
Object instantiation |
__destruct() |
Object destruction / end of scope |
__get($name) |
Reading inaccessible/undefined property |
__set($name, $value) |
Writing inaccessible property |
__toString() |
Object used as string |
__invoke() |
Object called as function |
__clone() |
Object is cloned |
__call($name, $args) |
Calling inaccessible method |
__callStatic($name, $args) |
Calling inaccessible static method |
__debugInfo() |
var_dump() output |
class Money {
public function __construct(
private int $amount,
private string $currency
) {}
public function __toString(): string {
return "{$this->amount} {$this->currency}";
}
public function __invoke(int $factor): static {
return new static($this->amount * $factor, $this->currency);
}
}
$price = new Money(100, 'EUR');
echo $price; // "100 EUR"
$double = $price(2);
echo $double; // "200 EUR"
10. What is late static binding? How does it differ from self?
self::refers to the class where the method is defined.static::refers to the class used at runtime (late static binding, PHP 5.3+).
class Base {
public static function create(): static {
return new static(); // Late static binding
}
public static function className(): string {
return static::class; // Runtime class name
}
}
class Child extends Base {}
$obj = Child::create(); // Returns Child instance (not Base)
echo Child::className(); // "Child"
PHP 8+ Features
11. What is the match expression?
match is a strict, expression-based replacement for switch:
// switch (loose ==, needs break, no return value)
switch ($status) {
case 1:
$label = 'Active';
break;
case 2:
$label = 'Inactive';
break;
default:
$label = 'Unknown';
}
// match (strict ===, expression, exhaustive)
$label = match($status) {
1 => 'Active',
2, 3 => 'Inactive', // Multiple conditions
default => throw new \InvalidArgumentException("Unknown status"),
};
match throws UnhandledMatchError if no arm matches and there is no default.
12. What are named arguments?
Named arguments let you pass values by parameter name, skipping optional parameters:
// Before PHP 8 — you had to pass every preceding arg
array_slice($array, 0, null, true); // preserve_keys = true
// PHP 8 — skip to the parameter you need
array_slice($array, 0, preserve_keys: true);
// Self-documenting
$result = implode(separator: ', ', array: $items);
Cannot be used with variadic ...$args.
13. What are enums (PHP 8.1)?
Enums are first-class types instead of class constants or integer mappings:
// Pure enum (no value)
enum Status {
case Active;
case Inactive;
case Pending;
}
// Backed enum (int or string backing value)
enum Color: string {
case Red = 'red';
case Green = 'green';
case Blue = 'blue';
public function label(): string {
return ucfirst($this->value);
}
}
// Usage
$status = Status::Active;
$color = Color::from('red'); // Color::Red
$color2 = Color::tryFrom('purple'); // null (not an exception)
echo $color->label(); // "Red"
// Enums in match
$hex = match($color) {
Color::Red => '#ff0000',
Color::Green => '#00ff00',
Color::Blue => '#0000ff',
};
14. What is the nullsafe operator (?->)?
Chains method/property access and short-circuits on null:
// Before PHP 8 — nested null checks
$city = null;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
$city = $address->getCity();
}
}
// PHP 8 nullsafe operator
$city = $user?->getAddress()?->getCity();
Returns null as soon as any link in the chain is null.
15. What are Fibers (PHP 8.1)?
Fibers are lightweight cooperative coroutines — stackful, interruptible functions:
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('first'); // Yield control, receive value back
echo "Resumed with: {$value}\n";
});
$yielded = $fiber->start(); // Start fiber, run to first suspend
echo "Fiber yielded: {$yielded}\n"; // "Fiber yielded: first"
$fiber->resume('hello'); // Resume, pass value in
// "Resumed with: hello"
Fibers power async frameworks like ReactPHP and Amp without callback hell.
Arrays
16. What is the difference between array_map() and array_walk()?
array_map() |
array_walk() |
|
|---|---|---|
| Return value | New array | true/false (modifies in-place) |
| Keys | Preserves (string), resets (int) | Preserves |
| Callback args | (value) |
(&value, key) |
| Multiple arrays | Yes | No |
$prices = [100, 200, 300];
// array_map — returns new array
$with_tax = array_map(fn($p) => $p * 1.2, $prices);
// array_walk — mutates in place
array_walk($prices, function (&$price, $key) {
$price = "$key: {$price}€";
});
17. How do you sort an array while preserving keys?
| Function | Sorts by | Preserves keys |
|---|---|---|
sort() |
Values ascending | No |
rsort() |
Values descending | No |
asort() |
Values ascending | Yes |
arsort() |
Values descending | Yes |
ksort() |
Keys ascending | Yes |
krsort() |
Keys descending | Yes |
usort() |
Custom callback | No |
uasort() |
Custom callback | Yes |
uksort() |
Custom (keys) | Yes |
$scores = ['Alice' => 90, 'Bob' => 75, 'Carol' => 85];
arsort($scores); // Alice=>90, Carol=>85, Bob=>75 (keys preserved)
// Custom object sort
usort($users, fn($a, $b) => $a->age <=> $b->age);
18. What does the spread operator (...) do with arrays?
// Unpack into function call
$args = [1, 2, 3];
echo array_sum(...$args); // Doesn't work this way; used in function defs
// Merge arrays (PHP 7.4+)
$a = [1, 2, 3];
$b = [4, 5, 6];
$merged = [...$a, ...$b]; // [1, 2, 3, 4, 5, 6]
// String keys (PHP 8.1+)
$defaults = ['color' => 'red', 'size' => 'M'];
$overrides = ['size' => 'L'];
$final = [...$defaults, ...$overrides]; // ['color' => 'red', 'size' => 'L']
19. What is array_reduce() and when do you use it?
array_reduce() folds an array into a single value using a callback:
$orders = [
['amount' => 100, 'tax' => 20],
['amount' => 200, 'tax' => 40],
['amount' => 50, 'tax' => 10],
];
$total = array_reduce($orders, function (int $carry, array $order): int {
return $carry + $order['amount'] + $order['tax'];
}, 0);
// $total = 420
Strings
20. How do PHP's string delimiters differ?
| Syntax | Variable interpolation | Escape sequences |
|---|---|---|
Single quotes '' |
No | \\ and \' only |
Double quotes "" |
Yes | Full (\n, \t, \u{...}) |
Heredoc <<<EOT |
Yes | Full |
Nowdoc <<<'EOT' |
No | None |
$name = "World";
echo 'Hello $name'; // Hello $name
echo "Hello $name"; // Hello World
echo "Hello {$name}!"; // Hello World! (complex expressions)
$sql = <<<EOT
SELECT *
FROM users
WHERE name = '$name'
EOT;
21. What are multibyte string functions and why do you need them?
PHP's standard strlen(), substr(), strtolower() operate on bytes, not characters. For UTF-8 text, one character can be 1–4 bytes.
$str = "café";
echo strlen($str); // 5 (bytes, not 4!)
echo mb_strlen($str); // 4 (characters)
echo strtolower("İ"); // wrong for Turkish
echo mb_strtolower("İ", 'UTF-8'); // correct
echo substr("café", 3); // may cut in mid-character
echo mb_substr("café", 3); // "é" — correct
Always use mb_* functions for user-facing strings. Set mbstring.internal_encoding = UTF-8 in php.ini.
22. How do you use regular expressions in PHP?
// preg_match — find first match
if (preg_match('/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i', $email)) {
echo "Valid email";
}
// preg_match_all — find all matches
$html = '<a href="foo.html">Foo</a> <a href="bar.html">Bar</a>';
preg_match_all('/<a href="([^"]+)">/', $html, $matches);
// $matches[1] = ['foo.html', 'bar.html']
// preg_replace
$clean = preg_replace('/\s+/', ' ', $text);
// preg_split
$parts = preg_split('/[\s,;]+/', "one, two; three");
// preg_replace_callback
$result = preg_replace_callback('/\d+/', function ($m) {
return $m[0] * 2;
}, "I have 3 cats and 5 dogs"); // "I have 6 cats and 10 dogs"
Error Handling
23. What are the PHP error levels?
| Constant | Value | Description |
|---|---|---|
E_ERROR |
1 | Fatal run-time error (stops execution) |
E_WARNING |
2 | Run-time warning (does not stop) |
E_NOTICE |
8 | Informational (undefined variable) |
E_DEPRECATED |
8192 | Deprecated feature |
E_ALL |
32767 | All errors |
In php.ini:
- Development:
error_reporting = E_ALL - Production:
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT+display_errors = Off+log_errors = On
24. How does exception handling work in PHP?
class NotFoundException extends \RuntimeException {}
class DatabaseException extends \RuntimeException {}
function findUser(int $id): array {
try {
$user = $db->query("SELECT * FROM users WHERE id = ?", [$id]);
if (!$user) {
throw new NotFoundException("User {$id} not found", 404);
}
return $user;
} catch (DatabaseException $e) {
logger()->error('DB error', ['exception' => $e]);
throw new \RuntimeException('Database unavailable', 503, $e); // Chain exceptions
} finally {
$db->close(); // Always runs
}
}
// Multiple catch (PHP 8: catch without variable)
try {
findUser(99);
} catch (NotFoundException | \InvalidArgumentException $e) {
http_response_code(404);
echo $e->getMessage();
} catch (\Throwable $e) { // Catches Error and Exception
http_response_code(500);
}
25. What is the difference between Exception and Error?
Since PHP 7, both implement \Throwable:
Exception |
Error |
|
|---|---|---|
| Use for | Application / business logic | PHP engine errors |
| Examples | InvalidArgumentException, RuntimeException |
TypeError, ParseError, ArithmeticError |
| Catchable | Yes | Yes (with catch(\Error) or catch(\Throwable)) |
// Catching both
try {
riskyOperation();
} catch (\Exception $e) {
// Application-level
} catch (\Error $e) {
// Engine-level (TypeError, etc.)
}
// Catch everything
} catch (\Throwable $e) { ... }
Database & Security
26. What is PDO and why use it over mysqli?
PDO (PHP Data Objects) is a database abstraction layer supporting 12 drivers.
| Feature | PDO | mysqli |
|---|---|---|
| Database support | 12+ | MySQL only |
| API style | OOP + procedural | OOP + procedural |
| Named placeholders | Yes (:name) |
No |
| Prepared statements | Yes | Yes |
| Transactions | Yes | Yes |
// PDO connection
$pdo = new \PDO(
'mysql:host=localhost;dbname=mydb;charset=utf8mb4',
'user',
'password',
[
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
]
);
// Prepared statement (prevents SQL injection)
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND active = :active');
$stmt->execute([':email' => $email, ':active' => 1]);
$user = $stmt->fetch();
27. How do you prevent SQL injection?
Never interpolate user input into queries. Always use prepared statements:
// VULNERABLE
$sql = "SELECT * FROM users WHERE name = '{$_GET['name']}'";
// SAFE — PDO named placeholder
$stmt = $pdo->prepare('SELECT * FROM users WHERE name = :name');
$stmt->execute([':name' => $_GET['name']]);
// SAFE — positional placeholder
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$_GET['id']]);
Additional measures:
- Whitelist column/table names if they come from user input
- Use
LIMITto reduce data exposure - Apply least-privilege DB user permissions
28. How do you prevent XSS (Cross-Site Scripting)?
Escape output according to context:
// HTML context — htmlspecialchars
echo htmlspecialchars($userInput, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
// URL context
echo urlencode($userInput);
// JavaScript context — json_encode is safe
$json = json_encode($userInput, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
echo "<script>var data = {$json};</script>";
Content Security Policy (HTTP header) as defense in depth:
Content-Security-Policy: default-src 'self'; script-src 'self'
29. How do you hash passwords correctly in PHP?
// Hash (uses bcrypt by default — strong, salted, adaptive)
$hash = password_hash($plainPassword, PASSWORD_BCRYPT, ['cost' => 12]);
// Or use the recommended default (currently bcrypt, future-proof)
$hash = password_hash($plainPassword, PASSWORD_DEFAULT);
// Verify — NEVER compare hashes with == or ===
if (password_verify($plainPassword, $storedHash)) {
// Authenticated
// Rehash if algorithm or cost changed
if (password_needs_rehash($storedHash, PASSWORD_DEFAULT)) {
$newHash = password_hash($plainPassword, PASSWORD_DEFAULT);
// Store $newHash
}
}
Never use: md5(), sha1(), sha256() for passwords — too fast, no salt by default.
30. How do you handle CSRF protection?
// Generate token (store in session)
function csrfToken(): string {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// Embed in form
echo '<input type="hidden" name="csrf_token" value="' . csrfToken() . '">';
// Validate on POST
function validateCsrf(): void {
$token = $_POST['csrf_token'] ?? '';
if (!hash_equals($_SESSION['csrf_token'] ?? '', $token)) {
http_response_code(403);
die('CSRF validation failed');
}
}
Use hash_equals() to prevent timing attacks.
Composer & PSR Standards
31. What is Composer and what does composer.lock do?
Composer is PHP's dependency manager. composer.lock pins exact versions of all packages (including transitive dependencies) for reproducible installs.
composer require vendor/package # Add dependency
composer require --dev vendor/package # Dev dependency
composer install # Install from composer.lock (CI/prod)
composer update # Update within constraints + rewrite lock
composer dump-autoload # Regenerate autoloader
Always commit composer.lock for applications. Libraries should gitignore it.
32. What is PSR-4 autoloading?
PSR-4 maps namespace prefixes to directory paths:
// composer.json
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
App\Controllers\UserController → src/Controllers/UserController.php
Rules:
- One class per file
- File name == class name (case-sensitive on Linux)
- Namespace mirrors directory structure
33. What are the key PSR standards?
| PSR | Topic |
|---|---|
| PSR-1 | Basic coding standard (opening tags, class names) |
| PSR-2 / PSR-12 | Coding style (indentation, braces, line length) |
| PSR-3 | Logger interface |
| PSR-4 | Autoloading |
| PSR-6 | Caching interface |
| PSR-7 | HTTP message interface |
| PSR-11 | Container interface (DI) |
| PSR-14 | Event dispatcher |
| PSR-15 | HTTP handlers (middleware) |
| PSR-17 | HTTP factories |
| PSR-18 | HTTP client |
Design Patterns
34. Implement the Singleton pattern in PHP.
final class Database {
private static ?self $instance = null;
private \PDO $connection;
private function __construct() {
$this->connection = new \PDO(/* ... */);
}
// Prevent cloning
private function __clone() {}
public static function getInstance(): static {
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function getConnection(): \PDO {
return $this->connection;
}
}
$db = Database::getInstance();
In modern PHP, prefer Dependency Injection over Singleton — it makes testing easier.
35. What is the Repository pattern?
Separates data access from business logic:
interface UserRepository {
public function find(int $id): ?User;
public function findAll(): array;
public function save(User $user): void;
public function delete(int $id): void;
}
class PdoUserRepository implements UserRepository {
public function __construct(private \PDO $pdo) {}
public function find(int $id): ?User {
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id]);
$row = $stmt->fetch();
return $row ? User::fromArray($row) : null;
}
// ...
}
// Controller depends on the interface, not the concrete class
class UserController {
public function __construct(private UserRepository $users) {}
public function show(int $id): array {
$user = $this->users->find($id);
if (!$user) throw new NotFoundException();
return $user->toArray();
}
}
36. What is Dependency Injection?
DI passes dependencies to a class rather than the class creating them:
// BAD — tight coupling
class OrderService {
private EmailService $mailer;
public function __construct() {
$this->mailer = new EmailService(); // hard to test, hard to swap
}
}
// GOOD — constructor injection
class OrderService {
public function __construct(
private readonly EmailServiceInterface $mailer,
private readonly OrderRepository $orders,
) {}
}
// Wire up with a DI container (e.g., PHP-DI, Pimple)
$container->bind(EmailServiceInterface::class, SmtpEmailService::class);
$service = $container->get(OrderService::class);
Performance
37. What is OPcache and how does it work?
OPcache stores the compiled bytecode of PHP scripts in shared memory, eliminating repeated parsing and compilation.
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60 ; seconds (0 = always revalidate in dev)
opcache.validate_timestamps=0 ; disable for maximum performance in prod
# Check OPcache status
php -r "var_dump(opcache_get_status());"
# Invalidate a single file after deploy
opcache_invalidate('/path/to/file.php', true);
38. What are some PHP performance tips?
| Tip | Why |
|---|---|
| Enable OPcache | Eliminates repeated compilation |
Use echo not print |
print is slightly slower (returns value) |
Avoid @ error suppression |
Slow — use proper checks instead |
Use isset() instead of array_key_exists() for nulls |
Faster |
Prefer str_contains() over strpos() !== false |
PHP 8, clearer + slightly faster |
| Use generators for large datasets | Low memory — values on demand |
| Cache DB queries | APCu, Redis, Memcached |
| Profile with Xdebug/Blackfire | Find real bottlenecks |
Modern PHP Patterns
39. What are generators and when are they useful?
A generator is a function that yields values on demand without building a full array in memory:
// Memory efficient CSV reader
function readCsv(string $path): \Generator {
$fh = fopen($path, 'r');
while (($row = fgetcsv($fh)) !== false) {
yield $row;
}
fclose($fh);
}
// Process a 1 GB CSV with constant memory
foreach (readCsv('big.csv') as [$id, $name, $email]) {
processRow($id, $name, $email);
}
// yield from — delegate to another generator
function merge(\Generator ...$gens): \Generator {
foreach ($gens as $gen) {
yield from $gen;
}
}
40. What are first-class callables (PHP 8.1)?
// Before PHP 8.1 — anonymous function wrapper
$fn = fn($x) => strlen($x);
$lengths = array_map(fn($s) => strlen($s), $strings);
// PHP 8.1 — first-class callable syntax
$fn = strlen(...);
$lengths = array_map(strlen(...), $strings);
// Works with methods too
$trimmed = array_map($str->trim(...), $strings);
$users = array_map(User::fromArray(...), $rows);
41. What are readonly classes (PHP 8.2)?
All properties of a readonly class are implicitly readonly:
readonly class Point {
public function __construct(
public float $x,
public float $y,
public float $z = 0.0,
) {}
}
$p = new Point(1.0, 2.0);
$p->x = 3.0; // Error: readonly
Ideal for value objects and DTOs.
42. What are PHP attributes (PHP 8.0)?
Attributes are structured metadata (replacing docblock annotations):
#[Attribute]
class Route {
public function __construct(
public string $path,
public string $method = 'GET',
) {}
}
class UserController {
#[Route('/users', 'GET')]
public function index(): array { ... }
#[Route('/users/{id}', 'GET')]
public function show(int $id): array { ... }
}
// Read attributes via reflection
$ref = new \ReflectionMethod(UserController::class, 'index');
$attrs = $ref->getAttributes(Route::class);
$route = $attrs[0]->newInstance();
echo $route->path; // '/users'
Miscellaneous
43. What is the difference between require, include, require_once, and include_once?
| On failure | Allows re-inclusion | |
|---|---|---|
include |
Warning (continues) | Yes |
require |
Fatal error (stops) | Yes |
include_once |
Warning (continues) | No |
require_once |
Fatal error (stops) | No |
Use require for files essential to the application, include for optional templates. In practice, Composer autoloading makes manual include/require rare.
44. What is output buffering?
Output buffering captures output instead of sending it immediately to the browser:
ob_start();
include 'template.php';
$html = ob_get_clean(); // Capture and stop buffering
// Useful for: capturing template output, gzip compression, headers after output
45. What does list() / [...] destructuring do?
// list() — positional destructuring
[$first, $second, $third] = [1, 2, 3];
// Skip elements
[, $second] = [1, 2, 3];
// Nested
[[$a, $b], [$c, $d]] = [[1, 2], [3, 4]];
// With string keys (PHP 7.1+)
['name' => $name, 'age' => $age] = ['name' => 'Alice', 'age' => 30];
// In foreach
$points = [[1, 2], [3, 4], [5, 6]];
foreach ($points as [$x, $y]) {
echo "{$x}, {$y}\n";
}
46. How does PHP handle references (&)?
$a = 1;
$b = &$a; // $b references the same memory as $a
$b = 2;
echo $a; // 2
// Function reference parameters
function increment(int &$value): void {
$value++;
}
$x = 5;
increment($x);
echo $x; // 6
// foreach by reference
$numbers = [1, 2, 3];
foreach ($numbers as &$num) {
$num *= 2;
}
unset($num); // IMPORTANT: unset reference after loop to avoid bugs
47. What is declare(strict_types=1) and where must it appear?
<?php
declare(strict_types=1); // Must be the FIRST statement in the file
function add(int $a, int $b): int {
return $a + $b;
}
add(1.5, 2); // TypeError: must be int, float given
Without strict_types, PHP coerces 1.5 → 1. This only affects the file containing the declaration (not called functions from other files).
48. What are the PHP superglobals?
| Variable | Content |
|---|---|
$_GET |
URL query parameters |
$_POST |
POST body (form data) |
$_COOKIE |
Cookies |
$_SESSION |
Session data |
$_SERVER |
Server and request info |
$_FILES |
Uploaded files |
$_REQUEST |
$_GET + $_POST + $_COOKIE (avoid) |
$_ENV |
Environment variables |
$GLOBALS |
All global variables |
Always filter/validate superglobal values:
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
http_response_code(400);
die('Invalid ID');
}
49. What are arrow functions?
Arrow functions (PHP 7.4+) are single-expression closures that capture outer variables automatically:
$multiplier = 3;
// Regular closure — must use `use`
$fn = function (int $x) use ($multiplier): int {
return $x * $multiplier;
};
// Arrow function — captures automatically
$fn = fn(int $x): int => $x * $multiplier;
// Nested arrow functions
$add = fn($a) => fn($b) => $a + $b;
$add5 = $add(5);
echo $add5(3); // 8
Limitation: single expression only (no multi-line body).
50. What are common PHP anti-patterns to avoid?
| Anti-pattern | Problem | Better approach |
|---|---|---|
mysql_* functions |
Removed in PHP 7 | Use PDO or mysqli |
@ error suppression |
Hides bugs, slows code | Use proper null checks |
global $var |
Hidden coupling | Dependency injection |
extract($_POST) |
Pollutes scope, security risk | Access keys explicitly |
Using == for security checks |
Type juggling bypasses | Use === or hash_equals() |
| Storing plaintext passwords | Critical security issue | password_hash() |
| Not escaping output | XSS vulnerability | htmlspecialchars() |
Selecting SELECT * |
Over-fetching, fragile | Select only needed columns |
Common mistakes
| Mistake | Correct approach |
|---|---|
echo vs print — thinking print is faster |
echo is marginally faster; use it |
Comparing with == null instead of === null |
Always use strict comparison |
Using count() in a loop condition |
Cache the count: $n = count($arr) |
array_push($arr, $val) for single element |
$arr[] = $val is faster |
| Not closing DB connections in long-running scripts | Use $pdo = null or explicit disconnect |
Using file_get_contents() for large files |
Use streaming (fopen, generators) |
Trusting $_SERVER['HTTP_HOST'] without validation |
Can be spoofed; whitelist expected hosts |
Not setting charset=utf8mb4 in PDO DSN |
Emoji and some CJK chars silently truncated |
PHP vs other languages
| Feature | PHP | Python | Node.js |
|---|---|---|---|
| Paradigm | Multi-paradigm | Multi-paradigm | Event-driven |
| Typing | Dynamic + optional strict | Dynamic + type hints | Dynamic |
| Concurrency | Multi-process (FPM) | GIL / asyncio | Single-thread async |
| Package manager | Composer | pip | npm |
| Template engine | Blade, Twig, native | Jinja2, native | Pug, Handlebars |
| Primary use | Web (dominant) | ML, data, web | Web, APIs, tooling |
FAQ
Q: What is the difference between echo and print?
echo outputs one or more strings, accepts multiple comma-separated arguments, and has no return value. print outputs a single string and returns 1. In practice always use echo.
Q: How does PHP's garbage collector work?
PHP uses reference counting as the primary mechanism. When a variable's reference count drops to zero, memory is freed immediately. A cyclic garbage collector (enabled by default since PHP 5.3) handles circular references using the tri-color mark-and-sweep algorithm. You can trigger it manually with gc_collect_cycles().
Q: What is the difference between static and self for properties?
self::$property always refers to the class where the code is written. static::$property uses late static binding — it resolves to the class that called the method at runtime, enabling polymorphic static behaviour.
Q: How does session management work in PHP?
session_start() creates or resumes a session. A session ID is stored in a cookie (PHPSESSID by default); session data is stored server-side (filesystem by default, configurable to Redis/DB). Regenerate the session ID after login with session_regenerate_id(true) to prevent session fixation attacks.
Q: What is the spaceship operator (<=>)?
Returns -1, 0, or 1 for less than, equal, or greater than. Useful for usort callbacks: usort($arr, fn($a, $b) => $a <=> $b).
Q: When should I use abstract class vs interface?
Use an interface when you want to define a contract (what a class can do) without any implementation — especially when multiple unrelated classes should share the contract. Use an abstract class when you want to share implementation code (concrete methods) among related classes that share an "is-a" relationship.