PHP Interview Questions (2025): OOP, Performance, and Modern PHP

PHP Interview Questions Overview

PHP powers 77% of websites with a server-side language (including WordPress, Facebook’s HHVM era, Shopify’s partner ecosystem, and Wikipedia). Modern PHP (8.0+) is significantly different from legacy PHP 5 — senior interviews test modern OOP, type safety, performance patterns, and PHP 8 features. This guide covers what you will actually be asked.

Object-Oriented Programming

What is the difference between abstract classes and interfaces in PHP?

An interface defines a contract (method signatures) with no implementation. A class can implement multiple interfaces. An abstract class can have both abstract methods (no implementation, must be overridden) and concrete methods (with implementation). A class can extend only one abstract class. Use interfaces for capability contracts (Countable, Serializable); use abstract classes when you want to provide shared implementation that subclasses customize.


interface Exportable {
    public function toArray(): array;
    public function toJson(): string;
}

abstract class BaseReport implements Exportable {
    abstract protected function fetchData(): array;

    public function toJson(): string {
        return json_encode($this->toArray());  // concrete implementation
    }
    // toArray() still abstract — subclasses implement it
}

What are traits in PHP?

Traits are a mechanism for code reuse in single-inheritance languages. A trait is like a partial class — it defines methods that can be mixed into any class. Unlike interfaces, traits contain implementation. A class can use multiple traits. PHP resolves conflicts with insteadof and as keywords.


trait Timestampable {
    private ?DateTime $createdAt = null;
    private ?DateTime $updatedAt = null;

    public function touch(): void {
        $now = new DateTime();
        if ($this->createdAt === null) {
            $this->createdAt = $now;
        }
        $this->updatedAt = $now;
    }

    public function getUpdatedAt(): ?DateTime {
        return $this->updatedAt;
    }
}

class Order {
    use Timestampable;
    // now has touch(), getUpdatedAt() etc.
}

PHP 8 Features

Named arguments


// Instead of remembering parameter order:
array_slice(array: $arr, offset: 2, length: 5, preserve_keys: true);

// Call functions with any subset of optional parameters:
htmlspecialchars(string: $str, double_encode: false);

Nullsafe operator


// Before PHP 8:
$city = null;
if ($user !== null && $user->getAddress() !== null) {
    $city = $user->getAddress()->getCity();
}

// PHP 8 nullsafe operator:
$city = $user?->getAddress()?->getCity();  // returns null if any link is null

Match expression (PHP 8.0)


$status = match($statusCode) {
    200, 201 => "success",
    301, 302 => "redirect",
    404      => "not_found",
    500      => "server_error",
    default  => "unknown",
};
// Unlike switch: strict comparison (===), no fallthrough, exhaustive

Enums (PHP 8.1)


enum Status: string {
    case Draft     = "draft";
    case Published = "published";
    case Archived  = "archived";

    public function label(): string {
        return match($this) {
            Status::Draft     => "Draft",
            Status::Published => "Published",
            Status::Archived  => "Archived",
        };
    }
}

$status = Status::Published;
echo $status->value;    // "published"
echo $status->label();  // "Published"
Status::from("draft");  // Status::Draft

Fibers (PHP 8.1): Cooperative Multitasking


$fiber = new Fiber(function(): void {
    $value = Fiber::suspend("first");   // suspend, return "first" to caller
    echo "Got: {$value}n";            // resumed with "hello"
    Fiber::suspend("second");
});

$result = $fiber->start();             // "first"
$result = $fiber->resume("hello");     // "second", prints "Got: hello"

Fibers are PHP’s primitive for cooperative multitasking — the foundation for async frameworks like ReactPHP and Amphp. Unlike goroutines, PHP fibers require explicit suspend/resume (no automatic I/O yielding). Libraries built on fibers can implement async/await patterns similar to JavaScript.

Type System


// PHP 8 union types:
function processId(int|string $id): User|null {
    return User::find($id);
}

// PHP 8.1 intersection types (must satisfy ALL types):
function process(Iterator&Countable $collection): void { }

// PHP 8 readonly properties (PHP 8.1):
class User {
    public function __construct(
        public readonly int $id,
        public readonly string $email,
    ) {}
}
// $user->id = 5; // Error: Cannot modify readonly property

// Nullables:
function find(int $id): ?User { ... }  // returns User or null

Generators

Generators produce values lazily without storing all values in memory. A function with yield is a generator. Useful for processing large datasets row by row or generating infinite sequences.


function readLargeFile(string $path): Generator {
    $handle = fopen($path, "r");
    while (($line = fgets($handle)) !== false) {
        yield $line;   // suspend here, resume on next()
    }
    fclose($handle);
}

// Process a 10GB file with O(1) memory:
foreach (readLargeFile("/var/log/access.log") as $line) {
    processLine($line);
}
// No array_map() on 50M lines — generator pulls one line at a time

Performance Optimization

OpCache

PHP normally parses and compiles source files on every request. OpCache stores compiled bytecode in shared memory — subsequent requests skip parsing and compilation. Enable in php.ini: opcache.enable=1, opcache.memory_consumption=256, opcache.validate_timestamps=0 (production — no file stat checks). 2-5× throughput improvement on typical applications.

Database Query Optimization


// Use PDO prepared statements (cache query plan + prevent SQL injection):
$stmt = $pdo->prepare("SELECT * FROM orders WHERE user_id = ? AND status = ?");
$stmt->execute([$userId, $status]);

// Avoid N+1: fetch related data in one query
$orders = $pdo->query("
    SELECT o.*, u.name as user_name
    FROM orders o
    JOIN users u ON o.user_id = u.id
    WHERE o.created_at > NOW() - INTERVAL 7 DAY
")->fetchAll();

Caching with Redis


function getUser(int $id): array {
    $key = "user:{$id}";
    $cached = $redis->get($key);
    if ($cached !== false) {
        return json_decode($cached, true);
    }
    $user = $db->fetchUser($id);  // slow DB query
    $redis->setex($key, 3600, json_encode($user));  // cache 1 hour
    return $user;
}

Composer and Dependency Management

Composer is PHP’s dependency manager. Key commands: composer require vendor/package installs a package and updates composer.json and composer.lock. composer install (CI/production) installs exact versions from composer.lock. composer update updates to latest allowed versions and regenerates composer.lock. Autoloading: Composer generates a PSR-4 autoloader from the autoload section in composer.json — class names map to file paths based on namespace prefixes.


// composer.json:
{
    "autoload": {
        "psr-4": {
            "App": "src/"
        }
    }
}
// AppServicesOrderService → src/Services/OrderService.php

Key Interview Takeaways

  • Traits provide multiple-inheritance-like reuse; interfaces define pure contracts; abstract classes provide partial implementation
  • PHP 8.1 enums are first-class types with methods — replace string constants
  • Fibers enable cooperative multitasking; foundation for async frameworks
  • Generators process large datasets in O(1) memory via lazy evaluation
  • OpCache is essential in production — disable validate_timestamps for performance
  • Readonly properties (PHP 8.1) enforce immutability on value objects
Scroll to Top