# PHP Interview Questions: 15 for Hiring Managers, Scored

> Useful PHP interview questions test understanding rather than memory: loose versus strict comparison, enums and readonly classes, PHP 8.3 and 8.4 features, safe database access, sessions, Composer, testing and debugging. Show candidates real code, ask why rather than what, and score every interview against the same sheet so you can compare candidates fairly.

- URL: https://php-entwickler-gesucht.de/en/assessing-candidates/php-interview-questions/
- Language: en
- Updated: 2026-10-19
- Publisher: PHP-Entwickler gesucht (OnlineBuilders LLC, https://onlinebuilders.biz)

According to the JetBrains State of PHP 2025 survey, 32 percent of PHP developers don't write tests at all, and 42 percent use no code quality tool whatsoever, not even PHPStan or a code style fixer. Those aren't fringe groups. Each is somewhere between a third and two-fifths of the market.

That shapes how you interview. The biggest differences between PHP candidates usually aren't about syntax. They're about how someone works. The questions below are chosen to show both.

## How to structure the interview

Good technical interviews follow a fixed script. Only then can you compare two candidates fairly. Improvised questions end up measuring how much you liked the person.

**A 75-minute PHP technical interview**

1. **5 minutes** Introduce the product, stack and role.
2. **20 minutes** Walk through the take-home task; ask for the reasoning behind decisions.
3. **25 minutes** Language, database and security questions using real code.
4. **15 minutes** How they work: tests, debugging, deploys, a real incident.
5. **10 minutes** The candidate's questions.

The take-home task up front saves a lot of interview time. Our guide to the [PHP coding test](https://php-entwickler-gesucht.de/en/assessing-candidates/php-coding-test/) explains what to set and why you should pay for it.

## Language fundamentals: four questions

### 1. What does this print, and why?

```php
$ids = ['7', '08', '1e1'];

var_dump(in_array(10, $ids));          // bool(true)
var_dump(in_array(8, $ids));           // bool(true)
var_dump(in_array(8, $ids, true));     // bool(false)
var_dump(array_search('7', [7, '7'])); // int(0)
```

The strong answer explains that `in_array()` and `array_search()` compare loosely by default, that numeric strings such as `'08'` and `'1e1'` are compared as numbers, and that the third argument switches to strict comparison. Bonus points for mentioning that PHP 8 changed how numbers compare with non-numeric strings, so `0 == 'abc'` is now false.

Then ask where this has bitten them. Someone who's chased a permissions bug caused by a loose `in_array()` won't forget the third parameter again.

### 2. What happens on the last line?

```php
declare(strict_types=1);

enum Currency: string
{
    case EUR = 'EUR';
    case GBP = 'GBP';
    case USD = 'USD';
}

final readonly class Money
{
    public function __construct(
        public int $minorUnits,
        public Currency $currency,
    ) {}

    public function add(Money $other): self
    {
        if ($other->currency !== $this->currency) {
            throw new LogicException('Currency mismatch');
        }

        return new self($this->minorUnits + $other->minorUnits, $this->currency);
    }
}

$price = new Money(1999, Currency::GBP);
$price->minorUnits = 2499;
```

It throws an `Error`: readonly properties can't be modified after initialisation. Follow up with two questions. Why store money as an integer in minor units rather than a float? And what's the difference between `Currency::from('JPY')` and `Currency::tryFrom('JPY')`? The first throws a `ValueError`, the second returns `null`. People who've built billing code answer both without hesitating.

### 3. What does `declare(strict_types=1)` actually change?

Scalar arguments are no longer silently coerced, so passing `"5"` to an `int` parameter throws a `TypeError`. Good candidates add that the setting applies per file and affects calls made from that file, not calls into it. That detail trips up plenty of experienced people, so don't fail anyone over it. Just note who knows.

### 4. Why prefer `match` over `switch`?

`match` compares strictly, returns a value, needs no `break` and throws an `UnhandledMatchError` when nothing matches. With enum-heavy code that last point is the real win: add a new case, forget to handle it, and you find out immediately.

## Modern PHP: three questions on 8.3 to 8.5

JetBrains found 89 percent of respondents on PHP 8.x. Newer features show whether someone follows the language. Nobody has to know everything, but curiosity is a good signal.

### 5. What's new here, and what does it protect against?

```php
interface RateLimited
{
    const int PER_MINUTE = 60;

    public function limiterKey(): string;
}

final class PartnerApiClient implements RateLimited
{
    const int PER_MINUTE = 120;

    #[\Override]
    public function limiterKey(): string
    {
        return 'partner-api';
    }
}
```

Two PHP 8.3 features: typed class constants and the `#[\Override]` attribute. The typed constant stops a subclass from quietly redefining `PER_MINUTE` as a string. The attribute makes PHP raise an error if the interface method is renamed and `limiterKey()` no longer overrides anything. Both catch mistakes at compile time that used to surface in production.

### 6. What does this property do?

```php
final class Customer
{
    public function __construct(
        public string $firstName,
        public string $lastName,
    ) {}

    public string $fullName {
        get => "{$this->firstName} {$this->lastName}";
    }
}
```

It's a property hook, new in PHP 8.4. Because the hook never touches its own backing value, `$fullName` is a virtual property: it's computed on every read and can't be written. The official explanation is in the [PHP manual on property hooks](https://www.php.net/manual/en/language.oop5.property-hooks.php). Strong candidates also mention asymmetric visibility, such as `public private(set)`, which arrived in the same release.

### 7. Have you used the pipe operator yet?

PHP 8.5, released in November 2025, added `|>`, which passes the value on its left into the callable on its right. "I've read about it but haven't used it" is a perfectly good answer. Confidently explaining it wrong is the only bad one.

## Databases, sessions, dependencies: five questions

### 8. How do you sort by a column the user chooses?

Placeholders can't stand in for column names, so this question exposes people who think prepared statements solve everything.

```php
$allowed = ['created_at', 'email', 'last_login_at'];
$sort = in_array($_GET['sort'] ?? '', $allowed, true) ? $_GET['sort'] : 'created_at';

$stmt = $pdo->prepare("SELECT id, email FROM users WHERE status = :status ORDER BY {$sort} DESC");
$stmt->execute(['status' => 'active']);
```

The value goes through a placeholder; the column name goes through an allow-list, compared strictly. Candidates who suggest escaping the column name with a quoting function haven't thought it through.

### 9. How do you store passwords?

`password_hash()` with `PASSWORD_DEFAULT`, checked with `password_verify()`. Anyone who mentions MD5 or SHA-1 isn't ready for a role that touches user data. Good answers also mention `password_needs_rehash()` to upgrade old hashes at the next login.

### 10. What should happen to the session at login?

Call `session_regenerate_id(true)` after a successful login to prevent session fixation, and set cookies with `HttpOnly`, `Secure` and a sensible `SameSite` value. Framework developers may say Laravel or Symfony does this for them. That's true. Ask them what exactly it does.

### 11. When do you run `composer update` versus `composer install`?

`install` reads `composer.lock` and installs exactly those versions; it's what CI and production should run. `update` resolves new versions within your constraints and rewrites the lock file. Someone who runs `composer update` on a production server has a story to tell you, so ask for it.

### 12. Where does XSS come from, and how do you prevent it?

Printing user input into HTML without escaping. The fix is escaping on output: `htmlspecialchars()` in plain PHP, automatic in Blade and Twig. The follow-up separates people: when does auto-escaping get bypassed? In Twig, the `raw` filter; in Blade, the unescaped echo syntax.

## How they work: three open questions

These have no model answer. Listen for specifics.

13. Show me a test you wrote recently. What did it protect against?
14. A page that loaded in 300 milliseconds now takes five seconds in production. What do you do in the first hour?
15. Tell me about a bug you shipped. How did it get through, and what changed afterwards?

Question 14 shows whether someone debugs systematically. JetBrains found 59 percent of PHP developers mostly debug with `var_dump`-style output and 39 percent use a debugger such as Xdebug. Both are fine. What matters is whether they measure first, with logs or a profiler or by counting queries, before guessing.

Question 15 is the most honest one in the interview. Anyone who claims never to have shipped a bug has either little experience or little self-awareness.

For senior roles, add architecture questions; our article on [senior PHP developer skills](https://php-entwickler-gesucht.de/en/assessing-candidates/senior-php-developer-skills/) has a set. For framework-specific roles, the [Laravel vs Symfony comparison](https://php-entwickler-gesucht.de/en/assessing-candidates/laravel-vs-symfony/) suggests what to probe.

## A scoring sheet that keeps interviews comparable

Right after each interview, score each area from 0 to 2 before you talk to anyone else. That way the loudest interviewer doesn't set everyone's opinion.

| Area | 0 | 1 | 2 |
|---|---|---|---|
| Language core | gaps on types and comparison | solid day to day | explains edge cases |
| Modern PHP | stuck on PHP 7 | uses enums and readonly | knows 8.3/8.4 and has opinions |
| Security | vague | standard measures solid | spots exceptions and traps |
| Tests and quality | doesn't test | tests when told | tests by habit, uses static analysis |
| Debugging | guesses | step by step | measures first, knows tools |
| Communication | short answers, no reasons | reasons when asked | reasons unprompted, asks back |

For a mid-level role, 8 out of 12 is a solid result. For senior roles, no area should score 0. These thresholds are a suggestion, not a standard; adjust them to your role.

## Questions you can skip

Some classics appear on every list and tell you little. "What's the difference between `include` and `require`?" Anyone can look that up in ten seconds. Same for the parameter order of `strpos()` or `array_search()`. That tests memory, not skill.

Whiteboard algorithm puzzles with no link to web work also say little about PHP development. If you want to test algorithmic thinking, take a real problem from your own product.

If you don't have the time to build a proper process, an outside team can handle the technical assessment or the development itself, for example a studio like [OnlineBuilders](https://onlinebuilders.biz/solutions/software-development). If you're hiring yourself, it all starts earlier, with a clear [PHP developer job description](https://php-entwickler-gesucht.de/en/hiring/php-developer-job-description/).

Copy the scoring sheet into a shared document now, pick six to eight questions that fit your role, and have a colleague answer them as a dry run. You'll know straight away whether they fit into 25 minutes.

## FAQ

### How long should a PHP technical interview be?

Sixty to ninety minutes is enough if candidates completed a short take-home task beforehand. Longer rarely means better, just more tiring. Keep the last ten minutes for their questions.

### Should I ask candidates to code live?

You can, but live coding while being watched mostly measures nerves. Talking through prepared code, or through the candidate's own take-home solution, shows how they think without the artificial pressure.

### How should I handle AI tools in interviews?

JetBrains reports that 80 percent of PHP developers use AI assistants regularly, so a blanket ban isn't realistic for take-home work. Ask how the candidate checks generated code, and have them explain a section of their own solution.

### Which questions should I never ask?

Questions about age, family plans, religion, nationality or disability are off-limits in the UK under the Equality Act 2010 and are risky in most US states and EU countries. Stick to the work.

Photo: Breather breather, CC0 (https://commons.wikimedia.org/wiki/File:Leather_chairs_in_meeting_room_(Unsplash).jpg)