PHP Interview Questions
All 40 PHP interview questions are now live, covering request lifecycle, forms, OOP, Composer, PDO, APIs, framework architecture, caching, and performance tuning.
PHP is a server-side scripting language that executes on the web server and returns HTML, JSON, or other output to the client.
For each request, the server boots PHP, loads the app, runs the script, sends the response, and usually discards in-memory state at the end of the request.
<?php $name = $_GET['name'] ?? 'Guest'; echo "Hello, {$name}";
A product page request loads configuration, queries data, renders HTML, and ends cleanly after the response.
Treating PHP like a long-running app by assuming request memory persists between users or requests.
What changes when PHP runs under RoadRunner or Swoole?
PHP supports scalar types like int, float, string, bool, compound types like array and object, and special types like null and resource.
Type juggling means PHP can coerce values between types during comparisons, arithmetic, and parameter handling unless strict typing is enforced.
<?php declare(strict_types=1); $sum = 2 + "3"; var_dump($sum); // int(5)
Loose input from forms and APIs often arrives as strings and may be coerced during business logic.
Using loose comparisons and assuming "0", 0, false, and null behave the same.
What is the difference between == and === in PHP?
PHP arrays are ordered maps, so the same structure can act like a list, dictionary, stack, or lookup table.
Indexed arrays use numeric keys, associative arrays use string keys, and multidimensional arrays nest arrays for grouped data.
<?php $user = ['name' => 'Amit', 'skills' => ['PHP', 'SQL']]; echo $user['skills'][0];
A JSON API response is often decoded into nested associative arrays for access and transformation.
Assuming numeric keys are always sequential or that missing keys return safe defaults.
When would you choose an object over an associative array?
Common string functions include strlen, trim, strpos, str_contains, substr, explode, implode, str_replace, strtolower, and strtoupper.
These functions cover searching, slicing, splitting, joining, cleaning, and normalizing string data in everyday PHP code.
<?php $email = trim(" user@example.com "); if (str_contains($email, "@")) { echo strtolower($email); }
Input cleanup and simple parsing often rely on trim, explode, replace, and substring checks.
Using strpos in a boolean check and missing matches at position 0.
Why is strpos returning 0 different from false?
Superglobals are predefined arrays available in all scopes and expose request, session, cookie, and server metadata.
Use $_GET for query parameters, $_POST for form bodies, $_SESSION for server-side user state, $_COOKIE for client cookies, and $_SERVER for request and environment details.
<?php session_start(); $page = $_GET['page'] ?? 'home'; $_SESSION['last_page'] = $page; echo $_SERVER['REQUEST_METHOD'];
Login flows often read POST credentials, set session state, and inspect SERVER headers for request context.
Trusting superglobal data directly without validation or output escaping.
Which data belongs in a session versus a cookie?
Functions encapsulate reusable logic and can define typed parameters, default values, variadic arguments, and return types.
PHP also supports pass-by-value by default, references when needed, and strict type enforcement when strict_types is enabled.
<?php function total(float $tax, int ...$items): float { return array_sum($items) * (1 + $tax); } echo total(0.18, 100, 200);
Helper functions centralize formatting, validation, and shared calculations across controllers and services.
Using weakly defined signatures that hide expected input and output.
When would you use a variadic parameter instead of an array argument?
include loads a file and emits a warning if it is missing, while require loads a file and causes a fatal error if it cannot be found.
The _once variants prevent duplicate inclusion, which is useful for shared definitions like classes, constants, and bootstrap files.
<?php require __DIR__ . '/config.php'; require_once __DIR__ . '/vendor/autoload.php'; include __DIR__ . '/optional-banner.php';
Apps usually require critical bootstrap files and only include optional view fragments.
Using include for critical files and letting the app continue in a broken state.
Why can duplicate includes cause class redeclaration errors?
Form handling reads submitted input from $_POST or $_GET, validates required fields and formats, and then processes or stores the data.
Validation should happen server-side even if client-side validation exists, and output should be escaped when redisplayed.
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST') { $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); if (!$email) { echo 'Invalid email'; } }
Contact forms validate email, subject, and message before sending mail or saving a ticket.
Relying only on HTML attributes like required and pattern.
What is the difference between validation and sanitization?
PHP can read and write files using simple helpers like file_get_contents and file_put_contents or lower-level handles such as fopen, fread, fwrite, and fclose.
Choose higher-level helpers for small files and streams/handles when you need chunked processing or precise control.
<?php $contents = file_get_contents(__DIR__ . '/notes.txt'); $handle = fopen(__DIR__ . '/log.txt', 'a'); fwrite($handle, "Visited\n"); fclose($handle);
Import jobs often stream large CSV files line by line instead of loading them fully into memory.
Reading large files into memory without checking size or handling failures.
When is fopen safer than file_get_contents?
PHP reports different severities such as notices for minor issues, warnings for recoverable problems, and fatal errors for execution-stopping failures.
Error handling combines error_reporting, display/log configuration, exceptions, and custom handlers to surface issues safely.
<?php set_error_handler(function ($severity, $message) { throw new ErrorException($message, 0, $severity); }); try { include 'missing.php'; } catch (Throwable $e) { error_log($e->getMessage()); }
Production systems log warnings and exceptions centrally instead of exposing raw errors to users.
Suppressing errors with @ instead of fixing the cause or handling it explicitly.
How do exception handling and traditional PHP errors differ?
PHP OOP organizes behavior into classes with properties and methods, controlled by visibility modifiers like public, protected, and private.
Interfaces define contracts, abstract classes provide partial implementation, and concrete classes extend or implement them to model behavior cleanly.
<?php interface Logger { public function log(string $message): void; } abstract class FileLogger implements Logger { protected string $path; public function __construct(string $path) { $this->path = $path; } }
A payment module can expose a PaymentGateway interface with Stripe and PayPal implementations.
Putting unrelated responsibilities into one large class with public everything.
When would you choose an abstract class over an interface?
Namespaces prevent class name collisions by grouping classes under unique logical paths such as App\Service or Domain\Billing.
PSR-4 autoloading maps namespaces to directories so Composer can load classes on demand without manual include statements.
<?php namespace App\Service; class Mailer {} // composer.json: "App\\": "src/"
Modern PHP apps rely on PSR-4 so classes load automatically across domain, controller, and infrastructure layers.
Mixing class names and file locations that do not match the autoload mapping.
How does Composer know where to find App\Service\Mailer?
Traits let PHP reuse method implementations across unrelated classes without inheritance, reducing duplication for shared behavior.
They are best for small cross-cutting concerns such as timestamp formatting or simple helper behavior, not as a substitute for good design.
<?php trait HasUuid { public function generateUuid(): string { return bin2hex(random_bytes(16)); } } class Order { use HasUuid; }
Multiple Eloquent models may share common audit or identifier behavior through traits.
Stuffing heavy business logic into traits and creating hidden coupling across many classes.
What happens if two traits define the same method?
Composer is PHP's dependency manager for installing packages, resolving versions, generating autoload files, and defining project metadata.
Dependencies are declared in composer.json, locked in composer.lock, and installed reproducibly across environments.
<?php // composer.json: {"require":{"monolog/monolog":"^3.0"}} // then: composer install
Teams use Composer to pin framework, logging, testing, and utility package versions across dev and production.
Running update in production or ignoring the lock file for application deployments.
What is the difference between composer install and composer update?
PDO provides a consistent database API for connecting, preparing queries, binding values, and fetching results across supported drivers.
Prepared statements separate SQL structure from input values, which improves safety against SQL injection and can improve clarity and reuse.
<?php $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email'); $stmt->execute(['email' => $email]); $user = $stmt->fetch();
Login and search endpoints use prepared statements to safely query user-supplied values.
Building SQL by string concatenation with user input.
Why are prepared statements safer than manual escaping?
HTTP is stateless, so PHP uses cookies and sessions to remember user-specific data across requests.
A cookie usually stores a session identifier in the browser, while session data itself is stored server-side and retrieved on later requests.
<?php session_start(); $_SESSION['user_id'] = 42; setcookie('theme', 'dark', time() + 3600, '/', '', true, true);
Authenticated apps store the logged-in user reference in a session and small preferences in cookies.
Storing sensitive raw data like passwords or full profiles directly in cookies.
What cookie flags improve security for sessions?
PHP uses PCRE functions like preg_match for matching patterns and preg_replace for replacing text based on regex rules.
Regex is powerful for structured text validation and extraction, but patterns should stay readable and targeted.
<?php if (preg_match('/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i', $email)) { echo 'valid'; }
Regex is commonly used to validate identifiers, extract tokens, or rewrite formatted content.
Writing overly complex regex for problems better solved with dedicated validators or simpler string checks.
When would filter_var be better than a regex?
json_encode converts PHP arrays or objects into JSON strings, and json_decode parses JSON into objects or associative arrays.
Use flags and error handling carefully because invalid UTF-8, depth, and decode mode can affect correctness.
<?php $json = json_encode(['name' => 'Amit', 'active' => true], JSON_THROW_ON_ERROR); $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
APIs serialize response payloads to JSON and decode incoming request bodies for processing.
Ignoring json_last_error or assuming decode always returns valid data.
Why might you decode JSON into an associative array instead of an object?
PHP supports scalar, class, interface, union, nullable, and mixed type declarations for parameters, properties, and return values.
These declarations document intent, improve static analysis, and fail earlier when invalid data crosses boundaries.
<?php function findUser(int $id): ?array { return $id > 0 ? ['id' => $id] : null; }
Typed service methods make API boundaries clearer and reduce runtime bugs in larger codebases.
Adding broad types like mixed everywhere and losing the value of contracts.
What is the difference between ?string and string|null?
Enums model a fixed set of named values, which is safer and clearer than free-form strings for statuses, roles, or modes.
match is an expression-based control structure that performs strict comparisons and returns a value without fall-through.
<?php enum Status: string { case Draft = 'draft'; case Published = 'published'; } $label = match ($status) { Status::Draft => 'Hidden', Status::Published => 'Live', };
Publishing workflows often use enums for state and match for clear state-to-action mapping.
Keeping legacy string constants everywhere and then only wrapping them partially in enums.
How is match different from switch in PHP?
Design patterns are reusable solutions to common design problems, but they should be applied only when they fit the constraints of the code.
Singleton controls a single instance, Factory centralizes creation, Observer broadcasts state changes, and Strategy swaps algorithms behind one interface.
<?php interface PaymentStrategy { public function pay(int $amount): void; } class CardPayment implements PaymentStrategy { public function pay(int $amount): void {} }
Checkout systems often use Strategy for payment providers and Observer for post-order notifications.
Forcing patterns into simple code and increasing indirection without solving a real problem.
Why is Singleton often discouraged in modern PHP apps?
Generators let a function produce values lazily with yield instead of building the full result set in memory first.
This is useful for large datasets, streaming reads, and pipelines where values can be processed one at a time.
<?php function readLines(string $file): Generator { $handle = fopen($file, 'r'); while (($line = fgets($handle)) !== false) { yield trim($line); } fclose($handle); }
A CSV import can process millions of rows lazily without exhausting memory.
Assuming a generator behaves like a reusable array that can be randomly accessed or rewound freely.
When is a generator better than returning an array?
Closures are anonymous functions that can capture surrounding variables, anonymous classes create one-off object implementations, and first-class callable syntax turns methods into callable values cleanly.
These features support flexible callbacks, small adapters, and inline behavior without creating named classes everywhere.
<?php $multiplier = 2; $double = fn(int $n): int => $n * $multiplier; $callable = strlen(...); echo $double($callable('php'));
Framework pipelines and collection APIs often accept closures for filtering, mapping, and dispatch behavior.
Capturing too much outer state in closures and making behavior hard to reason about or test.
What is the difference between a closure and a named function?
The Reflection API inspects classes, methods, properties, attributes, parameters, and types at runtime.
Frameworks use it for dependency injection, annotations or attributes, testing helpers, and automated wiring, though it adds overhead and complexity if overused.
<?php $ref = new ReflectionClass(App\Service\Mailer::class); foreach ($ref->getMethods() as $method) { echo $method->getName(); }
Containers inspect constructor signatures to resolve dependencies automatically.
Using reflection for routine business logic where explicit interfaces would be simpler and faster.
Why do DI containers often use reflection?
The Standard PHP Library provides iterators, heap, queue, stack, fixed array, and filesystem traversal tools for structured data handling.
Classes like ArrayIterator, DirectoryIterator, SplQueue, SplStack, and SplPriorityQueue offer more intent and specialized behavior than generic arrays.
<?php $queue = new SplQueue(); $queue->enqueue('job-1'); $queue->enqueue('job-2'); echo $queue->dequeue();
Background processing and traversal code can use SPL queues and iterators to model behavior more clearly.
Using arrays for every structure even when queue or stack semantics matter.
Why might SplQueue be preferable to an array-based queue?
Dependency injection passes required collaborators into a class instead of having the class build them internally.
A container stores object definitions, and autowiring can resolve dependencies automatically from constructor type hints when configuration is unambiguous.
<?php class OrderService { public function __construct(private PaymentGateway $gateway) {} }
Framework containers build controllers, services, and handlers with their dependencies at runtime.
Turning the container into a global service locator inside business code.
What problem does constructor injection solve in tests?
Middleware wraps request handling in a chain where each layer can inspect, modify, short-circuit, or pass control to the next handler.
It is commonly used for authentication, logging, CORS, rate limiting, and request/response decoration in HTTP frameworks.
<?php $middleware = function ($request, $next) { if (!$request->user()) { throw new RuntimeException('Unauthorized'); } return $next($request); };
An API stack may run auth, tenant resolution, and audit logging before the controller executes.
Putting core business rules in middleware that should live in domain services or policies.
What is the benefit of short-circuiting in middleware?
Fibers provide lightweight cooperatively scheduled execution units that can suspend and resume without blocking the entire control flow.
They do not make PHP magically asynchronous by themselves, but libraries can use them to build cleaner async abstractions over event loops or non-blocking I/O.
<?php $fiber = new Fiber(function (): void { Fiber::suspend('waiting'); echo 'resumed'; }); echo $fiber->start(); $fiber->resume();
Async libraries can hide callback-heavy flow behind Fiber-based code that reads more sequentially.
Assuming Fibers automatically improve performance without non-blocking I/O or compatible runtime support.
Why are Fibers cooperative rather than preemptive?
Laravel's service container resolves dependencies, manages bindings, and supports contextual construction across the framework.
Facades provide static-looking proxies to container-resolved services, which makes usage terse while the actual implementation still comes from the container.
<?php use Illuminate\Support\Facades\Cache; Cache::put('key', 'value', 60); // facade resolves the cache service from the container
Controllers and jobs rely on container injection, while common framework services are often accessed through facades.
Overusing facades deep in business logic and hiding dependencies that should be explicit.
Why can constructor injection be preferable to a facade in domain code?
Event-driven architecture models important state changes as events and lets listeners or subscribers react without tight coupling to the producer.
In PHP apps this often appears as domain events, framework events, or message-based integration between application modules.
<?php event(new OrderPlaced($order)); // listeners may send email, update analytics, and enqueue fulfillment
After checkout, separate listeners can send receipts, notify warehouses, and update reporting asynchronously.
Using events for everything and making control flow impossible to trace.
How are domain events different from framework events?
Queue systems move slow or retryable work out of the request path by serializing jobs into a broker or database and processing them with background workers.
Workers fetch jobs, execute handlers, retry transient failures, and often support delays, priorities, dead-letter handling, and monitoring.
<?php dispatch(new SendInvoiceEmail($orderId)); // worker consumes and processes the job later
Email, media processing, and third-party webhook syncs are common tasks handled by background workers.
Writing jobs with side effects that break when retried after partial success.
Why must queued jobs be idempotent?
A solid REST API uses clear resource-oriented routes, validated input, consistent response formats, proper HTTP status codes, and reliable authentication.
Versioning, pagination, rate limits, and error contracts help clients evolve safely as the API grows.
<?php // POST /api/v1/orders -> validate payload, authorize user, create order, return 201 with JSON body
Public mobile and SPA backends depend on stable versioned APIs with predictable error and auth behavior.
Returning inconsistent shapes and statuses across endpoints.
When would you choose header-based versioning over URI versioning?
Effective tests focus on behavior, cover critical paths, isolate external dependencies, and stay fast enough to run frequently.
PHPUnit and Pest support unit, integration, and feature testing, while mocks and fakes help control boundaries like mail, queues, or third-party APIs.
<?php test('it calculates total', function () { expect(total(100, 0.18))->toBe(118.0); });
Teams mix unit tests for core logic with integration tests for HTTP, database, and queue workflows.
Writing brittle tests that mirror private internals instead of observable behavior.
When is a fake better than a mock?
Safe migrations are small, reversible when possible, tested on realistic data, and designed to avoid long locks or breaking rolling deployments.
Expand-and-contract changes, backfills, feature flags, and deployment sequencing reduce risk when schema and code evolve together.
<?php // 1) add nullable column 2) deploy code writing both fields 3) backfill 4) switch reads 5) drop old column later
Large production tables often require phased schema changes to avoid downtime and compatibility failures.
Combining destructive schema changes with code that still depends on the old structure in the same release.
Why is the expand-and-contract pattern safer in production?
Caching reduces repeated work by storing expensive results closer to where they are needed, from PHP bytecode to computed application data.
Redis and Memcached handle shared runtime cache, OPcache speeds PHP execution, and application cache stores domain-specific results with careful invalidation rules.
<?php $user = $cache->remember("user:$id", 300, fn() => $repo->find($id));
Dashboards often cache expensive aggregated queries while product pages cache rendered fragments or lookup data.
Adding cache without a clear invalidation strategy and serving stale or inconsistent data.
When should you prefer cache-aside over write-through caching?
OPcache stores compiled PHP opcodes in shared memory so scripts do not need to be parsed and compiled on every request.
Tuning focuses on memory size, file count, timestamp validation, preloading stable code, and understanding that JIT mainly helps CPU-heavy workloads rather than typical I/O-bound web apps.
<?php // php.ini: opcache.enable=1; opcache.memory_consumption=256; opcache.max_accelerated_files=20000; opcache.validate_timestamps=0
Production deployments often warm OPcache and disable frequent file timestamp checks for better throughput.
Enabling JIT and expecting dramatic gains for database-heavy CRUD traffic without profiling.
Why does OPcache usually matter more than JIT for web apps?
Profiling measures where time and memory are spent so optimization targets are based on evidence instead of intuition.
Xdebug is common for local tracing, while Blackfire and Tideways provide lower-overhead call graphs, timeline views, and production-friendly performance insights.
<?php // profile a slow request, inspect call graph, identify hot functions, then verify improvement with a second run
A slow checkout might be traced to repeated DB calls, heavy serialization, or expensive template rendering.
Micro-optimizing syntax or loops before finding the real bottleneck.
What is the difference between profiling and benchmarking?
The N+1 query problem happens when code fetches a parent set with one query and then runs extra queries for each item, multiplying database round trips.
You detect it with query logs, profilers, ORM debug tools, and slow traces, then fix it with eager loading, joins, batching, or reshaping data access.
<?php $posts = Post::with('author')->get(); foreach ($posts as $post) { echo $post->author->name; }
Admin listings often become slow when each row triggers separate queries for related users, tags, or counts.
Assuming ORM convenience automatically means efficient SQL.
How can eager loading still be inefficient if overused?
Swoole and RoadRunner run PHP in long-lived worker processes instead of booting the full application fresh for every request.
This reduces bootstrap overhead and enables persistent state, but it also requires careful handling of shared memory, cleanup, and libraries that assume classic request isolation.
<?php // worker boots app once, handles many requests, and must reset per-request state between executions
High-throughput APIs use long-lived workers to reduce latency from repeated framework bootstrapping.
Leaving request-specific data in static properties or singletons across worker requests.
What kinds of bugs appear when code assumes per-request process isolation?
Bootstrap overhead comes from loading configuration, service providers, Composer metadata, and early object creation before useful work starts.
Optimization usually means authoritative classmaps, fewer eager bindings, cached config and routes, reduced package count, and avoiding expensive work during startup.
<?php // composer dump-autoload -o; cache framework config/routes; defer heavy service initialization until first use
Large frameworks often regain noticeable latency by trimming providers and optimizing Composer autoload generation.
Optimizing business logic while ignoring the fact that the app spends most of its time booting.
What is the benefit of Composer optimized autoload files?
Frequently Asked Questions
This page contains the full 40-question PHP track across all 5 levels: basic, intermediate, advanced, experienced, and performance.
The live set covers PHP basics, forms, arrays, OOP, Composer, PDO, sessions, JSON, framework architecture, queues, APIs, caching, and runtime performance topics.
Both. It starts from core PHP fundamentals and then expands into framework-oriented topics such as DI containers, middleware, Laravel architecture, and job processing.
Yes. The examples use realistic request handling, data access, validation, and runtime patterns instead of toy-only snippets.
Yes. It includes enums, match expressions, Fibers, typed signatures, OPcache, and current performance-oriented practices.
Yes. Input validation, prepared statements, session handling, and safe request processing are covered throughout the track.
Yes. The later sections cover service containers, queues, middleware, testing, migrations, and architectural topics commonly discussed in framework roles.
Because production PHP still depends on getting request lifecycle, input safety, data access, and state management right before higher-level architecture matters.