Top 100 PHP Interview Questions
Top 100 PHP interview questions covering core concepts, OOP, PHP 8+ features, security, databases, and modern PHP practices.
PHP (Hypertext Preprocessor) is a widely-used, open-source, server-side scripting language designed specifically for web development. It was created by Rasmus Lerdorf in 1994. PHP code is executed on the server, and the result is sent to the browser as plain HTML — the client never sees the PHP code. It is embedded directly into HTML using <?php ... ?> tags. PHP can connect to databases, handle forms, create sessions, read and write files, and interact with server-side resources. It powers a huge portion of the web, including WordPress, Wikipedia, and Facebook (which used PHP at its early stages and created Hack as a typed superset).
Both echo and print are used to output data in PHP, but they have key differences. echo is a language construct (not strictly a function), can accept multiple comma-separated arguments (echo "Hello", " World";), and has no return value — it is marginally faster. print also a language construct, always returns 1, making it usable in expressions: $x = print("Hello");. It accepts only one argument. In practice, echo is used far more commonly. Neither requires parentheses for a single argument, though you can use them for clarity.
PHP supports eight primitive data types. Scalar types: int (whole numbers), float (decimal numbers), string (sequence of characters), bool (true or false). Compound types: array (ordered map that can hold multiple values of any type), object (instance of a class). Special types: null (variable has no value), resource (reference to an external resource like a file handle or database connection). PHP is a loosely typed (dynamically typed) language — a variable can hold any type and can change type during execution, though PHP 7+ added strict type declarations.
In PHP, == is the loose comparison operator — it compares values after type juggling (type coercion). For example, "1" == 1 returns true because PHP converts the string "1" to the integer 1. === is the strict comparison operator — it compares both value AND type without any coercion. So "1" === 1 returns false because one is a string and the other is an integer. Always prefer === for comparisons to avoid subtle bugs from unexpected type coercion. This is especially important when comparing with false, 0, null, and "", which are all loosely equal to each other in PHP.
Superglobals are built-in PHP variables that are always accessible regardless of scope — they can be used in any function, class, or file without needing to declare them global. PHP has nine superglobals: $GLOBALS (all global variables), $_SERVER (server/execution environment info), $_GET (URL query string parameters), $_POST (HTTP POST form data), $_FILES (file upload data), $_COOKIE (HTTP cookies), $_SESSION (session variables), $_REQUEST (combination of GET, POST, and COOKIE), and $_ENV (environment variables). Always sanitize and validate data from superglobals before using it — they contain user-supplied input and are common vectors for injection attacks.
The GET method sends data appended to the URL as query parameters (?name=Alice&age=25), accessible via $_GET. GET requests are cached, stored in browser history, can be bookmarked, and have URL length limits (around 2048 characters). They should only be used for retrieving data (idempotent operations). The POST method sends data in the HTTP request body, accessible via $_POST. POST data is not visible in the URL, not cached, not stored in browser history, and has no practical size limit. Use POST for submitting forms with sensitive data (passwords), uploading files, or any operation that modifies server state (creating, updating, deleting records).
A session is a mechanism to store user-specific information on the server across multiple HTTP requests. Because HTTP is stateless, sessions provide continuity between page loads. Start a session with session_start() (must be called before any output). Store data: $_SESSION["user"] = "Alice";. Retrieve data on the next page: echo $_SESSION["user"];. PHP generates a unique session ID (usually stored in a cookie named PHPSESSID) that links the browser to the server-side session data. Destroy a session: session_destroy(). Sessions are more secure than cookies for storing sensitive data (like user IDs after login) because the data lives on the server, not in the browser.
A cookie is a small piece of data stored in the user's browser, sent to the server with every HTTP request. Set a cookie with setcookie(name, value, expiry, path, domain, secure, httponly) — this must be called before any output. Read cookies via $_COOKIE["name"]. Cookies persist on the browser until they expire or are deleted. Key security flags: HttpOnly (prevents JavaScript access, mitigating XSS), Secure (only sent over HTTPS), and SameSite (controls cross-site request behavior, mitigating CSRF). Unlike sessions (server-side), cookies store data client-side — do not store sensitive data (passwords, tokens) in cookies without encryption.
A PHP array is a versatile ordered map that can store multiple values of any type under named keys or auto-incremented integer indices. PHP supports three types: Indexed arrays with numeric keys ($fruits = ["apple", "banana", "cherry"]), Associative arrays with string keys ($person = ["name" => "Alice", "age" => 25]), and Multidimensional arrays (arrays containing other arrays). PHP arrays are dynamic — they can grow or shrink at runtime, and a single array can mix integer and string keys. Unlike many languages, PHP arrays are actually ordered hash maps, so they maintain insertion order.
Both include and require insert the contents of another PHP file into the current script at the point they are called. The key difference is error handling when the file is not found: include produces a warning (E_WARNING) and continues executing the script. require produces a fatal error (E_COMPILE_ERROR) and halts execution. Use require for files that are essential to the application (configuration, database connections), and include for optional parts. Their _once variants (include_once, require_once) ensure the file is included only once, preventing function redeclaration errors.
A function in PHP is a reusable block of code that performs a specific task. Define with the function keyword: function greet(string $name): string { return "Hello, $name!"; }. PHP functions support default parameter values, type declarations (PHP 7+), and return type declarations. PHP passes arguments by value by default — to pass by reference, prefix the parameter with &: function increment(int &$n): void { $n++; }. PHP also has variable functions (storing a function name in a variable and calling it), anonymous functions (closures), and arrow functions (PHP 7.4+). Functions are globally scoped by default in PHP.
In PHP, null represents the absence of a value — a variable that has been explicitly set to null, not yet assigned, or unset. false is a boolean value representing logical falsehood. Empty string ("") is a string with zero characters. While all three are "falsy" (evaluate to false in a boolean context), they are distinct values: is_null(null) is true, is_null("") is false. The empty() function returns true for all of them as well as for 0, "0", []. Always use strict comparisons (===) or type-specific functions (is_null(), is_bool()) to distinguish between them.
PHP provides a rich library of built-in string functions. Commonly used ones: strlen($str) (length), strtolower($str) / strtoupper($str) (case conversion), trim($str) (strip whitespace from both ends), str_replace($find, $replace, $str) (find and replace), strpos($str, $needle) (find position of first occurrence — returns false if not found), substr($str, $start, $length) (extract substring), explode($delimiter, $str) (split string into array), implode($glue, $array) (join array into string), sprintf($format, $args) (format a string), str_pad(), str_repeat(), and htmlspecialchars() (escape HTML entities for security).
PHP has an extensive set of built-in array functions. Essential ones: count($arr) (number of elements), array_push($arr, $val) / array_pop($arr) (add/remove from end), array_shift($arr) / array_unshift($arr, $val) (remove/add at start), in_array($val, $arr) (check if value exists), array_key_exists($key, $arr) (check if key exists), array_merge($a, $b) (merge arrays), array_slice($arr, $offset, $length) (extract portion), array_search($val, $arr) (returns key of value), sort() / rsort() / asort() / ksort() (sorting), array_unique($arr) (remove duplicates), array_flip($arr) (swap keys and values), and array_map() / array_filter() / array_reduce() (functional operations).
PHP supports variable interpolation inside double-quoted strings — variables and expressions are automatically replaced with their values. Simple variable: "Hello, $name!". For array values or object properties, use curly braces: "User: {$user["name"]}" or "Age: {$user->age}". Single-quoted strings do NOT interpolate variables — 'Hello, $name' outputs the literal string with the dollar sign. Using single quotes for strings with no variables is marginally faster and clearly signals no interpolation is expected. The heredoc syntax (<<<EOT) also supports interpolation for multi-line strings, while nowdoc (<<<'EOT') does not.
These are PHP type-conversion functions. intval($var) converts a variable to an integer — a string like "42abc" becomes 42, "abc" becomes 0, and a float like 4.9 becomes 4 (truncated toward zero). floatval($var) (also doubleval()) converts to a float. strval($var) converts to a string — true becomes "1", false becomes "", null becomes "". Alternatively, PHP supports direct casting: (int)$var, (float)$var, (string)$var, (bool)$var, (array)$var, (object)$var. The settype($var, "integer") function modifies the variable in place. Always validate and sanitize user input before type conversion rather than relying on implicit coercion.
PHP has comprehensive date/time functions. time() returns the current Unix timestamp (seconds since January 1, 1970 UTC). date($format, $timestamp) formats a timestamp — e.g., date("Y-m-d H:i:s") gives "2024-01-15 14:30:00". strtotime($str) converts a human-readable date string to a Unix timestamp. The modern, object-oriented approach uses the DateTime and DateTimeImmutable classes along with DateInterval and DateTimeZone — these handle timezone conversions, date arithmetic, and formatting more cleanly than procedural functions. Always store dates in UTC in the database and convert to user timezones only for display.
PHP provides two extensions for MySQL: MySQLi (MySQL Improved) and PDO (PHP Data Objects). MySQLi: $conn = new mysqli("host", "user", "pass", "dbname"); if ($conn->connect_error) { die("Connection failed"); }. It is MySQL-specific. PDO is the modern, recommended approach: $pdo = new PDO("mysql:host=localhost;dbname=mydb", "user", "pass", [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);. PDO supports 12+ database drivers (MySQL, PostgreSQL, SQLite, etc.), making it portable. Both support prepared statements for SQL injection prevention. The old mysql_* functions were removed in PHP 7 — never use them.
Prepared statements (parameterized queries) are a technique where the SQL query template is sent to the database separately from the data. The database compiles the query once, and the parameters are bound and sent separately — the database never interprets the parameters as SQL code. This is the primary defense against SQL injection attacks. PDO example: $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?"); $stmt->execute([$email]);. Named parameters: "WHERE email = :email" with ["email" => $email]. Prepared statements also improve performance when the same query is executed multiple times with different parameters, since the query is only compiled once.
Cross-Site Scripting (XSS) is a security vulnerability where an attacker injects malicious JavaScript into web pages viewed by other users. If you output user-supplied data directly to HTML without sanitization, an attacker could inject <script>stealCookies()</script>. Prevention in PHP: always escape output using htmlspecialchars($data, ENT_QUOTES, "UTF-8") before rendering user data in HTML — this converts <, >, ", ', and & into their HTML entity equivalents. Also use Content Security Policy (CSP) headers, set the HttpOnly flag on cookies, and validate/sanitize all input. Never trust user input.
SQL injection is an attack where an attacker manipulates a SQL query by injecting malicious SQL code through user input. Example: if you build a query like "SELECT * FROM users WHERE name = '" . $name . "'" and an attacker enters " OR '1'='1, they can bypass authentication. Prevention: always use prepared statements with PDO or MySQLi — they separate SQL code from data, making injection impossible. Additional measures: use a least-privilege database user, implement input validation, use an ORM (like Eloquent or Doctrine), and escape identifiers (table/column names) when they must be dynamic. Never concatenate user input directly into SQL strings.
PHP supports full object-oriented programming starting from PHP 5. OOP in PHP includes: Classes (blueprints with properties and methods), Objects (instances created with new), Encapsulation (access modifiers: public, protected, private), Inheritance (a class extends another using extends), Polymorphism (overriding methods in subclasses), Abstraction (abstract classes and interfaces), Traits (horizontal code reuse without inheritance). PHP supports single inheritance for classes but multiple interface implementation. OOP allows for better code organization, reusability, and maintainability compared to procedural PHP code.
A class in PHP is a blueprint that defines the structure and behavior of objects. Define a class with the class keyword, then instantiate it with new: class Car { public string $color; public function __construct(string $color) { $this->color = $color; } public function getColor(): string { return $this->color; } }. Create an instance: $car = new Car("red");. Access properties and methods with the arrow operator (->): $car->getColor(). Static members are accessed with the double-colon (::): Car::$count or Car::create(). PHP classes also support constructors, destructors, magic methods, and interfaces.
The __construct() method is PHP's constructor — it is automatically called when an object is created with new. It is used to initialize object properties and perform setup. PHP 8.0 introduced constructor property promotion, which dramatically reduces boilerplate: class User { public function __construct(private string $name, private int $age) {} } — declaring the parameter with an access modifier automatically creates and assigns the property. PHP only supports one constructor per class (no overloading), but you can use default parameter values to simulate multiple constructor signatures. The counterpart is __destruct(), called when the object is destroyed.
Magic methods are special methods that PHP calls automatically in response to certain actions. They all start with a double underscore (__). Key magic methods: __construct() (object creation), __destruct() (object destruction), __get($name) and __set($name, $value) (accessing/setting inaccessible properties), __isset($name), __unset($name), __call($name, $args) (calling inaccessible methods), __callStatic($name, $args) (calling inaccessible static methods), __toString() (convert object to string — called when object is used as string), __invoke() (called when object is used as a function), __clone() (called when object is cloned with clone), and __debugInfo() (controls var_dump output).
$this is a pseudo-variable available inside non-static class methods that refers to the current object instance being operated on. It is used to access instance properties and methods: $this->name accesses the name property, and $this->getName() calls the getName() method. $this is not available in static methods because static methods belong to the class, not an instance. Attempting to use $this in a static context produces a notice. The equivalent for referencing the class itself (including static properties and methods) is the self::, static::, and parent:: keywords.
PHP has three access modifiers for class properties and methods. public: accessible from anywhere — inside the class, subclasses, and external code. protected: accessible only within the class itself and by subclasses (child classes). Not accessible from outside the class hierarchy. private: accessible only within the class where it is defined. Subclasses cannot access private members. Best practice: make all properties private or protected and expose them through public getters/setters to maintain encapsulation. PHP 8.1 added readonly properties (can be set only once, in the constructor), and PHP 8.2 added asymmetric visibility modifiers.
Inheritance in PHP allows a class to inherit properties and methods from a parent class using the extends keyword: class Dog extends Animal { }. The child class gets all public and protected members of the parent and can override them. PHP supports only single inheritance — a class can extend only one parent class. Use parent:: to call the parent class method or constructor: parent::__construct($name);. You can prevent a class from being extended by declaring it final. You can also prevent a specific method from being overridden by declaring it final. Traits compensate for the single-inheritance limitation by enabling horizontal code reuse.
An interface in PHP defines a contract — a set of public method signatures that any implementing class must provide. Interfaces have no implementation code. Declare with interface and implement with implements: class Car implements Drivable, Serializable { }. A class can implement multiple interfaces (unlike single class inheritance). All methods in an interface are public and abstract by default. Since PHP 8.0, interfaces can include constants. Since PHP 8.1, interfaces can include enum cases as constants. Interfaces are ideal for defining shared contracts between unrelated classes, enabling polymorphism without forcing an inheritance relationship. Type-hint against interfaces for flexibility: function drive(Drivable $vehicle).
An abstract class is a class declared with the abstract keyword that cannot be instantiated directly — it must be subclassed. It can contain a mix of abstract methods (declared without implementation, which subclasses must provide) and concrete methods (with implementation, which subclasses inherit). abstract class Shape { abstract public function area(): float; public function describe(): string { return "I am a shape"; } }. Use abstract classes when you want to share code among closely related classes while enforcing a common interface. The key difference from interfaces: abstract classes can have instance variables, constructor logic, and concrete methods. A class can only extend one abstract class.
Namespaces solve the problem of name collisions between classes, functions, and constants in large projects or when using third-party libraries. Declare a namespace at the top of a file: namespace App\Controllers;. Access a class in another namespace with its fully qualified name: new \Vendor\Package\MyClass(), or import it with use Vendor\Package\MyClass;. You can alias imports: use Vendor\Package\MyClass as VendorClass;. PHP namespaces follow the folder structure convention (enforced by PSR-4 autoloading). All modern PHP frameworks and libraries use namespaces extensively, and Composer's autoloader maps namespaces to file paths automatically.
Composer is PHP's standard dependency manager. It allows you to declare the libraries your project depends on and manages installing and updating them. Create a composer.json file specifying dependencies: { "require": { "vendor/package": "^2.0" } }. Run composer install to download dependencies to the vendor/ folder. Composer also provides autoloading — just include vendor/autoload.php and all installed packages and your own classes (configured in the autoload section) are available without manual require statements. It uses the composer.lock file to lock dependency versions for reproducible builds. Packagist (packagist.org) is the main repository for Composer packages.
PSR (PHP Standards Recommendation) is a series of interoperability standards published by the PHP-FIG (Framework Interop Group) to help framework and library authors write compatible code. Key PSRs: PSR-1 (Basic Coding Standard — file encoding, class/method naming), PSR-2 / PSR-12 (Extended Coding Style — indentation, braces, spacing), PSR-4 (Autoloading Standard — namespace to directory mapping, used by Composer), PSR-3 (Logger Interface — defines the LoggerInterface), PSR-7 (HTTP Message Interface — request/response objects), and PSR-11 (Container Interface — dependency injection containers). Following PSR standards ensures your code integrates smoothly with modern PHP tools and frameworks.
Both array() and [] create arrays in PHP — they are functionally identical. The [] short syntax was introduced in PHP 5.4 and is now universally preferred because it is more concise and reads like array literals in other languages. $fruits = ["apple", "banana"] is exactly equivalent to $fruits = array("apple", "banana"). The short syntax also works for destructuring assignments (PHP 7.1+): [$first, $second] = $array; as opposed to list($first, $second) = $array;. Modern PHP code almost exclusively uses the short [] syntax.
Type hinting (formally called type declarations in PHP 7+) allows you to specify the expected data type of function parameters, return values, and class properties. PHP 7 added scalar type hints: function add(int $a, int $b): int { return $a + $b; }. By default, PHP coerces values to the declared type (weak mode). Strict mode (enforces exact types with a fatal error for mismatches) is enabled with declare(strict_types=1); at the top of the file. PHP 8 added union types (int|string), nullable types (?int), the mixed, never, and void types, and PHP 8.1 added intersection types (Iterator&Countable). Type declarations make code self-documenting and catch type-related bugs early.
The null coalescing operator (??), introduced in PHP 7, returns the left operand if it exists and is not null, otherwise returns the right operand. It is equivalent to isset($var) ? $var : $default but much more concise. Example: $username = $_GET["user"] ?? "Guest";. It can be chained: $val = $a ?? $b ?? $c ?? "default"; — returns the first non-null value. PHP 7.4 added the null coalescing assignment operator (??=): $data["key"] ??= "default"; assigns the right side only if the left side is null or does not exist. This operator is very common in PHP for handling missing GET/POST parameters and optional array keys.
The spaceship operator (<=>), introduced in PHP 7, is a three-way comparison operator that returns -1, 0, or 1 — negative if the left side is less than the right, 0 if equal, positive if greater. It works on integers, floats, strings, arrays, and objects. It is primarily used in sorting callbacks: usort($people, fn($a, $b) => $a["age"] <=> $b["age"]); — this concisely replaces the old pattern of comparing values and returning -1, 0, or 1 manually. For descending sort, swap the operands: $b["age"] <=> $a["age"]. The name "spaceship" comes from the resemblance of <=> to a UFO.
A closure (anonymous function) in PHP is a function without a name that can be assigned to a variable or passed as an argument. $multiply = function(int $a, int $b): int { return $a * $b; };. Closures can capture variables from the outer scope using the use keyword: function($x) use ($multiplier) { return $x * $multiplier; }. To capture by reference: use (&$counter). Closures are objects of the Closure class and have methods like bind() and bindTo(). PHP 7.4 introduced arrow functions (fn($x) => $x * 2) as a short syntax that automatically captures variables from the outer scope without needing use.
PHP 7 (released December 2015) was a landmark release that doubled performance over PHP 5 through a new Zend Engine 3 with the PHPNG (PHP Next Generation) optimizations. Key new features: scalar type declarations (int, float, string, bool for parameters and returns), return type declarations, null coalescing operator (??), spaceship operator (<=>), anonymous classes, group use declarations, integer division function (intdiv()), uniform variable syntax, and throwable interface (allowing both Errors and Exceptions to be caught with a single catch). It also removed deprecated features like the mysql_* extension. PHP 7 made PHP a genuinely competitive language for high-performance applications.
PHP 8.0 (released November 2020) introduced significant language improvements. Key features: JIT (Just-In-Time) Compiler for significant performance gains in CPU-intensive code, named arguments (pass arguments by parameter name in any order), attributes (structured metadata for classes, methods, properties — the native replacement for docblock annotations), match expression (strict, exhaustive version of switch), nullsafe operator (?->), constructor property promotion, union types (int|string), throw as an expression, str_contains() / str_starts_with() / str_ends_with() functions, and the Fibers feature (PHP 8.1). PHP 8 fundamentally modernized the language and reduced boilerplate significantly.
The match expression (PHP 8.0) is a more powerful and strict replacement for the switch statement. Key differences from switch: match uses strict comparison (===) instead of loose comparison (==), each branch returns a value (it is an expression), there is no fall-through between branches, and it throws an UnhandledMatchError if no branch matches (instead of silently doing nothing). Example: $result = match($status) { 1 => "active", 2 => "inactive", default => "unknown" };. Multiple conditions per branch: 1, 2 => "low value". Match makes code safer and more concise than switch.
Named arguments (PHP 8.0) allow you to pass arguments to a function by specifying the parameter name, regardless of their position in the signature. Example: array_slice(array: $arr, offset: 1, length: 3, preserve_keys: true);. Benefits: you can skip optional parameters you do not need (no need to pass null placeholders), the code is self-documenting (argument purpose is explicit), and argument order does not matter. Named arguments also work with built-in PHP functions. They are particularly useful for functions with many optional parameters — you only name the ones you want to set, and the rest use their defaults. Named arguments cannot be combined with positional arguments in ambiguous ways.
A trait is a mechanism for code reuse in single-inheritance languages like PHP. Traits allow you to include methods (and abstract methods) in multiple classes without inheritance. Declare with trait Loggable { public function log(): void { ... } } and use with use Loggable inside a class. A class can use multiple traits. Traits can define abstract methods (forcing the using class to implement them), properties, and even use other traits. Conflict resolution: if two traits define the same method, you must resolve the conflict explicitly with the insteadof and as operators. Traits are "copied-and-pasted" into the class at compile time — they are not independently instantiatable.
The nullsafe operator (?->), introduced in PHP 8.0, allows you to safely chain method calls and property accesses on potentially null values. If any value in the chain is null, the entire expression short-circuits and returns null instead of throwing an error. Before PHP 8: $city = $user ? ($user->getAddress() ? $user->getAddress()->getCity() : null) : null;. With nullsafe operator: $city = $user?->getAddress()?->getCity();. It is conceptually similar to Kotlin's safe call operator (?.). The nullsafe operator can be combined with the null coalescing operator: $user?->getProfile()?->bio ?? "No bio";.
Enums (PHP 8.1) are a first-class way to define a type that has a fixed set of possible values. A pure enum has only case names: enum Status { case Active; case Inactive; case Pending; }. A backed enum associates each case with a scalar value: enum Color: string { case Red = "red"; case Blue = "blue"; }. Access cases as singletons: Status::Active. Backed enums can be created from a value: Color::from("red") (throws on invalid) or Color::tryFrom("red") (returns null). Enums can implement interfaces, have methods, and use constants. They replace the common pattern of class constants for representing finite sets of values.
Readonly properties (PHP 8.1) can only be initialized once — typically in the constructor — and cannot be modified after that. Attempting to write to a readonly property after initialization throws an Error. class User { public function __construct(public readonly string $name, public readonly int $id) {} }. Combined with constructor promotion, this creates immutable value objects with minimal boilerplate. PHP 8.2 added readonly classes — marking the class as readonly makes all its promoted and explicitly declared properties readonly automatically. Readonly properties are particularly useful for DTOs (Data Transfer Objects), value objects, and event objects where immutability is a design requirement.
Fibers (PHP 8.1) are units of concurrency that allow functions to be paused and resumed. They are PHP's implementation of coroutines at the language level. A Fiber can be paused mid-execution with Fiber::suspend($value) and resumed with $fiber->resume($value). Unlike threads, Fibers are cooperative — they yield control explicitly and run in a single thread. They enable non-blocking I/O patterns and are the foundation for asynchronous PHP frameworks like ReactPHP and Amp (v3+). Example: $fiber = new Fiber(function(): void { $value = Fiber::suspend("paused"); }); $result = $fiber->start(); $fiber->resume("hello");. Fibers are lower-level than async/await but enable async libraries to be built on top of them.
The static keyword in PHP has multiple uses. Static class members (properties and methods) belong to the class rather than an instance — access with ClassName::$property or ClassName::method(). Static variables inside functions retain their value between function calls: function counter() { static $count = 0; return ++$count; }. Late static binding (using static:: instead of self::) refers to the class that was actually called at runtime, not where the method was defined — crucial for inheritance scenarios where you want to reference the child class from a parent method. self:: always refers to the class where the code is written; static:: refers to the runtime class.
Late static binding (LSB), enabled by static::, allows a method defined in a parent class to reference the class it is called on at runtime, not the class where the method is defined. self:: always resolves to the class in which the method is written — even if a child class overrides the property. static:: resolves to the class that was actually called. Example: if ParentClass has a static factory method using return new static();, calling ChildClass::create() returns a ChildClass instance. Using return new self(); would always return a ParentClass instance. LSB is essential for building fluent query builders, active record patterns, and other class-hierarchy-aware patterns.
PHP has two error systems. The traditional PHP error system generates errors, warnings, and notices (E_ERROR, E_WARNING, E_NOTICE) controlled by error_reporting() and handled by a custom error handler set with set_error_handler(). The modern exception handling system uses try-catch-finally blocks, just like Java. Throw: throw new InvalidArgumentException("Invalid input");. Catch: catch (InvalidArgumentException $e) { log($e->getMessage()); }. PHP 7+ unified them: Error (PHP fatal errors) and Exception both implement the Throwable interface, so you can catch both with catch (Throwable $t). Use set_exception_handler() for a global fallback exception handler.
PDO (PHP Data Objects) is a database abstraction layer that provides a consistent interface for accessing different database systems (MySQL, PostgreSQL, SQLite, MSSQL, etc.). By changing the DSN (Data Source Name), you can switch databases with minimal code changes. Key features: prepared statements for SQL injection prevention, transactions (beginTransaction(), commit(), rollBack()), multiple fetch modes (PDO::FETCH_ASSOC, PDO::FETCH_OBJ, PDO::FETCH_CLASS), and error handling via exceptions (PDO::ERRMODE_EXCEPTION). Always set error mode to ERRMODE_EXCEPTION: $pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);.
These are PHP's core higher-order array functions. array_map($callback, $array) applies a callback to each element and returns a new array of the same size with the transformed values. array_filter($array, $callback) passes each element through a callback and returns a new array with only elements for which the callback returns true (preserves keys). Without a callback, it removes falsy values. array_reduce($array, $callback, $initial) reduces the array to a single value by passing an accumulator and the current element to the callback — useful for sums, building strings, or creating new data structures from an array. All three are non-destructive — they return new arrays/values without modifying the original.
Dependency Injection (DI) is a design pattern where dependencies (objects a class needs) are provided from outside rather than created internally. Instead of class UserService { public function __construct() { $this->db = new Database(); } }, you inject it: class UserService { public function __construct(private Database $db) {} }. Benefits: loosely coupled code, easily testable (inject mock dependencies in tests), and flexible (swap implementations by injecting different objects). DI Containers (like PHP-DI, Symfony's DI Container, Laravel's IoC Container) automate the wiring of dependencies throughout an application using reflection and configuration. DI is the foundation of modern PHP frameworks.
The Iterator interface allows objects to be iterated with a foreach loop just like arrays. Implement five methods: current() (return current element), key() (return current key), next() (move to next element), rewind() (reset to beginning), valid() (check if current position is valid). This allows custom data structures to be traversed naturally. The IteratorAggregate interface provides a simpler alternative — implement only getIterator() which returns an Iterator or array. PHP's Generators (using yield) provide an easy way to create lazy iterators without implementing the full Iterator interface.
Generators (PHP 5.5+) provide a simple, memory-efficient way to implement iterators without needing to build a full Iterator class. A generator function uses the yield keyword to produce values one at a time, pausing execution between each yield. Unlike returning an array (which builds everything in memory), a generator produces each value on demand: function fibonacci() { [$a, $b] = [0, 1]; while(true) { yield $a; [$a, $b] = [$b, $a + $b]; } }. This enables infinite sequences with no memory issues. yield from delegates to another generator. Generators return a Generator object, which implements the Iterator interface, so they work directly in foreach loops.
Autoloading automatically loads class files when they are first used, eliminating the need for manual require or include statements. Register a custom autoloader with spl_autoload_register(function($class) { include str_replace("\\", "/", $class) . ".php"; });. In modern PHP, Composer's autoloader handles this automatically based on PSR-4 configuration in composer.json. PSR-4 maps namespaces to directories: "autoload": { "psr-4": { "App\\": "src/" } }. After running composer dump-autoload, including vendor/autoload.php makes all your classes available without any manual includes. This is how all modern PHP frameworks and packages work.
Cross-Site Request Forgery (CSRF) tricks an authenticated user's browser into making an unwanted request to a web application (like transferring money or deleting account) by exploiting the fact that the browser automatically sends cookies with requests. Prevention: generate a unique, unpredictable CSRF token per user session and include it in every form as a hidden field: <input type="hidden" name="_token" value="<?= $_SESSION["csrf_token"] ?>">. On form submission, verify the token matches: hash_equals($_SESSION["csrf_token"], $_POST["_token"]). Use hash_equals() (not ==) to prevent timing attacks. Also: set the SameSite cookie attribute (Strict or Lax) for additional protection. Laravel and Symfony do this automatically.
Both include_once and require_once include a file only once — if the file has already been included, PHP skips including it again (preventing function/class redeclaration errors). The only difference is what happens when the file cannot be found: include_once generates a warning (E_WARNING) and continues script execution. require_once generates a fatal error (E_COMPILE_ERROR) and halts execution. Use require_once for files that the application cannot function without (autoloaders, configuration files, base classes). In modern PHP with Composer autoloading, you rarely need to call these manually — the autoloader handles file inclusion automatically.
File uploads in PHP use an HTML form with enctype="multipart/form-data" and method="POST". The uploaded file data is available in $_FILES["fieldname"] which contains: name (original filename), type (MIME type — do not trust this), tmp_name (temp path on server), error (UPLOAD_ERR_OK if successful), and size (file size in bytes). After validating, move the file with move_uploaded_file($_FILES["file"]["tmp_name"], $destination). Security: validate the actual MIME type using finfo_file(), check file extension against an allowlist, limit file size, use a non-guessable filename, store files outside the webroot if possible, and never trust the client-provided filename or MIME type.
Output buffering tells PHP to store all output (HTML, echo statements) in a buffer rather than sending it directly to the browser. Start buffering with ob_start(), retrieve the buffer as a string with ob_get_clean() or ob_get_contents(), and send/discard with ob_end_flush() or ob_end_clean(). Key use cases: modifying output before sending (adding headers, compressing), capturing the output of functions that echo instead of return, using header() or setcookie() after some output has been generated (normally these require being before any output), and template systems that capture rendered HTML. Many PHP frameworks use output buffering internally for response handling.
The Standard PHP Library (SPL) is a collection of built-in interfaces and classes for common data structures, iterators, and utilities. Data structures: SplStack, SplQueue, SplDoublyLinkedList, SplPriorityQueue, SplMinHeap, SplMaxHeap, SplFixedArray (memory-efficient fixed-size array). Iterators: DirectoryIterator, RecursiveDirectoryIterator, FilesystemIterator, ArrayIterator, LimitIterator. Exceptions: RuntimeException, InvalidArgumentException, LogicException. Patterns: Countable, Serializable, ArrayAccess (use object like an array), Stringable. SPL is part of PHP core and requires no installation.
The ArrayAccess interface allows objects to be accessed using array notation ($object["key"]). Implement four methods: offsetExists($offset) (called by isset($obj[$key])), offsetGet($offset) (called by $obj[$key]), offsetSet($offset, $value) (called by $obj[$key] = $val), and offsetUnset($offset) (called by unset($obj[$key])). This is used by Laravel's Collections, Symfony's ParameterBag, and many ORMs so their objects behave like arrays. It enables a uniform interface where consuming code does not need to know if it is working with a plain array or a complex object — both support the same [] access syntax.
Guzzle is the most popular PHP HTTP client library, installed via Composer. It provides a clean API for making HTTP requests to external APIs and services. Basic GET request: $client = new \GuzzleHttp\Client(); $response = $client->get("https://api.example.com/users");. POST with JSON body: $client->post($url, ["json" => $data]);. It supports: async requests ($client->getAsync() returns a Promise), middleware (for logging, retrying, authentication), request options (headers, timeout, authentication), and PSR-7 request/response objects. Guzzle is used internally by most PHP SDK libraries (AWS SDK, Stripe PHP SDK, etc.) and is a standard tool for any PHP application that makes external HTTP calls.
Symfony is a set of reusable PHP components and a full-stack web framework. It is the foundation for many other PHP projects (Drupal, API Platform, Magento uses some components). Known for its flexibility, strict standards, and use in enterprise applications. Laravel is the most popular PHP framework, built on top of Symfony components but with a different philosophy — it prioritizes developer experience, expressiveness, and convention over configuration. Laravel includes Eloquent ORM, Blade templating, Artisan CLI, queues, broadcasting, and many first-party packages. While Symfony gives you more control and is preferred for complex enterprise projects, Laravel is faster to build with and is the go-to choice for most new PHP web applications.
An ORM (Object-Relational Mapper) maps database tables to PHP classes (models) and rows to objects, allowing you to interact with the database using PHP objects instead of writing raw SQL. Eloquent is Laravel's ORM. Define a model: class User extends Model { } — Eloquent automatically maps it to the users table. CRUD operations: User::find(1), User::create(["name" => "Alice"]), $user->update(["name" => "Bob"]), $user->delete(). Relationships are defined as methods: hasMany(), belongsTo(), belongsToMany(), hasOne(). Eloquent's fluent query builder: User::where("active", true)->orderBy("name")->paginate(20).
Adding declare(strict_types=1); at the very top of a PHP file (before any other code) enables strict type mode for that file. In strict mode, PHP enforces type declarations strictly — passing a string where an int is declared throws a TypeError instead of coercing the type. Example: with strict types, calling add("5", 3) on a function declared as function add(int $a, int $b) throws an error. Without strict types, PHP would quietly convert "5" to 5. Strict types apply per-file — other files calling your functions still use coercive mode unless they also declare strict types. This is highly recommended for all new code to catch type errors at development time rather than having silent coercions cause bugs in production.
Both are PHP extensions for database interaction, but they differ in scope and philosophy. PDO (PHP Data Objects) is a database abstraction layer supporting 12+ database drivers — switching from MySQL to PostgreSQL requires only changing the DSN connection string. It uses a unified API regardless of the underlying database. MySQLi (MySQL Improved) is MySQL-specific but provides both an object-oriented and a procedural interface. MySQLi supports all MySQL-specific features (async queries, multi-statements) that PDO might not expose. Both support prepared statements and transactions. The recommendation is always use PDO for new projects — the database portability it provides is invaluable, and its API is cleaner for building abstractions on top of. Use MySQLi only if you need MySQL-specific features PDO does not expose.
A Service Container (also called an IoC Container or DI Container) is an object that manages class instantiation and dependency resolution automatically. You register services (bindings) into the container: $container->bind(LoggerInterface::class, FileLogger::class). When you request a class, the container resolves all its constructor dependencies recursively using Reflection. Laravel's container (app()) is one of the most powerful — it supports automatic injection, contextual bindings, singletons (singleton()), and method injection. The container makes dependency injection practical at scale — instead of manually wiring hundreds of classes, the container does it automatically. This is the core of how modern PHP frameworks work internally.
Redis is an in-memory data structure store used as a cache, message broker, and session store. In PHP applications, it is commonly used for: caching (storing frequently accessed database results), session storage (faster and more scalable than file-based sessions), queue backends (Laravel queues with the Redis driver), rate limiting, and pub/sub messaging. Connect using the predis/predis Composer package or the phpredis C extension (faster). In Laravel, configure Redis in config/database.php and use it with Cache::remember(), Session::driver("redis"), or the Queue system. Redis is significantly faster than database-based caching because all data lives in RAM.
Arrow functions (PHP 7.4+), declared with fn($x) => $x * 2, are a concise syntax for closures with one key difference: they automatically capture variables from the outer scope without needing the use keyword. A regular closure requires explicit capture: function($x) use ($multiplier) { return $x * $multiplier; }. The equivalent arrow function: fn($x) => $x * $multiplier — $multiplier is captured automatically. Arrow functions are limited to a single expression (the body is implicitly returned). They cannot modify outer variables (capture is by value only, not by reference). They are ideal for short callback functions in array operations: array_map(fn($n) => $n * 2, $numbers).
The Countable interface allows objects to be used with PHP's built-in count() function. Implement the single required method: public function count(): int — this returns the count when count($yourObject) is called. Example: a custom collection class that wraps an array and implements Countable so that count($collection) works naturally. Laravel's Collection class implements Countable along with Iterator and ArrayAccess, making it behave like a native PHP array in most contexts. Combined with the Stringable interface (implementing __toString(), formalized in PHP 8.0), these interfaces let custom objects blend seamlessly with built-in PHP functions and language constructs.
Sessions are a common attack vector. Best practices: Regenerate session ID after login (session_regenerate_id(true)) to prevent session fixation attacks. Use HTTPS and set session.cookie_secure = true so the session cookie is only sent over encrypted connections. Set session.cookie_httponly = true to prevent JavaScript from accessing the session cookie (XSS mitigation). Set session.cookie_samesite = "Lax" or "Strict" for CSRF mitigation. Set a reasonable timeout: track the last activity time in the session and invalidate stale sessions. Do not store sensitive data (credit card numbers, passwords) in sessions. Use Redis or Memcached for session storage in production instead of files (faster, easier to clear on logout). Validate session data server-side — never trust it blindly.
Both abstract methods (in abstract classes) and interface methods define a contract that implementing/extending classes must fulfill, but they exist in different contexts. Abstract methods are declared in an abstract class with the abstract keyword and can be public or protected. The abstract class may also contain concrete methods, state (instance variables), and a constructor. Interface methods are implicitly public and abstract — they define what must be done without any implementation context. A class can only extend one abstract class (single inheritance) but implement multiple interfaces. Use abstract classes when subclasses share state or partial implementation; use interfaces to define capabilities for unrelated classes. Since PHP 8.0, interfaces can also have constants.
The Adapter pattern allows incompatible interfaces to work together by wrapping one class with another that translates calls from the expected interface to the wrapped class's interface. It is a structural pattern that acts as a bridge between two incompatible interfaces. Example: you have third-party payment library StripePayment with a charge() method, but your application expects a PaymentGateway interface with a process() method. Create StripeAdapter implements PaymentGateway that wraps StripePayment and delegates process() to charge(). Now your application works with any gateway as long as you have an adapter. This is how PSR-7 adapters allow different frameworks to work with the same HTTP message format, and how logging adapters (Monolog) provide PSR-3 compliance for various logging backends.
The Facade pattern provides a simplified, static-like interface to complex subsystems. In Laravel, facades are proxy classes that forward static method calls to underlying objects resolved from the service container. Example: Cache::get("key") appears to be a static call but actually resolves the CacheManager from the container and calls get() on it. Each facade defines a getFacadeAccessor() method returning the container binding key. Benefits: concise, readable syntax without dependency injection boilerplate for common operations. Drawbacks: can make dependencies implicit (harder to see what a class depends on) and can make unit testing harder (use Cache::shouldReceive() in tests). Real facades differ from static methods — the underlying object is injected and testable.
Method chaining is the technique of calling multiple methods in sequence on an object: $result->method1()->method2()->method3(). This works when each method returns $this (the same object) or a new object of the same type. A fluent interface is a broader design pattern built on method chaining that creates a readable, DSL-like API. PHP examples: Laravel's Query Builder (DB::table("users")->where("active", 1)->orderBy("name")->get()), Guzzle's request builder, and PHPUnit's assertion API. To implement, return $this from methods that configure the object: public function where(string $col, $val): static { $this->wheres[] = [$col, $val]; return $this; }. PHP 8 allows : static as return type for covariant return type support in subclasses.
The JIT (Just-In-Time) compiler, introduced in PHP 8.0, compiles parts of PHP code to native machine code at runtime, bypassing the bytecode interpretation step for hot code paths. Before JIT, PHP executed by having the Zend Engine interpret opcodes (precompiled bytecode cached by OPcache). JIT sits on top of OPcache and further compiles frequently-executed opcodes to machine code. JIT provides the most significant speedup for CPU-intensive tasks (math operations, algorithms) but provides minimal improvement for typical web requests (which are I/O-bound). Enable in php.ini: opcache.jit=tracing and opcache.jit_buffer_size=100M. For web applications, the practical improvement is typically 5-15%; for scientific computation, it can be much higher.
OPcache is a PHP bytecode caching extension bundled with PHP since 5.5. Normally, PHP parses and compiles every source file into opcodes (bytecode) on every request. OPcache stores compiled opcodes in shared memory, so subsequent requests reuse the precompiled bytecode, skipping the parse and compile steps. This significantly reduces CPU usage and request response times — typically 50-70% performance improvement for web applications. Enable in php.ini: opcache.enable=1. Key settings: opcache.memory_consumption (amount of shared memory), opcache.max_accelerated_files (how many files to cache), opcache.revalidate_freq (how often to check if source changed). In production, set opcache.validate_timestamps=0 to disable file change checks entirely for maximum performance.
PHP-FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation designed for high-traffic sites. Instead of Apache's mod_php (which embeds PHP into every Apache process), PHP-FPM runs PHP as a separate service managing a pool of PHP worker processes. Nginx or Apache proxies PHP requests to PHP-FPM via the FastCGI protocol. Benefits: independent process management (PHP workers can be restarted without touching the web server), configurable pool sizes (per-site pools with different user permissions), graceful worker recycling (preventing memory leaks), slow request logging, and emergency restart on memory overflow. PHP-FPM with Nginx is the standard production PHP stack today, replacing the older Apache + mod_php setup.
PHP's Reflection API allows introspection of classes, interfaces, functions, methods, and properties at runtime. ReflectionClass, ReflectionMethod, ReflectionProperty, and ReflectionFunction are the main classes. Example: $ref = new ReflectionClass($object); $methods = $ref->getMethods(); $props = $ref->getProperties();. Reflection enables reading and instantiating classes from string names, accessing private members (for testing), reading method parameter types and defaults, and checking for attributes (PHP 8) or docblock annotations (older approach). Dependency injection containers use Reflection extensively to automatically resolve constructor dependencies. Reflection is powerful but has a performance cost — use it in bootstrapping/compilation phases rather than hot request paths.
Attributes (PHP 8.0) are structured, machine-readable metadata added to classes, methods, properties, function parameters, and constants using the #[AttributeName] syntax. They replace the docblock annotation workaround (e.g., /** @Route("/home") */) with native, type-safe metadata. Define a custom attribute: #[Attribute] class Route { public function __construct(public string $path) {} }. Apply it: #[Route("/home")] function index() {}. Read via Reflection: $method->getAttributes(Route::class)[0]->newInstance(). Symfony, Doctrine, and modern PHP frameworks use attributes for routing (#[Route]), entity mapping (#[ORM\Entity]), validation (#[Assert\NotBlank]), and DI configuration (#[Autowire]).
PHP uses reference counting as its primary memory management strategy — every variable has a reference count, and when it drops to zero, the memory is immediately freed. This works well for simple cases but fails with circular references (object A references B, B references A — both have refcount > 0 even when unreachable). To handle this, PHP 5.3+ added a cycle collector that periodically identifies circular reference cycles and frees them. The cycle collector runs when the root buffer exceeds 10,000 possible roots (gc_collect_cycles() can trigger it manually). PHP 8.0 improved GC with better detection. Set gc_enable() / gc_disable() to control it. For long-running scripts (daemons, queue workers), GC management is especially important to prevent memory exhaustion.
PHP allocates memory from the OS using its own memory manager, which maintains pools of pre-allocated blocks for efficient allocation. The default memory limit is 128MB (memory_limit in php.ini). Check current usage: memory_get_usage(). Optimization techniques: unset variables when done (unset($largeArray)), use generators instead of building large arrays in memory, process large datasets in chunks rather than loading everything, use streams for large file processing, avoid keeping database result sets in memory (use cursors), enable OPcache to reduce repeated parsing overhead. For CLI scripts processing millions of records, consider disabling the query log in ORM and batching operations. Memory profiling tools: Xdebug memory profiler, Blackfire.io.
The Decorator pattern dynamically adds behavior to objects by wrapping them in decorator classes that implement the same interface. The decorator delegates the core work to the wrapped object and adds its own behavior before or after. PHP example: a Logger interface, a FileLogger implementation, and a TimestampLogger decorator that wraps any Logger and prepends timestamps. $logger = new TimestampLogger(new FileLogger("app.log"));. Multiple decorators can be chained: new JsonLogger(new FilterLogger(new FileLogger($path))). PHP's stream wrappers, middleware stacks in frameworks (PSR-15), and pipeline patterns are all implementations of the Decorator principle. It follows Open/Closed principle — adding behavior without modifying existing classes.
The Repository pattern abstracts the data access layer, providing a collection-like interface for accessing domain objects. Business logic interacts with a repository interface, not directly with the database or ORM. Example: interface UserRepository { public function findById(int $id): User; public function findByEmail(string $email): ?User; public function save(User $user): void; }. Implement for Eloquent, Doctrine, or a plain array (for testing). Benefits: testability (inject a mock repository in unit tests), database independence (swap Eloquent for Doctrine without changing business logic), and separation of concerns (query logic lives in one place). In Laravel, repositories are often used as wrappers around Eloquent models to avoid coupling domain logic to the ORM.
SOLID is a set of five OOP design principles. S — Single Responsibility: a class should have one reason to change (one job). O — Open/Closed: open for extension, closed for modification — use interfaces and abstractions to add behavior without editing existing code. L — Liskov Substitution: subclasses must be substitutable for their parent classes without changing the correctness of the program — honor the parent's contract. I — Interface Segregation: prefer small, specific interfaces over large, general ones — clients should not be forced to implement methods they do not use. D — Dependency Inversion: high-level modules should not depend on low-level modules — both should depend on abstractions (interfaces). Following SOLID produces loosely coupled, easily testable, and maintainable PHP code.
Middleware is a pipeline of components that process HTTP requests and responses. Each middleware wraps the next one in the pipeline — it can inspect/modify the request before passing it forward, or inspect/modify the response on the way back. The PSR-15 standard defines: MiddlewareInterface (with process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface) and RequestHandlerInterface. Common middleware uses: authentication checks, rate limiting, CORS headers, request logging, body parsing, CSRF verification, cache headers. In Laravel, global middleware in app/Http/Middleware wraps every request. Route middleware can be applied selectively: Route::middleware(["auth", "throttle:60,1"])->group().
Event-driven programming decouples the code that detects something happening (event dispatcher) from the code that handles it (event listeners). In PHP, PSR-14 defines the standard EventDispatcherInterface. In Laravel: fire an event with event(new UserRegistered($user)) and listen with class SendWelcomeEmail { public function handle(UserRegistered $event) { ... } }. Symfony's EventDispatcher is the reference implementation. Benefits: open for extension (add listeners without touching the dispatching code), decoupled code (listener does not know about the dispatcher's context), and multiple listeners can react to the same event. Long-running PHP processes use event loops (ReactPHP, Amp) to handle I/O events asynchronously.
CQRS is an architectural pattern that separates read operations (Queries) from write operations (Commands). A Command changes state and returns nothing (or just an ID). A Query returns data and changes nothing. This separation allows you to optimize reads and writes independently — for example, using Elasticsearch for complex reads while MySQL handles writes. In PHP, implement with a command bus: $commandBus->dispatch(new CreateUserCommand($dto)). Libraries: Broadway, Tactician, Laravel's command bus. CQRS pairs well with Event Sourcing (storing state as a sequence of events instead of current state). It adds complexity and is best suited for domains with complex business logic, high read/write ratio differences, or scalability requirements.
Traditional PHP is synchronous and blocking — each request runs in its own process, sequential I/O blocks execution. ReactPHP and Amp are event loop libraries that enable non-blocking I/O in PHP, similar to Node.js. An event loop watches for I/O events (socket ready, timer fired) and dispatches callbacks. ReactPHP uses Promises for async operations. Amp (v3) uses PHP 8.1 Fibers, allowing async code to be written in a synchronous style with $value = await($promise);. Use cases: HTTP servers handling thousands of concurrent connections, WebSocket servers, background job processors, and any I/O-heavy long-running PHP process where spawning a new process per connection is too expensive. Traditional PHP (FPM) is still better for most web apps due to simpler deployment.
Domain-Driven Design (DDD) is an approach to software development focused on modeling the core business domain in code. Key concepts: Entities (objects with identity that persists over time — e.g., User with ID), Value Objects (immutable objects defined by their value, not identity — e.g., Money(100, "USD")), Aggregates (cluster of objects treated as one unit with a root entity), Domain Events (something significant that happened in the domain), Repositories (abstract persistence per aggregate), Services (operations that do not belong to entities), and Bounded Contexts (explicit boundaries where a model applies). PHP frameworks like Symfony are commonly used for DDD because of their flexibility, while Laravel can also accommodate DDD with proper structuring.
Intersection types (PHP 8.1) allow declaring that a value must satisfy multiple type constraints simultaneously, using the & operator. Example: function process(Iterator&Countable $collection): void — the argument must implement both Iterator AND Countable. This is the opposite of union types (|, introduced in PHP 8.0), which accept any one of the specified types. Intersection types can only be used with class and interface names — not with primitive types like int, string, or null. They improve type safety for code that requires objects with multiple capabilities, eliminating the need to create a combined interface just to express the type constraint. Pure intersection types (A&B) cannot be mixed with union types yet (that is a future proposal).
Profiling measures where your PHP application spends time and memory. Xdebug is the most popular PHP debugging and profiling extension. For profiling, enable xdebug.mode=profile — it generates cachegrind files that can be analyzed with KCacheGrind or Webgrind to see a call tree with time spent in each function. Blackfire.io is the production-grade profiler — it has low overhead, integrates with CI/CD, tracks performance over time, and provides recommendations. For simple timing: microtime(true). For memory: memory_get_peak_usage(). Common bottlenecks: N+1 queries (use eager loading), missing database indexes, un-cached repeated computations, and large in-memory data processing. Always profile before optimizing — measure, do not guess.
The FFI (Foreign Function Interface) extension (PHP 7.4+) allows PHP to call C functions and use C data types and structures directly from PHP code without writing a PHP extension in C. Load a C library: $ffi = FFI::cdef("int printf(const char *format, ...);", "libc.so.6"); $ffi->printf("Hello %s\n", "world");. FFI enables: calling system libraries, using high-performance C algorithms, accessing hardware APIs, and interoperating with native code. It is significantly faster than implementing equivalent logic in pure PHP. Drawbacks: complex API, type safety responsibility falls on the developer, platform-dependent (library paths differ by OS), and security risk (direct memory access). FFI is primarily for performance-critical extensions, scientific computing, and system programming where a full PHP extension is impractical.
The Proxy pattern provides a surrogate or placeholder object that controls access to another object. The proxy implements the same interface as the real subject and delegates work to it, while adding its own behavior. Types: Virtual Proxy (lazy initialization — creates the expensive object only when first needed), Protection Proxy (access control — checks permissions before forwarding), Remote Proxy (represents an object in a different address space), and Caching Proxy (caches results of expensive operations). PHP example: a LazyUserRepository proxy that only loads the database connection the first time a query is made. Laravel's Eloquent lazy loading relationships is a form of virtual proxy — related models are loaded only when accessed. Doctrine ORM uses proxy classes extensively for lazy-loading entities.
Immutable objects cannot be changed after creation — every "modification" returns a new object with the changed state. In PHP, implement with readonly properties (PHP 8.1) or by returning new instances from methods: public function withCurrency(string $currency): static { return new static($this->amount, $currency); }. Value Objects are immutable objects defined entirely by their values (not an identity/ID) — two Money objects with the same amount and currency are equal. Common value objects: Money, Email, Address, DateRange, Color. Benefits: thread safety, predictable behavior (no side effects), easy to test, and can be safely shared. PHP 8.1 readonly classes make immutable value objects trivial: readonly class Money { public function __construct(public int $amount, public string $currency) {} }.
PHP's native arrays are incredibly versatile but are not always the most efficient data structure. SPL data structures offer typed, memory-efficient alternatives. SplFixedArray uses significantly less memory than a native PHP array for large integer-indexed arrays because it allocates a contiguous block of memory (like C arrays). SplStack and SplQueue are proper LIFO/FIFO implementations. SplMinHeap and SplMaxHeap provide O(log n) priority queue operations. SplDoublyLinkedList allows O(1) head/tail operations. The tradeoff: SPL structures are faster for their specific use cases but less flexible than arrays and have an unfamiliar API. For most web applications, native PHP arrays are sufficient — consider SPL structures only when profiling shows array operations as bottlenecks.
Traditional PHP-FPM handles one request per process — it is synchronous and blocking. ReactPHP's event loop enables a single PHP process to handle thousands of concurrent I/O operations asynchronously. The event loop monitors file descriptors, timers, and signals. When an I/O operation (network request, file read) is ready, the loop dispatches the registered callback — no blocking. Key ReactPHP components: EventLoop (the core), Socket (async TCP/UDP), Http (async HTTP server), ChildProcess (spawn processes), Promise (deferred values). Example: a ReactPHP HTTP server handles 10,000 concurrent connections in a single PHP process, while PHP-FPM would need 10,000 worker processes. ReactPHP is ideal for WebSocket servers, API gateways, and long-running background services where PHP-FPM's process-per-request model is too expensive.
The Command pattern encapsulates a request as a standalone object containing all the information needed to perform the action. This allows parameterizing clients with different requests, queuing or logging requests, and supporting undo operations. In PHP, a command is a simple DTO: class CreateUserCommand { public function __construct(public readonly string $name, public readonly string $email) {} }. A Command Bus routes commands to their handlers: $bus->dispatch(new CreateUserCommand("Alice", "alice@example.com")) finds and calls the corresponding CreateUserHandler::handle(). This decouples the caller from the handler, makes operations testable in isolation, enables middleware (logging, transactions, authorization) around any command, and supports async execution by pushing commands onto a queue. Libraries like Tactician provide a simple command bus, while Laravel's Job system implements a variant of this pattern.
Common PHP design patterns: Singleton (one instance — use with caution, makes testing hard), Factory/Abstract Factory (encapsulate object creation), Builder (construct complex objects step-by-step), Repository (abstract data access), Observer/Event Dispatcher (publish-subscribe), Strategy (swap algorithms at runtime), Decorator (add behavior dynamically), Adapter (wrap incompatible interfaces), Facade (simplify a complex subsystem — Laravel's facades), Proxy (control access to an object), Command (encapsulate a request as an object — used in command buses), and Specification (encapsulate business rules as objects that can be combined). Modern PHP favors composition over inheritance and dependency injection over singletons.