Interview Q&A · 102 questions · 25 answered publicly

PHP 8 Interview Questions

Ordered by how often they're asked. Sign in free to see every answer, rate your confidence and run timed mock interviews.

  1. Most asked What are the most important features introduced in PHP 8.0?

    Summary

    PHP 8.0 added the JIT compiler, union types, named arguments, attributes, constructor property promotion, the match expression, the nullsafe operator and much stricter, more consistent error handling.

    Longer Answer

    PHP 8.0 (November 2020) was a major release focused on type safety and expressiveness. The language gained union types (int|string), the mixed and static return types, named arguments, attributes (native metadata replacing docblock annotations), constructor property promotion, the match expression, and the nullsafe operator (?->).

    Under the hood it shipped the JIT (tracing and function modes on top of OPcache), turned many warnings into TypeError/ValueError exceptions for internal functions, and changed string-to-number comparison semantics so 0 == "foo" is now false.

    Key Points

    • Union types, mixed, static return type
    • Named arguments and constructor promotion reduce boilerplate
    • Attributes give first-class, reflectable metadata
    • match is a strict, expression-based switch
    • Saner string/number comparisons and consistent internal-function errors

    Permalink

  2. Most asked What are union types in PHP 8 and how do you use them?

    Summary

    Union types let a parameter, return value or property accept one of several types, written TypeA|TypeB, and are enforced by the engine at runtime.

    Longer Answer

    Before PHP 8 you could only document "int or string" in a docblock. Union types make that a real contract the engine checks. Any combination of class/interface names and scalar types is allowed, plus null (so ?Foo is shorthand for Foo|null) and the special false pseudo-type (PHP 8.2 also allows standalone true, false and null).

    Redundant types are compile-time errors (int|int), and void cannot be part of a union. Under coercive typing mode, PHP tries the types in a defined preference order (int, float, string, bool) when coercing scalars.

    Code Example

    <?php
    declare(strict_types=1);
    
    function formatId(int|string $id): string
    {
        return is_int($id) ? sprintf('#%05d', $id) : strtoupper($id);
    }
    
    echo formatId(42);      // #00042
    echo formatId('ab-1');  // AB-1
    

    Key Points

    • Syntax: int|string, Foo|Bar|null
    • ?T is equivalent to T|null
    • void can never be in a union
    • Prefer narrow unions; a wide union often signals a missing abstraction

    Permalink

  3. Most asked What are named arguments and what are their benefits and risks?

    Summary

    Named arguments let callers pass values by parameter name (fn(name: 'x')), so they can skip optional parameters and make call sites self-documenting.

    Longer Answer

    With named arguments the order no longer matters and you only pass what you need, which is ideal for functions with many optional flags like htmlspecialchars($s, double_encode: false). They can be combined with positional arguments, but positional ones must come first.

    The risk: parameter names become part of your public API. Renaming a parameter in a library is now a breaking change for callers using named arguments. Child classes that rename parameters can also cause errors when the call goes through the parent signature. You can also spread string-keyed arrays: foo(...['b' => 2]).

    Code Example

    <?php
    function createUser(string $email, bool $admin = false, bool $notify = true, ?string $locale = null) { /* ... */ }
    
    // Before: createUser('[email protected]', false, false, 'de');
    createUser('[email protected]', notify: false, locale: 'de');
    
    echo htmlspecialchars('<a>&amp;', double_encode: false);
    

    Key Points

    • Skip optional parameters without passing defaults
    • Self-documenting call sites (bool flags become readable)
    • Parameter names are now part of the public API
    • Positional arguments must precede named ones; duplicates are an Error

    Permalink

  4. Most asked What is constructor property promotion?

    Summary

    Constructor promotion lets you declare and assign class properties directly in the constructor signature by adding a visibility modifier to the parameter.

    Longer Answer

    It removes the classic three-step boilerplate (declare property, declare parameter, assign in body). A promoted parameter with public, protected or private (and optionally readonly from 8.1) automatically becomes a property initialized with the argument.

    You can mix promoted and normal parameters, and the constructor body still runs after promotion. Restrictions: no promotion in abstract constructors or interfaces, no variadic promotion, and callable cannot be a promoted type.

    Code Example

    <?php
    final class Money
    {
        public function __construct(
            public readonly int $amount,
            public readonly string $currency = 'USD',
        ) {
            if ($amount < 0) {
                throw new InvalidArgumentException('Amount must be >= 0');
            }
        }
    }
    
    $price = new Money(1999);
    echo $price->currency; // USD
    

    Key Points

    • Add a visibility modifier to promote a parameter
    • Combine with readonly for immutable value objects
    • Constructor body still runs for validation
    • Not allowed for variadic parameters or callable types

    Permalink

  5. Most asked How does `match` differ from `switch`?

    Summary

    match is an expression that returns a value, uses strict (===) comparison, has no fall-through, and throws UnhandledMatchError if no arm matches.

    Longer Answer

    switch is a statement with loose comparison (==) and implicit fall-through, which is a frequent bug source. match fixes all three: each arm is a single expression, comparison is strict, and missing cases fail loudly instead of silently doing nothing.

    Arms can have comma-separated conditions, and match (true) is a common idiom for range checks.

    Code Example

    <?php
    $status = 404;
    
    $message = match (true) {
        $status >= 500 => 'Server error',
        $status === 404, $status === 410 => 'Not found',
        $status >= 400 => 'Client error',
        default => 'OK',
    };
    
    echo $message; // Not found
    

    Key Points

    • Expression: assign or return its result
    • Strict comparison (===)
    • No fall-through, no break needed
    • Unmatched value throws UnhandledMatchError
    • Arms are single expressions (use a function for multi-statement logic)

    Comparison / Tradeoffs

    Aspect switch match
    Kind Statement Expression
    Comparison Loose == Strict ===
    Fall-through Yes (needs break) No
    No match Silently skipped UnhandledMatchError

    Permalink

  6. Most asked What are enums in PHP 8.1? Explain pure vs backed enums.

    Summary

    Enums define a closed set of named values as a first-class type. Pure enums have only cases; backed enums map each case to a scalar int or string value.

    Longer Answer

    Enums replace class constants used as pseudo-enums, giving real type safety: a parameter typed Status can only receive a valid case. Each case is a singleton object, so === comparison works.

    Backed enums (enum Status: string) give ->value, plus Status::from($v) (throws ValueError on unknown input) and Status::tryFrom($v) (returns null). They are ideal for database columns and API payloads. All enums get cases(). Enums can implement interfaces, have methods and constants, and use traits without properties, but cannot have state or be extended.

    Code Example

    <?php
    enum OrderStatus: string
    {
        case Pending = 'pending';
        case Paid = 'paid';
        case Shipped = 'shipped';
    
        public function label(): string
        {
            return match ($this) {
                self::Pending => 'Awaiting payment',
                self::Paid => 'Paid',
                self::Shipped => 'On its way',
            };
        }
    }
    
    $status = OrderStatus::from($row['status']);
    echo $status->label();
    $maybe = OrderStatus::tryFrom('unknown'); // null
    

    Key Points

    • Pure: enum Suit { case Hearts; }
    • Backed: enum Status: string { case Active = 'active'; }
    • from() throws, tryFrom() returns null
    • Cases are singletons; compare with ===
    • Enums can have methods and implement interfaces, but no properties

    Permalink

  7. Most asked Explain readonly properties (8.1) and readonly classes (8.2).

    Summary

    A readonly property can be initialized once, from inside the declaring class scope, and never modified afterwards. A readonly class makes every declared property readonly.

    Longer Answer

    Readonly properties must be typed. They are initialized once (usually in the constructor) and any further write throws an Error. Note that readonly is shallow: if the property holds an object, that object's internal state can still change.

    PHP 8.2 added readonly class, which marks all properties readonly and forbids dynamic properties. To produce a modified copy you write "withers" that construct a new instance. PHP 8.3 allowed re-initializing readonly properties during __clone(), which makes withers using clone possible.

    Code Example

    <?php
    final readonly class Email
    {
        public function __construct(public string $value)
        {
            if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                throw new InvalidArgumentException("Invalid email: $value");
            }
        }
    
        public function withValue(string $value): static
        {
            return new static($value);
        }
    }
    
    $email = new Email('[email protected]');
    // $email->value = 'x'; // Error: Cannot modify readonly property
    

    Key Points

    • Must be typed, initialized once from class scope
    • Writes after init throw Error
    • Readonly is shallow for objects
    • readonly class (8.2) applies to all properties
    • 8.3 allows reinitialization inside __clone()

    Permalink

  8. Most asked When would you use an interface, an abstract class, or a trait in PHP?

    Summary

    Use an interface to define a contract, an abstract class to share base state and partial implementation in an is-a hierarchy, and a trait to reuse code horizontally across unrelated classes.

    Longer Answer

    Interfaces declare methods (and constants, and since 8.4 properties) with no state; a class can implement many. They're the backbone of dependency inversion.

    Abstract classes can hold properties, constructors and concrete methods, but a class can extend only one. Use them when subclasses genuinely share identity and behavior (e.g., AbstractController).

    Traits copy methods/properties into a class at compile time. They're good for cross-cutting helpers but can hide coupling; they're not types, so you can't type-hint a trait.

    Key Points

    • Interface = contract, multiple allowed, type-hintable
    • Abstract class = shared state + partial implementation, single inheritance
    • Trait = compile-time code reuse, not a type
    • Prefer composition and interfaces over deep inheritance

    Comparison / Tradeoffs

    Interface Abstract class Trait
    State No (8.4: hooked props only) Yes Yes
    Multiple Yes No Yes
    Type-hintable Yes Yes No

    Permalink

  9. Most asked How does Composer autoloading work, and what is PSR-4?

    Summary

    PSR-4 maps a namespace prefix to a base directory so App\Models\User loads from src/Models/User.php. Composer generates an autoloader from composer.json that requires files on first use.

    Longer Answer

    You configure "autoload": {"psr-4": {"App\\": "src/"}} and include vendor/autoload.php. Composer registers an spl_autoload_register callback. In production, run composer dump-autoload --optimize (or --classmap-authoritative) to generate a class map and avoid filesystem lookups.

    Code Example

    {
      "autoload": {
        "psr-4": { "App\\": "src/" }
      },
      "autoload-dev": {
        "psr-4": { "Tests\\": "tests/" }
      }
    }
    

    Key Points

    • Namespace prefix → directory mapping
    • vendor/autoload.php registers the loader
    • --optimize/-o builds a classmap for production
    • --classmap-authoritative skips filesystem fallback entirely

    Permalink

  10. Most asked How do you prevent SQL injection in PHP?

    Summary

    Use prepared statements with bound parameters (PDO or mysqli), never concatenate user input into SQL, and whitelist anything that can't be parameterized such as column names or sort direction.

    Longer Answer

    With PDO, set PDO::ATTR_EMULATE_PREPARES => false (for MySQL) so real server-side prepares are used, and PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION. Identifiers (table/column names, ORDER BY fields) can't be bound, so validate them against an allow-list. ORMs and query builders parameterize for you, but raw expressions (DB::raw, whereRaw) reintroduce risk.

    Code Example

    <?php
    $pdo = new PDO($dsn, $user, $pass, [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]);
    
    $sort = in_array($_GET['sort'] ?? '', ['name', 'created_at'], true) ? $_GET['sort'] : 'name';
    $stmt = $pdo->prepare("SELECT id, name FROM users WHERE status = :status ORDER BY $sort");
    $stmt->execute(['status' => $_GET['status'] ?? 'active']);
    $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
    

    Key Points

    • Prepared statements for all values
    • Allow-list identifiers and sort directions
    • Exceptions error mode; disable emulated prepares where practical
    • Least-privilege DB users

    Permalink

  11. Most asked How do you prevent XSS in PHP applications?

    Summary

    Escape output for its context: htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') for HTML, json_encode with hex flags for JS, rawurlencode for URLs. Add a Content-Security-Policy as defense in depth.

    Longer Answer

    XSS is an output problem: you escape at render time based on where data lands. Templating engines (Twig, Blade {{ }}) auto-escape HTML by default; raw output ({!! !!}, |raw) must be reserved for sanitized HTML (use HTML Purifier for user-supplied rich text). Set cookies HttpOnly so scripts can't read session IDs.

    Code Example

    <?php
    function e(?string $value): string
    {
        return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }
    ?>
    <p>Hello, <?= e($user->name) ?></p>
    <script>const user = <?= json_encode($user, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;</script>
    

    Key Points

    • Escape on output, per context
    • Use auto-escaping templates
    • Sanitize rich HTML with HTML Purifier
    • CSP and HttpOnly cookies limit impact

    Permalink

  12. Most asked How do you protect a PHP app against CSRF and session attacks?

    Summary

    Use per-session CSRF tokens on state-changing requests, SameSite cookies, session_regenerate_id(true) on login, and secure cookie flags (Secure, HttpOnly).

    Longer Answer

    CSRF: generate bin2hex(random_bytes(32)), store it in the session, embed it in forms, and compare with hash_equals(). SameSite=Lax blocks most cross-site POSTs. Session fixation: regenerate the ID after authentication or privilege change. Also set session.use_strict_mode=1 and session.cookie_secure=1, and expire idle sessions server-side.

    Code Example

    <?php
    session_set_cookie_params(['secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
    session_start();
    $_SESSION['csrf'] ??= bin2hex(random_bytes(32));
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST'
        && !hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
        http_response_code(419);
        exit('Invalid CSRF token');
    }
    

    Key Points

    • Token + hash_equals() for CSRF
    • SameSite=Lax/Strict cookies
    • session_regenerate_id(true) on login
    • session.use_strict_mode=1

    Permalink

  13. Most asked How should passwords be stored in PHP?

    Summary

    Use password_hash() (bcrypt by default, or Argon2id) and password_verify(), and call password_needs_rehash() on login to upgrade old hashes.

    Longer Answer

    These functions handle salt generation and embed algorithm and cost in the hash string. Never use md5, sha1 or unsalted hash(). Bcrypt truncates input at 72 bytes; Argon2id (PASSWORD_ARGON2ID) is memory-hard and recommended where available.

    Code Example

    <?php
    $hash = password_hash($password, PASSWORD_ARGON2ID);
    
    if (password_verify($input, $hash)) {
        if (password_needs_rehash($hash, PASSWORD_ARGON2ID)) {
            $hash = password_hash($input, PASSWORD_ARGON2ID);
            // save new hash
        }
    }
    

    Key Points

    • password_hash($pw, PASSWORD_DEFAULT)
    • password_verify() is timing-safe
    • Rehash on login when cost/algorithm changes
    • Never use fast hashes like MD5/SHA1 for passwords

    Permalink

  14. Most asked What is OPcache and how should it be configured in production?

    Summary

    OPcache stores compiled opcodes in shared memory so PHP doesn't re-parse scripts on every request. It's the single most important PHP performance setting.

    Longer Answer

    Recommended production settings: opcache.enable=1, enough opcache.memory_consumption (e.g., 256M) and opcache.max_accelerated_files for your file count, and opcache.validate_timestamps=0 so PHP never stats files, which means you must reset OPcache (reload FPM) on deploy. opcache.interned_strings_buffer saves memory for repeated strings.

    Code Example

    opcache.enable=1
    opcache.memory_consumption=256
    opcache.interned_strings_buffer=16
    opcache.max_accelerated_files=20000
    opcache.validate_timestamps=0
    

    Key Points

    • Caches compiled opcodes in shared memory
    • validate_timestamps=0 in prod + reload on deploy
    • Size memory and max files for your codebase
    • Monitor hit rate with opcache_get_status()

    Permalink

  15. Most asked What happens to an HTTP request in a PHP-FPM application, and what does 'shared nothing' mean?

    Summary

    nginx passes the request to PHP-FPM over FastCGI; the master assigns it to an idle worker process; the worker runs the entry script (public/index.php) from the top: autoloading, framework bootstrap, routing, controller, response; then all request memory (variables, objects, static properties) is discarded. Nothing in application memory survives to the next request, only OPcache's compiled code, caches, sessions and databases.

    Longer Answer

    Each worker handles one request at a time, so capacity is workers divided by request time: slow outbound calls exhaust the pool (502/504) even when CPU is idle. Long-running runtimes (Octane, RoadRunner, FrankenPHP worker mode, Swoole) avoid repeated bootstrap but give up shared-nothing isolation, so request state can leak between requests.

    Key Points

    • FastCGI → worker → script runs from the top.
    • All request memory discarded afterwards.
    • One request per worker; capacity = workers ÷ time.
    • Long-running runtimes trade isolation for speed.

    Deep dive: Request Lifecycle course: PHP-FPM and the Shared-Nothing Model

    Permalink

  16. Most asked What does the nullsafe operator `?->` do?

    Summary

    ?-> short-circuits a method or property chain and returns null as soon as the left-hand side is null, instead of throwing an error.

    Longer Answer

    It replaces nested null checks such as $user !== null && $user->getAddress() !== null ? ... : null. When any element in the chain evaluates to null, the rest of the chain is skipped, including argument evaluation.

    It is read-only: you cannot use it on the left of an assignment ($a?->b = 1 is a compile error), and it cannot be used with references.

    Code Example

    <?php
    $country = $session?->user?->getAddress()?->country ?? 'Unknown';
    

    Key Points

    • Returns null instead of erroring on a null receiver
    • Short-circuits the entire remaining chain
    • Not allowed in write context
    • Pairs well with ?? to supply a default

    Permalink

  17. Most asked How does the PHP 8 JIT work and when does it improve performance?

    Summary

    The JIT compiles hot opcodes to native machine code at runtime on top of OPcache. It greatly speeds up CPU-bound work (math, image processing), but gives little benefit to typical I/O-bound web apps.

    Longer Answer

    PHP normally compiles source to opcodes (cached by OPcache) that the Zend VM interprets. The JIT, built on DynASM, adds a further step: it compiles frequently executed code to native machine code. The tracing JIT (opcache.jit=tracing, default mode when enabled) profiles hot loops and functions; the function JIT compiles whole functions.

    Most web requests spend their time waiting on databases, network and I/O, so benchmarks like WordPress or Laravel see small gains. Fractals, ML in PHP and long-running workers benefit far more. PHP 8.4 moved to a new IR-based JIT backend and changed the configuration defaults (opcache.jit=disable with a non-zero buffer).

    Code Example

    ; php.ini
    opcache.enable=1
    opcache.enable_cli=1
    opcache.jit_buffer_size=128M
    opcache.jit=tracing
    

    Key Points

    • Requires OPcache; enable via opcache.jit_buffer_size and opcache.jit
    • Tracing JIT targets hot paths
    • Large gains for CPU-bound code, small for I/O-bound apps
    • Measure with real workloads before enabling in production

    Permalink

  18. Most asked What changed in PHP 8 error handling for internal functions?

    Summary

    Many internal functions now throw TypeError or ValueError instead of emitting warnings and returning null/false, and many notices were promoted to warnings or Error exceptions.

    Longer Answer

    In PHP 7, strlen([]) emitted a warning and returned null. In PHP 8 it throws TypeError. Invalid values (e.g. array_fill(0, -1, 'x')) throw ValueError. Accessing undefined variables went from notice to warning, and attempting to read a property of a non-object is now a warning; writing a property on null throws Error.

    The @ operator no longer silences fatal errors, and the default error_reporting is E_ALL. This makes bugs surface earlier, but legacy code often hits new exceptions after upgrading.

    Key Points

    • TypeError for wrong argument types in internals
    • ValueError for valid type but invalid value
    • @ no longer suppresses fatal errors
    • Default error_reporting is E_ALL

    Permalink

  19. Most asked How did string-to-number comparison change in PHP 8?

    Summary

    When comparing a number with a non-numeric string, PHP 8 now compares them as strings instead of casting the string to a number, so 0 == 'foo' is false (it was true in PHP 7).

    Longer Answer

    PHP 7 cast 'foo' to 0, making 0 == 'foo' true, a classic source of security bugs (e.g. in loose in_array or switch). PHP 8's "Saner string to number comparisons" RFC: if the string is numeric, compare as numbers; otherwise cast the number to a string and compare strings.

    Also, PHP 8 treats leading-numeric strings like '42abc' more consistently ("Saner numeric strings" RFC): arithmetic on them gives a warning instead of a notice, and non-numeric strings in arithmetic throw TypeError.

    Key Points

    • 0 == 'foo': PHP 7 true, PHP 8 false
    • '1' == '01' still true (both numeric)
    • Affects switch, in_array and array_search without strict mode
    • Prefer === and strict flags anyway

    Comparison / Tradeoffs

    Expression PHP 7 PHP 8
    0 == 'foo' true false
    '1' == '01' true true
    100 == '1e2' true true
    null == false true true

    Permalink

  20. Most asked Compare `??`, `?:`, and `?->`.

    Summary

    ?? returns the left operand unless it is null/undefined, ?: returns the left operand unless it is falsy, and ?-> safely calls a method or property on a possibly-null object.

    Longer Answer

    $a ?? $b doesn't emit a warning for undefined variables or keys, making it ideal for $_GET['x'] ?? 'default'. $a ?: $b treats 0, '', '0', [] and false as missing, which is often a bug for numeric input. ?-> chains through nulls. ??= (PHP 7.4) assigns only when null.

    Key Points

    • ?? checks null/undefined only
    • ?: checks falsiness (beware 0 and '0')
    • ?-> short-circuits on null in chains
    • ??= assigns if null

    Comparison / Tradeoffs

    Operator Falls back when Example
    ?? null or undefined $_GET['page'] ?? 1
    ?: falsy $name ?: 'Anonymous'
    ?-> receiver is null $user?->profile

    Permalink

  21. Most asked What is late static binding?

    Summary

    Late static binding means static:: resolves to the class that was actually called at runtime, whereas self:: always resolves to the class where the method is written.

    Longer Answer

    It enables inheritable static factories and active-record patterns: User::create() defined in Model can instantiate User via new static(). parent:: still refers to the direct parent.

    Code Example

    <?php
    class Model {
        public static function table(): string { return static::TABLE; }
    }
    class User extends Model { const TABLE = 'users'; }
    
    echo User::table(); // users
    

    Key Points

    • self:: = defining class (compile time)
    • static:: = called class (runtime)
    • Use new static() for inheritable factories

    Permalink

  22. Most asked Explain the SOLID principles with PHP examples.

    Summary

    SOLID stands for Single responsibility, Open/closed, Liskov substitution, Interface segregation and Dependency inversion, five guidelines for maintainable OO design.

    Longer Answer

    • S: a class has one reason to change (an InvoiceMailer doesn't calculate totals).
    • O: extend behavior via new classes, e.g. add a StripeGateway implementing PaymentGateway, without editing callers.
    • L: subtypes must honour parent contracts. A ReadOnlyRepository that throws on save() violates it.
    • I: prefer small interfaces (Readable, Writable) over one fat one.
    • D: depend on abstractions; inject PaymentGateway rather than new Stripe() inside the service.

    Code Example

    <?php
    interface PaymentGateway { public function charge(Money $amount, string $token): string; }
    
    final class CheckoutService
    {
        public function __construct(private PaymentGateway $gateway) {} // DIP
    
        public function pay(Order $order, string $token): string
        {
            return $this->gateway->charge($order->total(), $token);
        }
    }
    

    Key Points

    • Small focused classes
    • Polymorphism over switch on types
    • Honor parent contracts
    • Small interfaces
    • Constructor injection of abstractions

    Permalink

  23. Most asked What is dependency injection and how does a DI container help?

    Summary

    Dependency injection means a class receives its collaborators from outside (usually via the constructor) instead of creating them. A container (PSR-11) builds and wires those object graphs automatically.

    Longer Answer

    DI makes classes testable (inject fakes) and decoupled. A container such as Symfony DI, Laravel's container or PHP-DI uses reflection/autowiring to resolve constructor type hints, manages lifetimes (shared vs new instance), and binds interfaces to implementations.

    Avoid the service locator anti-pattern (pulling $container->get() inside business classes): it hides dependencies.

    Key Points

    • Constructor injection is preferred
    • Container autowires via reflection
    • PSR-11 standardizes get()/has()
    • Service locator usage hides dependencies

    Permalink

  24. Most asked What does `declare(strict_types=1)` do?

    Summary

    It makes scalar type declarations strict for calls made from that file, so passing '5' to an int parameter throws TypeError instead of being coerced.

    Longer Answer

    It is per-file and applies to the caller file: calls originating in a strict file are checked strictly, even into non-strict files. Return types are checked based on the file where the function is defined. The only allowed implicit conversion is int → float.

    Key Points

    • Per-file, must be the first statement
    • Affects calls made from that file
    • Only int → float widening is allowed
    • Recommended for all new code

    Permalink

  25. Most asked What is the N+1 query problem and how do you fix it in PHP ORMs?

    Summary

    N+1 happens when you load N records and then lazily query a relation for each one, producing N extra queries. Fix it with eager loading (with() in Eloquent, JOIN FETCH/addSelect in Doctrine) or batched queries.

    Longer Answer

    Example: listing 50 posts and calling $post->author->name triggers 51 queries. In Laravel use Post::with('author')->get(); enable Model::preventLazyLoading() in development to catch it. In Doctrine, use DQL with JOIN ... SELECT or batch fetching. Tools: Laravel Debugbar, Symfony Profiler, query logs.

    Key Points

    • Eager load relations you will access
    • Detect with profiler or preventLazyLoading()
    • Select only needed columns
    • Paginate large lists

    Permalink

  26. What are intersection types (PHP 8.1) and when are they useful?
  27. What are attributes in PHP 8 and how are they read?
  28. What is the `never` return type?
  29. What is first-class callable syntax in PHP 8.1?
  30. What are `str_contains`, `str_starts_with` and `str_ends_with`?
  31. What is the `Stringable` interface?
  32. What did PHP 8.3 add for class constants and overriding?
  33. What are property hooks in PHP 8.4?
  34. Why were dynamic properties deprecated in PHP 8.2 and how do you handle it?
  35. What is the Random extension added in PHP 8.2?
  36. Explain covariance and contravariance in PHP method signatures.
  37. Which magic methods matter most in modern PHP?
  38. What is the difference between `composer install` and `composer update`, and why commit `composer.lock`?
  39. Which PSRs should a PHP developer know?
  40. How does PSR-15 middleware work?
  41. How do you handle file uploads securely in PHP?
  42. Why is `unserialize()` on user input dangerous?
  43. How do generators help with memory usage?
  44. How does PHP handle copying arrays and objects (copy-on-write)?
  45. Describe PHP's exception hierarchy and good error-handling practices.
  46. Why prefer `DateTimeImmutable` over `DateTime`?
  47. Which SPL data structures and interfaces are useful in practice?
  48. How do you tune PHP-FPM process management?
  49. How do PHPStan/Psalm and Rector improve a PHP codebase?
  50. How do you write unit tests in PHP with PHPUnit or Pest?
  51. How do you use transactions safely with PDO?
  52. How would you add caching to a PHP application?
  53. Explain the Repository and Service layer patterns in PHP apps.
  54. What are Value Objects and DTOs, and how do they differ?
  55. How do you build a robust JSON API endpoint in plain PHP 8?
  56. How do you run background jobs in PHP?
  57. How would you diagnose a slow PHP endpoint?
  58. How would you plan an upgrade from PHP 7.4 to PHP 8.3+?
  59. Compare Laravel and Symfony from an architecture standpoint.
  60. Which php.ini and HTTP settings harden a production PHP app?
  61. Explain Symfony's HttpKernel request lifecycle and its kernel events.
  62. What are Disjunctive Normal Form (DNF) types in PHP 8.2?
  63. What is the `mixed` type and when should you avoid it?
  64. What does the `static` return type mean?
  65. What are Fibers in PHP 8.1 and how do they relate to async PHP?
  66. What is a WeakMap and when would you use it?
  67. What is the `throw` expression in PHP 8?
  68. What does `$object::class` do in PHP 8?
  69. What does PHP 8.1's `new` in initializers allow?
  70. How did array unpacking change in PHP 8.1?
  71. What does `array_is_list()` check?
  72. What is asymmetric visibility in PHP 8.4?
  73. What else changed in PHP 8.4 besides property hooks?
  74. What is the pipe operator in PHP 8.5?
  75. What is the `#[\SensitiveParameter]` attribute for?
  76. How do you resolve method conflicts between two traits?
  77. Explain closures, `use`, arrow functions and `Closure::bind`.
  78. How does PHP's garbage collection work?
  79. How do you handle Unicode strings correctly in PHP?
  80. What is OPcache preloading (PHP 7.4+)?
  81. Compare PHP-FPM with long-running runtimes like Swoole, RoadRunner and FrankenPHP.
  82. How do you handle PHP sessions across multiple servers?
  83. How would you implement the Strategy pattern in modern PHP?
  84. When are anonymous classes useful?
  85. How do namespaces and `use` imports work in PHP?
  86. How do `ArrayAccess`, `IteratorAggregate` and `Countable` help build collection classes?
  87. How do you make HTTP requests from PHP reliably?
  88. How do you implement logging and observability in PHP?
  89. How do you containerize a PHP 8 application for production?
  90. What are PHP superglobals and why shouldn't business code use them directly?
  91. What are common pitfalls with `preg_*` functions?
  92. Why are static properties and singletons discouraged?
  93. How do you write robust PHP CLI scripts?
  94. Describe how PHP executes a script internally.
  95. Which array functions should every PHP developer know well?
  96. How do you control how objects are serialized to JSON?
  97. What options exist for concurrency or parallelism in PHP?
  98. How should a PHP app send email reliably?
  99. Why does running a supported PHP version matter?
  100. How would you structure a large PHP application using hexagonal architecture?
  101. How would you implement rate limiting for a PHP API?
  102. What is the difference between `include`, `require`, `include_once` and `require_once`?