PHP Backend Developer Top 100 Interview Questions
100 PHP backend developer interview questions with structured answers, comparison tables, flow diagrams, worked calculations and runnable code. Covers modern typed PHP, the request lifecycle, HTTP and APIs, security, PDO and databases, caching, queues, testing, performance, deployment and system design, from the basics to production incidents.
Questions 1-20 of 100
Q1.What does a PHP backend developer own beyond writing request handlers?
A PHP backend developer owns the whole service: its API design, data access, security, performance and behavior in production. Writing the code that answers a request is only the visible part.
Typical responsibilities:
- API and application design: clear routes, validation, consistent errors and versioning.
- Data access: safe queries with prepared statements, transactions, migrations and caching.
- Security: password hashing, session and cookie safety, protection against injection, cross-site scripting and request forgery.
- Performance: bytecode caching, query efficiency, profiling and sizing the process pool.
- Background work: queues and workers for slow tasks, such as emails and report generation.
- Testing and quality: unit and integration tests, strict types and static analysis.
- Operations: configuration, logging, deployments without downtime and dependency upgrades.
Modern PHP is fast and strongly typed when used well, so a strong developer writes typed, testable code and understands how the runtime executes each request.
Q2.How does a PHP request lifecycle work in a typical production setup?
In the common setup, a web server receives the HTTP request and passes it to a pool of PHP worker processes managed by the FastCGI process manager. One worker runs the application from the start for that request, sends the response and then discards all request state before taking the next request.
sequenceDiagram participant C as Client participant W as Web server participant P as Process manager participant K as PHP worker C->>W: HTTP request W->>P: FastCGI request P->>K: Assign an idle worker K->>K: Load cached bytecode, run the script K-->>W: Response W-->>C: HTTP response K->>K: Free all request memoryWhat this "share-nothing" model means:
- No state survives between requests in a worker, so memory leaks within a request rarely accumulate, and one bad request cannot corrupt the next.
- Bootstrapping happens every request, so frameworks and applications rely on bytecode caching and cached configuration to stay fast.
- Shared state lives outside PHP, in databases, caches or session stores.
- Concurrency equals worker count: each worker handles one request at a time, so slow requests tie up workers.
Long-running application servers for PHP also exist; they keep the application in memory between requests, trading simplicity for speed.
Q3.How does PHP's type system work, and what does `strict_types` change?
PHP is dynamically typed, but functions, methods and properties can declare types for parameters, return values and properties, which PHP checks at run time. Supported types include scalars, classes, nullable types, union types such as
int|string, intersection types andmixed.Coercive mode, the default: scalar arguments are converted when possible, so passing
"5"to anintparameter becomes5, and"5 apples"fails.Strict mode:
declare(strict_types=1);at the top of a file makes calls from that file require exact scalar types, with one exception: anintis accepted where afloatis expected. Passing"5"to anintparameter throws aTypeError.Why strict mode is recommended:
- Bugs from silent conversions surface immediately, close to their cause.
- Code becomes easier to analyze with static analysis tools.
- Behavior is predictable across inputs.
Practical tips:
- Add
declare(strict_types=1);to every file. - Type every parameter, return value and property.
- Convert and validate external input, such as query strings, at the boundary, since everything from HTTP arrives as strings.
Example
Without strict types, a numeric string is converted; with them, it throws.
<?php declare(strict_types=1); function total(int $quantity, float $price): float { return $quantity * $price; } echo total(3, 450), "\n"; // 1350 try { echo total("3", 450.0), "\n"; } catch (TypeError $e) { echo get_class($e), "\n"; // TypeError }The integer
450is accepted for thefloatparameter, which is the one conversion strict mode allows.Q4.How do `==` and `===` differ in PHP, and what are the comparison pitfalls?
===compares both type and value without conversion.==converts operands to a common type before comparing, following rules that have caused many security and logic bugs, although PHP 8 made string-to-number comparisons much saner.==loose comparison===strict comparisonConverts types before comparing Requires the same type and value 0 == "a"is false since PHP 8, but"1" == "01"is true"1" === "01"is falsenull == falseand"" == nullare truenull === falseis falseUse rarely, and only deliberately Use by default Common pitfalls:
- Numeric strings compare as numbers:
"1e3" == "1000"is true, which breaks comparisons of tokens or hashes; usehash_equalsfor secrets. in_arrayandarray_searchare loose by default; passtrueas the strict flag.switchuses loose comparison;matchuses strict comparison.- Sorting mixed types gives surprising orders; sort homogeneous data.
Rule: use
===and!==everywhere, enable strict types and convert input explicitly before comparing.Example
Loose comparison treats different numeric strings as equal; strict comparison and
hash_equalsdo not.<?php declare(strict_types=1); var_dump("1e3" == "1000"); var_dump("1e3" === "1000"); var_dump(in_array("1", [1, 2, 3])); var_dump(in_array("1", [1, 2, 3], true)); var_dump(hash_equals("1e3", "1000")); // Output: // bool(true) // bool(false) // bool(true) // bool(false) // bool(false)- Numeric strings compare as numbers:
Q5.How do arrays work in PHP?
A PHP array is an ordered hash map: it stores key-value pairs, keeps insertion order and allows integer or string keys. The same structure serves as a list, a dictionary, a set, a stack or a queue.
Key behaviors:
- Keys: integer-like strings, such as
"5", become integers; floats and booleans are cast too, which can merge keys unexpectedly. - Lists: an array with keys
0ton - 1in order is a list, checked witharray_is_list. - Order is preserved for iteration, and functions such as
sortorksortreorder in place. - Copying: arrays are values; assigning copies them lazily, only when one copy is modified.
Useful functions:
array_map,array_filterandarray_reducefor transformations.array_columnto pull a field out of a list of records.array_key_existsversusisset:issetreturns false for keys whose value isnull.- The spread operator
[...$a, ...$b]andarray_merge, which renumber integer keys, versus+, which keeps the left array's keys.
When to use something else: for strict typing and behavior, value objects or the SPL data structures, such as
SplQueueandSplPriorityQueue, are often clearer than arrays everywhere.Example
Integer-like string keys become integers,
issetandarray_key_existsdiffer on null, and+keeps existing keys.<?php declare(strict_types=1); $a = ["5" => "five", "x" => null]; echo json_encode(array_keys($a)), "\n"; var_dump(isset($a["x"]), array_key_exists("x", $a)); $left = [0 => "a", 1 => "b"]; $right = [0 => "z", 2 => "c"]; echo json_encode($left + $right), "\n"; echo json_encode(array_merge($left, $right)), "\n"; // Output: // [5,"x"] // bool(false) // bool(true) // ["a","b","c"] // ["a","b","z","c"]With
+, key0keeps the left valueaand only key2is added from the right, so the result is still a list;array_mergerenumbers and keeps every value.- Keys: integer-like strings, such as
Q6.How do copy-on-write and references work for PHP arrays and objects?
Arrays and strings are values with copy-on-write: assigning an array to another variable shares the data until one of them changes, and only then is a copy made. Objects are handled through object identifiers, so assigning an object gives another handle to the same object, not a copy.
Practical consequences:
- Passing a large array to a function is cheap until the function modifies it.
- Modifying an object inside a function changes the caller's object, because both refer to the same instance.
clonecreates a shallow copy of an object; nested objects are still shared unless__clonecopies them.
References with
&:function add(array &$items)lets a function modify the caller's array directly.foreach ($items as &$item)modifies elements in place, but leaves$itemas a reference after the loop; reusing the variable later can silently overwrite the last element. Callunset($item)after such loops.- References often make code harder to follow and can prevent copy-on-write optimizations.
Guideline: prefer returning new values over modifying through references, and use immutable value objects for data that should not change.
Example
Arrays copy on change, objects are shared, and a leftover reference from
foreachcorrupts an array.<?php declare(strict_types=1); $original = [1, 2, 3]; $copy = $original; $copy[] = 4; echo count($original), " ", count($copy), "\n"; // 3 4 $order = new stdClass(); $order->status = "new"; $alias = $order; $alias->status = "paid"; echo $order->status, "\n"; // paid $values = [1, 2, 3]; foreach ($values as &$v) { $v *= 10; } foreach ($values as $v) { } echo json_encode($values), "\n"; // [10,20,20]The second loop writes each element into the last slot through the leftover reference, so
30is lost.Q7.How do classes, interfaces, abstract classes and traits differ in PHP?
They are four ways to share structure and behavior. Interfaces define contracts, abstract classes share partial implementations through inheritance, traits copy reusable methods into classes and concrete classes provide full implementations.
Interface Abstract class Trait Class Declares methods and constants a type must provide Partial implementation that subclasses complete Reusable methods and properties copied into classes Full implementation that can be instantiated A class can implement many A class can extend only one A class can use many Can extend one class and implement many interfaces Best for dependency boundaries and type hints Best for shared logic in a closely related family Best for small cross-cutting helpers Best for concrete behavior Guidelines:
- Type hints should use interfaces for dependencies, such as
PaymentGateway, so implementations can change and tests can use fakes. - Prefer composition over deep inheritance trees; inject collaborators instead of extending base classes.
- Use traits sparingly: they hide where methods come from and can conflict; they suit small, stateless helpers.
- Mark classes
finalunless they are designed for extension, which keeps behavior predictable. - Abstract classes fit template-style code where subclasses fill in specific steps.
- Type hints should use interfaces for dependencies, such as
Q8.How do enums work in PHP?
Enums define a closed set of named values, such as order statuses. Pure enums have cases only; backed enums give each case a string or integer value, which makes them easy to store in databases and send in JSON.
Features:
- Type safety: a parameter typed
OrderStatusaccepts only its cases, not arbitrary strings. - Backed values:
OrderStatus::from("paid")converts a stored value to a case and throws on unknown values;tryFromreturnsnullinstead. - Methods and interfaces: enums can have methods, such as a human-readable label, and implement interfaces.
cases()lists every case, useful for validation and dropdown lists.matchwith enums gives exhaustive handling: an unhandled case throws an error at run time, and static analysis can warn earlier.
Good practices:
- Use enums instead of class constants or magic strings for fixed sets of values.
- Use backed enums at system boundaries, such as databases and APIs, and parse with
tryFromto handle bad input gracefully. - Put state transition rules on the enum, such as which statuses can follow
paid.
Example
A backed enum parses stored values, rejects unknown ones and holds its own transition rules.
<?php declare(strict_types=1); enum OrderStatus: string { case Pending = "pending"; case Paid = "paid"; case Shipped = "shipped"; public function canMoveTo(self $next): bool { return match ($this) { self::Pending => $next === self::Paid, self::Paid => $next === self::Shipped, self::Shipped => false, }; } } $status = OrderStatus::from("paid"); echo $status->name, "\n"; var_dump($status->canMoveTo(OrderStatus::Shipped)); var_dump(OrderStatus::tryFrom("lost")); echo implode(",", array_column(OrderStatus::cases(), "value")), "\n"; // Output: // Paid // bool(true) // NULL // pending,paid,shipped- Type safety: a parameter typed
Q9.How do readonly properties and readonly classes support immutability in PHP?
A readonly property can be assigned once, from inside the class, usually in the constructor, and never changed afterwards. A readonly class makes all its properties readonly. Together with constructor promotion, they make immutable value objects short and safe.
Rules:
- A readonly property must have a type.
- It can be initialized only once, and only from within the class scope.
- Modifying it later throws an
Error. - Readonly protects the property's value, but if the value is an object, that object's own properties can still change unless it is immutable too.
"Withers" for changes: to change a value, return a new instance with the modified property, for example
$money->add($other)returns a newMoney. Since PHP 8.5,clonecan take an array of new property values, which makes this concise.Why immutability helps:
- Value objects, such as money, email addresses and date ranges, cannot be corrupted after validation.
- Objects can be shared freely without defensive copies.
- Code is easier to reason about, especially in long-running worker processes.
Example
A readonly value object validates once, refuses changes and returns new instances for arithmetic.
<?php declare(strict_types=1); final readonly class Money { public function __construct(public int $minor, public string $currency) { if ($minor < 0) { throw new InvalidArgumentException("amount cannot be negative"); } } public function add(Money $other): Money { if ($other->currency !== $this->currency) { throw new InvalidArgumentException("currency mismatch"); } return new Money($this->minor + $other->minor, $this->currency); } } $price = new Money(45000, "INR"); $total = $price->add(new Money(5000, "INR")); echo $total->minor, " ", $price->minor, "\n"; // 50000 45000 try { $price->minor = 1; } catch (Error $e) { echo $e->getMessage(), "\n"; // Cannot modify readonly property Money::$minor }Q10.How do constructor property promotion and named arguments simplify PHP code?
Constructor property promotion declares and assigns properties directly in the constructor's parameter list, removing repetitive property declarations and assignments. Named arguments let callers pass arguments by parameter name, in any order, skipping optional ones.
Constructor promotion:
public function __construct(private OrderRepository $orders, private int $pageSize = 20) {}declares two properties and assigns them.- Visibility and
readonlymodifiers apply as with normal properties. - It works well for dependency injection and value objects.
Named arguments:
- Readability:
new Page(size: 50, number: 2)is clearer than positional numbers. - Skipping defaults: pass only the options you need, such as
json_encode($data, flags: JSON_THROW_ON_ERROR). - Safer refactoring of call sites with many boolean or numeric parameters.
Caution: parameter names become part of your public interface once callers use named arguments; renaming a parameter can break them. Treat parameter names in libraries as stable.
Example
Promotion removes boilerplate, and named arguments skip a default and make the call self-explanatory.
<?php declare(strict_types=1); final class Paginator { public function __construct( private int $perPage = 20, private int $maxPages = 100, private bool $includeTotal = false, ) {} public function describe(): string { return sprintf("per page %d, max %d, total %s", $this->perPage, $this->maxPages, $this->includeTotal ? "yes" : "no"); } } echo (new Paginator())->describe(), "\n"; // per page 20, max 100, total no echo (new Paginator(perPage: 50, includeTotal: true))->describe(), "\n"; // per page 50, max 100, total yesQ11.How does a `match` expression differ from a `switch` statement?
matchis an expression that returns a value, compares strictly with===, has no fall-through between arms and throws anUnhandledMatchErrorwhen no arm matches.switchis a statement with loose comparison and fall-through that requiresbreak.matchswitchReturns a value, so it can be assigned or returned Executes statements; no return value Strict comparison Loose comparison No fall-through; several conditions share an arm with commas Falls through without breakThrows if nothing matches and there is no defaultSilently does nothing if nothing matches When to use
match:- Mapping values to results, such as status to label or error code to HTTP status.
- Exhaustive handling of enum cases, where a missing case should fail loudly.
- Conditions with
match (true), where each arm is a Boolean expression, for range checks.
When
switchstill fits: several statements per case where fall-through is genuinely useful, though this is rare in modern code.Example
matchcompares strictly, combines conditions and fails loudly on an unknown value.<?php declare(strict_types=1); function httpStatus(string $error): int { return match ($error) { "not_found" => 404, "invalid", "missing_field" => 422, "conflict" => 409, }; } function shippingBand(float $kg): string { return match (true) { $kg <= 1 => "light", $kg <= 10 => "standard", default => "heavy", }; } echo httpStatus("missing_field"), " ", shippingBand(4.5), "\n"; // 422 standard try { httpStatus("boom"); } catch (UnhandledMatchError $e) { echo get_class($e), "\n"; // UnhandledMatchError }Q12.How do the nullsafe and null coalescing operators work in PHP?
The null coalescing operator
??returns its left side if it exists and is not null, otherwise its right side, without warnings for undefined keys. The nullsafe operator?->calls a method or reads a property only when the object is not null; otherwise the whole chain evaluates to null.Operators:
$a ?? $b: a default for missing or null values, such as$_GET["page"] ?? 1.$a ??= $b: assigns only if$ais null or unset, useful for lazy initialization.$a?->b()?->c: stops at the first null and returns null.
Pitfalls:
??treats0,""andfalseas present values; use?:when you want any falsy value replaced, and be careful that?:replaces0.- Nullsafe chains can hide bugs: if a value should never be null, fail loudly instead of quietly returning null.
??on input does not validate it; convert and check types after applying defaults.
These operators make optional data, such as an order without a shipping address, explicit and concise.
Example
Defaults are applied without warnings, and a missing address short-circuits the chain.
<?php declare(strict_types=1); final class Address { public function __construct(public string $city) {} } final class Customer { public function __construct(public ?Address $address) {} } $query = ["limit" => "0"]; echo $query["page"] ?? "1", " ", $query["limit"] ?? "20", "\n"; // 1 0 $withAddress = new Customer(new Address("Pune")); $withoutAddress = new Customer(null); echo $withAddress->address?->city ?? "unknown", "\n"; // Pune echo $withoutAddress->address?->city ?? "unknown", "\n"; // unknownQ13.How do closures, arrow functions and first-class callables work in PHP?
A closure is an anonymous function that can capture variables from its surrounding scope. Classic closures capture explicitly with
use, by value unless written with&. Arrow functions, writtenfn ($x) => ..., capture outer variables automatically by value but can contain only a single expression.Forms:
function ($item) use ($rate) { return $item * $rate; }captures$rateby value.fn ($item) => $item * $ratecaptures$rateautomatically.- First-class callable syntax:
strlen(...)or$service->handle(...)turns any function or method into a closure, without string names. - Closures can be bound to objects with
bindToorcall, though this is rarely needed.
Uses in backend code:
- Callbacks for
array_map,array_filterandusort. - Middleware pipelines, where each layer is a callable that receives the next one.
- Lazy factories in dependency containers.
Cautions: variables captured by value are copied at creation time, so later changes are not seen; capture by reference only when you really need shared state.
Example
A value captured by
useis frozen at creation, while first-class callable syntax passes a method as a callback.<?php declare(strict_types=1); final class Tax { public function __construct(private float $rate) {} public function apply(float $amount): float { return round($amount * (1 + $this->rate), 2); } } $discount = 0.1; $classic = function (float $p) use ($discount): float { return $p * (1 - $discount); }; $arrow = fn (float $p): float => $p * (1 - $discount); $discount = 0.5; echo $classic(100), " ", $arrow(100), "\n"; // 90 90 $tax = new Tax(0.18); echo implode(",", array_map($tax->apply(...), [100, 250])), "\n"; // 118,295Q14.How do generators work in PHP, and why are they useful for large data?
A generator is a function that uses
yieldto produce values one at a time. Calling it returns an iterator, and the function's body runs only as the caller asks for the next value, pausing at eachyield. Memory stays constant however many values are produced.Why they matter:
- Large files: read and process a file line by line instead of loading it all.
- Database results: stream rows from a query and process each without building a huge array.
- Pipelines: chain generators, such as read, parse, filter and batch, where each stage pulls from the previous one.
Features:
yield $key => $valueproduces keys too.yield fromdelegates to another generator or array.- Generators can receive values with
send, enabling coroutine-style code, though this is rarely needed in services. returninside a generator sets a final value readable withgetReturn.
Caution: a generator can be iterated only once; and code inside it runs lazily, so errors appear during iteration, not when the generator is created.
Example
A pipeline of generators reads, parses and batches records while holding only one batch in memory.
<?php declare(strict_types=1); function lines(string $text): Generator { foreach (explode("\n", trim($text)) as $line) { yield $line; } } function amounts(iterable $lines): Generator { foreach ($lines as $line) { [$id, $amount] = explode(",", $line); yield $id => (int) $amount; } } function batches(iterable $items, int $size): Generator { $batch = []; foreach ($items as $key => $value) { $batch[$key] = $value; if (count($batch) === $size) { yield $batch; $batch = []; } } if ($batch !== []) { yield $batch; } } $csv = "o1,100\no2,250\no3,75\no4,500\no5,40"; foreach (batches(amounts(lines($csv)), 2) as $batch) { echo implode(" ", array_keys($batch)), " = ", array_sum($batch), "\n"; } // Output: // o1 o2 = 350 // o3 o4 = 575 // o5 = 40Q15.How do exceptions and errors work in PHP?
Since PHP 7, most serious problems are thrown as objects that implement
Throwable. There are two branches:Exceptionfor application-level problems, which code is expected to handle, andErrorfor engine-level problems, such as calling an undefined method or a type error.ExceptionErrorThrown by application code and libraries Thrown by the engine for programming errors Examples: InvalidArgumentException,RuntimeException, custom domain exceptionsExamples: TypeError,ValueError,ArithmeticErrorCatch where you can recover or translate Usually caught only at the top level for logging Good practices:
- Throw specific exceptions, including custom ones such as
OrderNotFound, so callers can catch precisely. - Chain causes: pass the original exception as
$previouswhen wrapping, so stack traces show the root cause. - Catch
Throwableonly at the top level, such as the front controller, to log and return a 500 response. - Convert warnings to exceptions with an error handler, so problems such as failed file reads do not continue silently.
- Use
finallyfor cleanup that must always run.
At API boundaries, map domain exceptions to HTTP statuses, and never show stack traces to users in production.
Example
A low-level failure is wrapped with context, the cause is preserved and an engine error is a separate branch.
<?php declare(strict_types=1); final class OrderNotFound extends RuntimeException {} function loadOrder(int $id): array { try { throw new PDOException("connection reset"); } catch (PDOException $e) { throw new OrderNotFound("order $id could not be loaded", 0, $e); } } try { loadOrder(7); } catch (OrderNotFound $e) { echo $e->getMessage(), " <- ", $e->getPrevious()?->getMessage(), "\n"; // order 7 could not be loaded <- connection reset } try { intdiv(1, 0); } catch (Error $e) { echo get_class($e), " is an Exception: ", var_export($e instanceof Exception, true), "\n"; // DivisionByZeroError is an Exception: false }- Throw specific exceptions, including custom ones such as
Q16.How do namespaces and autoloading work in PHP?
Namespaces group related classes and prevent name collisions, such as
App\Orders\OrderandApp\Billing\Order. Autoloading loads a class file automatically the first time the class is used, so applications do not need long lists ofrequirestatements.How autoloading works:
- An autoloader function is registered with
spl_autoload_register. - When code references an unknown class, PHP calls the autoloader with the fully qualified class name.
- The autoloader maps the name to a file path, usually by converting namespace separators to directory separators, and includes it.
The common convention: a namespace prefix, such as
App\, maps to a source directory, such assrc/, and each class lives in a file named after it. The dependency manager generates an optimized autoloader for your code and all your packages.Good practices:
- One class per file, with the file path matching the namespace.
- Import classes with
usestatements at the top of the file instead of writing full names everywhere. - Generate an optimized class map for production, which avoids file system lookups.
- Avoid side effects at file level, such as running code when a file is included; files should only declare things.
- An autoloader function is registered with
Q17.How do you manage dependencies and versions in a PHP project?
Declare dependencies with version constraints in the project's manifest, let the dependency manager resolve compatible versions and commit the lock file, so every environment installs exactly the same versions.
Key practices:
- Version constraints: caret constraints, such as
^3.2, allow compatible minor and patch updates under semantic versioning, while the lock file pins exact versions. - Commit the lock file for applications; production installs should use it, without development dependencies, and with an optimized autoloader.
- Separate development tools, such as test frameworks and static analysis, from runtime dependencies.
- Declare required PHP extensions and the PHP version, so installation fails early on incompatible servers.
- Audit dependencies for known vulnerabilities in continuous integration.
- Update regularly in small steps, reviewing changelogs for major versions.
Supply-chain care:
- Prefer well-maintained packages with active security response.
- Review install scripts that packages run.
- Use a private mirror or cache for builds, so outages and removed packages do not break deployments.
Fewer dependencies mean fewer upgrades and less risk; PHP's standard library and extensions cover many needs.
- Version constraints: caret constraints, such as
Q18.How do `self`, `static` and late static binding work in PHP?
selfrefers to the class where the code is written.staticrefers to the class that was actually called at run time, which can be a subclass. This late static binding lets base classes write factory methods and fluent APIs that return the right subclass.Where the difference shows:
- Factories:
return new static()in a base class creates an instance of the subclass the method was called on;new self()always creates the base class. - Return types: a method declared to return
statictells static analysis that subclasses return their own type, useful for fluent builders. - Static properties:
static::$tablereads a property that subclasses can override.
Cautions with static members:
- Static properties are shared across all instances for the whole request, or across many requests in long-running workers, which makes them a common source of leaking state.
- Static method calls are hard to replace in tests; prefer injected objects for anything with side effects.
- Use static methods for pure functions and named constructors, such as
Money::fromString("10.50").
Example
The same inherited factory creates the base class with
selfbut the subclass withstatic.<?php declare(strict_types=1); class Model { public static function createSelf(): self { return new self(); } public static function createStatic(): static { return new static(); } } final class Order extends Model {} echo get_class(Order::createSelf()), "\n"; // Model echo get_class(Order::createStatic()), "\n"; // Order- Factories:
Q19.How do magic methods work in PHP, and when should you avoid them?
Magic methods are special methods, with names starting with two underscores, that PHP calls automatically in certain situations. They power features such as constructors and string conversion, and they can intercept property access and method calls.
Common magic methods:
__constructand__destructfor setup and cleanup.__toStringto convert an object to a string.__get,__set,__issetand__unsetfor inaccessible properties.__calland__callStaticfor undefined methods.__invoketo make an object callable like a function.__cloneto customize copying, and__serializeand__unserializefor serialization.
When to avoid the interception methods:
- They hide behavior:
__getand__callmake properties and methods invisible to static analysis and editors. - They are slower than real properties and methods.
- They turn typos into silent bugs: a misspelled property goes to
__getinstead of failing.
Good uses:
__invokefor single-action handlers,__toStringfor value objects such as email addresses, and__serializeto control what is stored. Prefer explicit properties and methods for everything else.Q20.How does PHP manage memory and garbage collection?
PHP frees most memory through reference counting: each value tracks how many variables refer to it, and it is freed as soon as the count drops to zero. A cycle collector runs periodically to free groups of objects that reference each other but are no longer reachable from the program.
In the request-per-process model:
- All memory allocated during a request is released at the end of the request, so small leaks within a request rarely matter.
memory_limitcaps memory per request; exceeding it stops the script with a fatal error.
Where memory problems appear:
- Large arrays: loading a whole result set or file into memory; use generators and streaming.
- Long-running scripts and workers, such as queue consumers, where leaks accumulate across many jobs.
- Circular references in object graphs, which wait for the cycle collector.
- Static caches that grow without bounds in long-running processes.
Tools:
memory_get_usageandmemory_get_peak_usagemeasure memory; profilers show allocation by function. In workers, restart the process after a number of jobs or a memory threshold as a safety net, while fixing real leaks.