Interview Prep Hub

PHP Core

PHP powers over 75% of the web. Whether you're working with WordPress, Laravel, or custom backends, interviewers expect solid fundamentals — types, OOP, error handling, security, and modern PHP 8.x features.

PHP Fundamentals

PHP is a server-side, dynamically typed scripting language. Code runs on the server and sends HTML to the browser — the client never sees your PHP code.

How PHP Executes

  1. Lexing/Tokenizing: PHP source code is broken into tokens.
  2. Parsing: Tokens are transformed into an Abstract Syntax Tree (AST).
  3. Compilation: AST is compiled to opcodes (bytecode).
  4. Execution: The Zend Engine executes opcodes.

OPcache: In production, OPcache stores compiled opcodes in shared memory so PHP skips steps 1-3 on subsequent requests. This alone can improve performance by 3-5x. Always enable it.

Variables & Types

// PHP is dynamically typed — variables don't need type declarations
$name = "Arvind";          // string
$age = 28;                 // int
$salary = 75000.50;        // float
$isActive = true;          // bool
$skills = ["Go", "PHP"];   // array
$nothing = null;           // null

// Type juggling (implicit conversion) — a common interview gotcha
var_dump(0 == "foo");      // true in PHP 7 (loose comparison), false in PHP 8
var_dump(0 === "foo");     // false (strict comparison — ALWAYS use ===)

// Type casting
$num = (int) "42abc";      // 42
$str = (string) 3.14;     // "3.14"
$bool = (bool) "";         // false
$bool = (bool) "0";        // false (special case!)

// Type declarations (PHP 7+)
function add(int $a, int $b): int {
    return $a + $b;
}

// Union types (PHP 8.0+)
function getId(): int|string {
    return random_int(0, 1) ? 42 : "abc-123";
}

// Intersection types (PHP 8.1+)
function process(Countable&Iterator $collection): void {
    // Must implement both interfaces
}

// Nullable types
function findUser(int $id): ?User {
    return $this->repository->find($id); // returns User or null
}

Strings In Depth

// Single quotes — no variable interpolation, faster
$greeting = 'Hello, World!';

// Double quotes — supports interpolation and escape sequences
$name = "Arvind";
$msg = "Hello, $name!\n";          // Hello, Arvind! + newline
$msg = "Score: {$user->score}";    // complex expressions need braces

// Heredoc (like double quotes, multiline)
$html = <<<HTML
<div class="card">
    <h2>{$title}</h2>
    <p>{$description}</p>
</div>
HTML;

// Nowdoc (like single quotes, multiline, no interpolation)
$template = <<<'EOT'
This is raw text. $variables are NOT interpolated here.
EOT;

// Important string functions
strlen($str);                       // byte length (NOT character count for UTF-8!)
mb_strlen($str);                   // character count (multibyte safe)
strpos($haystack, $needle);       // find position (returns false if not found — use ===)
str_contains($str, "search");     // PHP 8.0+ (cleaner than strpos)
str_starts_with($str, "Hello");   // PHP 8.0+
str_ends_with($str, "World");     // PHP 8.0+
explode(",", "a,b,c");            // split → ["a", "b", "c"]
implode("-", ["a", "b", "c"]);    // join → "a-b-c"
strtolower($str);
trim($str);
sprintf("User %s (ID: %d)", $name, $id);  // formatted strings

Arrays — The Swiss Army Knife

PHP arrays are actually ordered hash maps. They can serve as arrays, lists, dictionaries, stacks, queues, and more.

// Indexed array
$fruits = ["apple", "banana", "cherry"];

// Associative array (dictionary/map)
$user = [
    "name"  => "Arvind",
    "email" => "arvind@example.com",
    "age"   => 28,
];

// Nested arrays
$config = [
    "database" => [
        "host" => "localhost",
        "port" => 5432,
    ],
];

// Common operations
count($arr);                    // length
in_array("apple", $fruits);    // search (O(n) — use array_flip for O(1))
array_key_exists("name", $user); // check key
array_push($fruits, "date");   // append (or: $fruits[] = "date")
array_merge($arr1, $arr2);     // merge
array_filter($arr, fn($v) => $v > 10);  // filter
array_map(fn($v) => $v * 2, $arr);      // map
array_reduce($arr, fn($carry, $v) => $carry + $v, 0); // reduce

// Destructuring (PHP 7.1+)
[$first, $second] = $fruits;
["name" => $name, "age" => $age] = $user;

// Spread operator (PHP 7.4+)
$merged = [...$arr1, ...$arr2];

// Sorting
sort($arr);              // by value, re-indexes
asort($arr);             // by value, preserves keys
ksort($arr);             // by key
usort($arr, fn($a, $b) => $a->age <=> $b->age); // custom comparator

Object-Oriented PHP

Modern PHP is heavily OOP. Laravel, Symfony, and WordPress (since Gutenberg) all rely on solid OOP patterns.

class User {
    // Visibility: public, protected, private
    private int $id;
    public string $name;
    protected string $email;
    
    // Constructor Property Promotion (PHP 8.0+ — massive boilerplate reduction)
    public function __construct(
        private int $id,
        public string $name,
        protected string $email,
        public readonly string $role = 'viewer', // readonly (PHP 8.1+)
    ) {}
    
    public function getId(): int {
        return $this->id;
    }
    
    // Magic methods
    public function __toString(): string {
        return "{$this->name} ({$this->email})";
    }
}

// Inheritance
class Admin extends User {
    public function __construct(
        int $id,
        string $name,
        string $email,
        private array $permissions = [],
    ) {
        parent::__construct($id, $name, $email, 'admin');
    }
}

// Abstract classes
abstract class Shape {
    abstract public function area(): float;
    
    // Concrete method shared by all subclasses
    public function describe(): string {
        return "Area: " . $this->area();
    }
}

// Interfaces
interface Cacheable {
    public function getCacheKey(): string;
    public function getCacheTTL(): int;
}

interface Serializable {
    public function toArray(): array;
}

// A class can implement multiple interfaces (but extend only one class)
class Product extends Model implements Cacheable, Serializable {
    public function getCacheKey(): string {
        return "product:{$this->id}";
    }
    public function getCacheTTL(): int { return 3600; }
    public function toArray(): array { return [...]; }
}

// Traits (horizontal code reuse — PHP's answer to multiple inheritance)
trait Timestamps {
    public DateTime $createdAt;
    public DateTime $updatedAt;
    
    public function touch(): void {
        $this->updatedAt = new DateTime();
    }
}

class Post {
    use Timestamps; // "mix in" the trait
}

// Enums (PHP 8.1+)
enum Status: string {
    case Active = 'active';
    case Inactive = 'inactive';
    case Suspended = 'suspended';
    
    public function label(): string {
        return match($this) {
            self::Active => 'Active',
            self::Inactive => 'Inactive',
            self::Suspended => 'Suspended',
        };
    }
}
// Usage: $user->status = Status::Active;

Static vs Instance

class Database {
    private static ?Database $instance = null;
    
    private function __construct(private PDO $pdo) {}
    
    // Singleton pattern (common interview question)
    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self(
                new PDO('pgsql:host=localhost;dbname=app', 'user', 'pass')
            );
        }
        return self::$instance;
    }
    
    // Static methods don't need an instance
    public static function table(string $name): QueryBuilder {
        return new QueryBuilder($name);
    }
    
    // Late static binding (self vs static)
    public static function create(): static {
        return new static(); // resolves to the *calling* class, not the defining class
    }
}

Error Handling

// Try-Catch (PHP 5+)
try {
    $result = riskyOperation();
} catch (InvalidArgumentException $e) {
    // Catch specific exception type
    log($e->getMessage());
} catch (RuntimeException | LogicException $e) {
    // Catch multiple types (PHP 8.0+: non-capturing catch)
    handleError($e);
} catch (Throwable $e) {
    // Catches everything — exceptions AND errors
    reportToSentry($e);
} finally {
    // Always runs, regardless of exception
    cleanup();
}

// Custom exceptions
class InsufficientBalanceException extends DomainException {
    public function __construct(
        public readonly float $balance,
        public readonly float $amount,
    ) {
        parent::__construct(
            "Cannot debit \$" . $amount . " from balance \$" . $balance
        );
    }
}

// Throwing
function withdraw(float $amount): void {
    if ($this->balance < $amount) {
        throw new InsufficientBalanceException($this->balance, $amount);
    }
    $this->balance -= $amount;
}

// PHP 8.0: throw is now an expression
$user = $this->findUser($id) ?? throw new UserNotFoundException($id);

Security — Critical for Interviews

PHP apps are historically targeted. Know these cold.

  • SQL Injection: NEVER concatenate user input into queries. Use prepared statements.
    // BAD — SQL Injection vulnerability
    $query = "SELECT * FROM users WHERE id = " . $_GET['id'];
    
    // GOOD — Prepared statement (parameterized query)
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
    $stmt->execute(['id' => $_GET['id']]);
  • XSS (Cross-Site Scripting): Always escape output. htmlspecialchars($input, ENT_QUOTES, 'UTF-8'). Templating engines (Blade, Twig) auto-escape with {{ $var }}.
  • CSRF (Cross-Site Request Forgery): Include a unique token in every form. Validate it on the server. Laravel does this automatically with @csrf.
  • Password Hashing: Use password_hash($password, PASSWORD_BCRYPT) and password_verify($input, $hash). NEVER use MD5 or SHA1.
  • File Uploads: Validate MIME type server-side (not just the extension), limit file size, store outside webroot, generate random filenames.

Database Access (PDO)

PDO (PHP Data Objects) is the standard database abstraction layer. It works with PostgreSQL, MySQL, SQLite, and more.

// Connection
$pdo = new PDO(
    'pgsql:host=localhost;dbname=myapp;port=5432',
    'username',
    'password',
    [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false, // Use real prepared statements
    ]
);

// Prepared statement with named parameters
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email AND active = :active");
$stmt->execute(['email' => $email, 'active' => true]);
$user = $stmt->fetch(); // single row
$users = $stmt->fetchAll(); // all rows

// Insert and get last ID
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (:name, :email)");
$stmt->execute(['name' => 'Arvind', 'email' => 'a@b.com']);
$newId = $pdo->lastInsertId();

// Transaction
try {
    $pdo->beginTransaction();
    $pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
    $pdo->exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    throw $e;
}

Modern PHP 8.x Features

If you say "I know PHP" but can't discuss PHP 8 features, it signals outdated knowledge.

// Named arguments (PHP 8.0)
htmlspecialchars($string, double_encode: false);
// No need to remember parameter order

// Match expression (PHP 8.0) — strict comparison, returns a value
$result = match($status) {
    'active'    => 'User is active',
    'suspended' => 'User is suspended',
    default     => 'Unknown status',
};
// Unlike switch: no fall-through, strict comparison, and it's an expression

// Nullsafe operator (PHP 8.0)
$country = $user?->getAddress()?->getCountry()?->getName();
// Returns null if any step is null (no more nested if-checks)

// Fibers (PHP 8.1) — cooperative multitasking
$fiber = new Fiber(function (): void {
    $value = Fiber::suspend('fiber started');
    echo "Value used: $value";
});
$result = $fiber->start();  // "fiber started"
$fiber->resume('hello');    // "Value used: hello"

// First-class callable syntax (PHP 8.1)
$fn = strlen(...);
array_map(strlen(...), ['hello', 'world']);

// Readonly properties (PHP 8.1) & classes (PHP 8.2)
readonly class Point {
    public function __construct(
        public float $x,
        public float $y,
    ) {}
}
// $point->x = 10; // Error: Cannot modify readonly property

// Disjunctive Normal Form types (PHP 8.2)
function process((Countable&Iterator)|null $input): void { }

// Typed class constants (PHP 8.3)
class Config {
    const string APP_NAME = 'MyApp';
    const int MAX_RETRIES = 3;
}

Design Patterns in PHP

Interviewers love asking about patterns. Know at least these four:

  • Repository Pattern: Abstracts database access behind an interface. The service layer calls UserRepository::findById() instead of writing SQL. Makes it testable — inject a mock repository in tests.
  • Strategy Pattern: Define a family of algorithms (e.g., payment gateways: Stripe, Razorpay) behind an interface. Swap implementations at runtime without changing the calling code.
  • Observer Pattern: When an event happens (UserRegistered), notify all subscribers (SendWelcomeEmail, CreateDefaultSettings). Laravel Events are exactly this.
  • Factory Pattern: Encapsulate object creation logic. Instead of new StripePayment(), call PaymentFactory::create('stripe'). Decouples the client from concrete classes.

Performance Optimization

  • OPcache: Caches compiled bytecode. Must be enabled in production. Check with opcache_get_status().
  • JIT Compiler (PHP 8.0+): Compiles opcodes to native machine code. Significant gains for CPU-intensive tasks (math, image processing). Minimal improvement for typical I/O-bound web apps.
  • Preloading (PHP 7.4+): Load framework files into memory at server startup, shared across all requests. Speeds up frameworks like Laravel/Symfony.
  • Connection Pooling: PHP's shared-nothing architecture creates a new DB connection per request. Use persistent connections (PDO::ATTR_PERSISTENT) or an external pooler (PgBouncer).
  • Avoid N+1 queries: Same as in any language. Use eager loading (with() in Eloquent) or batch queries.
  • Profile with Xdebug or Blackfire: Never guess at bottlenecks — measure.

PHP vs Node.js — Common Interview Comparison

AspectPHPNode.js
Execution ModelShared-nothing (each request is isolated)Event loop (single process, shared state)
ConcurrencyMulti-process (php-fpm workers)Non-blocking async I/O
Memory LeaksImpossible — memory is freed after each requestCommon — long-running process accumulates state
EcosystemComposer (packagist.org)npm (npmjs.com)
Best ForCMS, traditional web apps, shared hostingReal-time apps, streaming, microservices
HostingRuns everywhere (cheapest shared hosting)Requires Node.js runtime (VPS/containers)

Interview Quick Reference

TopicKey Points to Mention
Type SystemDynamic but supports strict typing (declare(strict_types=1)). Union types, intersection types, enums (PHP 8.1+).
OOPInterfaces, abstract classes, traits for horizontal reuse. Constructor promotion (8.0). Readonly properties (8.1).
SecurityPrepared statements (PDO), htmlspecialchars for XSS, password_hash/verify, CSRF tokens.
PerformanceOPcache (always on), JIT compiler (8.0+), preloading (7.4+), php-fpm worker tuning.
Modern PHPNamed arguments, match expression, nullsafe operator, fibers, enums, readonly classes.
Error Handlingtry/catch with Throwable hierarchy. Custom exceptions. Non-capturing catches (8.0).