LearnThatStack Ace your next interview

PHP

Assumes PHP 8. Features after 8.0 name their version Checked Nov 2026 133 code snippets

The model

  • PHP runs on the server and sends only its output, usually HTML. Each request starts fresh and frees everything at the end. In this shared-nothing model, state survives only in outside storage.
  • A variable takes its type from its value, and PHP converts types as needed. This is type juggling. Use === and strict types to stop it.
  • Every array is one type, an ordered map from keys to values. Lists and dictionaries are both this map.
  • An object variable holds an identifier, not the object. Passing it copies the identifier, so a function changes the same object.
BrowserNginxserves static filesPHP-FPM workerone at a timeOutlives requestOPcache, Redis, DBREQUESTrequestFastCGI to idle workeropcodes, sessionRESPONSEsaved stateresponse through Nginx
The worker frees its variables at the end. Only the right lane survives to the next request.

Syntax & types

how a script looks and how PHP treats values

What PHP is

Expand the namePHP: Hypertext Preprocessor, a recursive acronym.

TipIt is an open source, server-side scripting language. The same code runs on Linux, Windows and macOS.

Strings and constants

Keep text literal'Hi $name'
Expand an array value"Hi {$u['name']}"
Write multi-line text<<<EOT<<<'EOT'
Declare a constantconst MAX = 10;define('MAX', 10);

TipHeredoc expands and nowdoc doesn't. const is compile time, so only define() works inside an if.

TrapIn single quotes, only \' and \\ are special. So '\n' is a backslash and the letter n.

Type declarations

Stop scalar conversiondeclare(strict_types=1);
Type a property (7.4+)public int $id;
Allow several typesint|float?string

TipA typed property with no default starts uninitialized, not null. Reading it first throws an Error.

TrapStrict mode for arguments follows the calling file, not the file that defines the function.

Functions & arrays

calling code and working with PHP's one array type

Calls and arguments

Call by variable name$func();
Accept extra argumentsfunction f(int ...$n)
Read them the old wayfunc_get_args()
Pass by value or referencefunction f($x)function f(&$x)

TipObjects don't pass by reference. PHP copies the identifier, so only & lets a new object reach the caller.

TrapCheck a name from user input against an allowlist, or a user could run any function.

Merge and transform arrays

Join arraysarray_merge($a, $b)
Pair keys with valuesarray_combine($k, $v)A size mismatch throws a ValueError since 8.0.
Transform, keep or foldarray_map($fn, $a)array_filter($a, $fn)array_reduce($a, $fn, 0)

Tiparray_merge renumbers numeric keys, and a later string key wins. $a + $b keeps the first array's values.

Traparray_filter() keeps the original keys. Wrap it in array_values() for a clean list.

Classes & objects

OOP in PHP and the newer class features

OOP, chaining and static binding

Name the four principlesEncapsulation, inheritance, polymorphism, abstraction.
Chain method callsreturn $this;
Use the defining classself::who()
Use the called classstatic::who()new static()

TipB::test() runs A's method. There self::who() means A and static::who() means B, so it prints AB.

Trapparent:: and self:: forward the called class. A direct A::foo() resets it.

Magic methods

Catch missing members__get()__set()__call()
Act as a string or function__toString()__invoke()
Control serialization__serialize()

TrapEvery magic method except __construct(), __destruct() and __clone() must be public, or PHP warns.

Promotion, readonly and enums

Promote a constructor argument__construct(private $x)
Allow one write (8.1+)readonly int $id;
Change a copy (8.5+)clone($o, ['a' => 1])
Declare an enum (8.1+)enum Status: string
Read a case from a valueStatus::from('a')Status::tryFrom('a')

Tipfrom throws a ValueError on an unknown value, and tryFrom returns null. Each case is a single object.

Trapclone is shallow, so the copy shares objects in properties. Hold DateTimeImmutable, not DateTime.

Errors, debugging & tests

report problems, find them and prove the fix

Mocks and stubs

Return a fixed answer$this->createStub(Repo::class)
Verify a call happened$this->createMock(Mail::class)

TrapToo many mocks tie tests to implementation details, so a refactor breaks them.

Databases with PDO

safe queries, transactions and query counts

Transactions and query tuning

Start it before try$pdo->beginTransaction()
Save or undo it all$pdo->commit();$pdo->rollBack();
Read the query planEXPLAIN SELECT ...
Page past a deep OFFSETWHERE id > ? LIMIT 20

TipIndex the WHERE, JOIN and ORDER BY columns. PHP has no built-in connection pool, so use PgBouncer or ProxySQL.

TrapMyISAM has no transactions. In MySQL, DDL such as CREATE TABLE commits the open one.

Sessions & security

the attacks interviewers ask about, and the PHP fix

Sessions and cookies

Store on the server$_SESSION['id'] = 7;
Reject planted IDssession.use_strict_mode
Get a new ID at loginsession_regenerate_id(true);
Expire a session yourselfStore last activity. gc_maxlifetime isn't a timeout.

TipA cookie holds about 4KB, and the user can change it. Use a session for what the user must not change.

TrapIn session fixation, the victim logs in on an ID the attacker planted. A new ID at login defeats it.

CSRF

Make a tokenrandom_bytes(32)
Compare in constant timehash_equals($a, $b)

TipAdd SameSite=Lax as a layer, not a replacement.

TrapCross-site scripting (XSS) defeats every CSRF defense, so prevent it too.

Password hashing

Hash a passwordpassword_hash($p, PASSWORD_DEFAULT)
Check a passwordpassword_verify($p, $h)
Spot an old hash at loginpassword_needs_rehash()

TipMake the column 255 wide. PASSWORD_DEFAULT is bcrypt today, and 8.4 raised its cost from 10 to 12.

TrapNever use MD5 or SHA-256. They are fast, so attackers test guesses at high speed.

Requests & APIs

read input and build web services

CORS

Allow one originAccess-Control-Allow-Origin
End an OPTIONS preflightexit();

TipThe browser enforces CORS. It isn't authentication.

Trap* can't be used with cookies. Echo one origin from an allowlist, and send Vary: Origin.

Rate limiting

Count in Redis$redis->incr($key);

TipReject with status 429. A sliding window stops a client sending twice the limit across a window boundary.

TrapIf the process dies between incr() and expire(), the key never expires. A Lua script does both at once.

Composer & tooling

load code, manage packages and set up a project

Include, autoload and namespaces

Load, warn if missinginclude 'a.php';
Load, stop if missingrequire 'a.php';
Skip a file already loadedrequire_once 'a.php';
Load a class on first usespl_autoload_register()
Declare and import namesnamespace App\Models;use App\Models\User;

TipWrite \Exception for a global class. Functions and constants fall back to global, so strlen() works.

TrapA use statement only imports a name. The file loads on first use, such as new User().

Composer, PSR and code style

Add a packagecomposer require a/b
Install from the lock filecomposer install
Fetch newest versionscomposer update
Name the common PSRsPSR-4 autoload, PSR-3 logger, PSR-7 HTTP. PER Coding Style replaces PSR-12.
Check or fix stylephpcs srcphp-cs-fixer fix src

Tip^2.0 means at least 2.0, below 3.0. --no-dev skips development packages on servers.

TrapCommit composer.lock, so every developer and server installs the same versions.

Configuration

Read an env valuegetenv('DB_HOST')

TipKeep the code the same, and put what differs between environments in environment variables.

TrapThe production php.ini can leave $_ENV empty, so check getenv() too. Keep .env out of git.

Design patterns & SOLID

the patterns PHP interviews ask you to explain

SOLID

Single responsibilityOne reason to change.
Open-closedAdd a class, not an edit.
Liskov substitutionA subtype replaces its base.
Interface segregationSmall interfaces beat one big one.
Dependency inversionDepend on abstractions.

TrapOver-applying SOLID adds an interface for every class. Apply it where code is expected to change.

Runtime & performance

what the server does, and how to make it faster

Request life cycle and OPcache

Cap requests, restart workerspm.max_childrenpm.max_requests
Compile at server startopcache.preload
Turn on the JITopcache.jit
Skip file checksopcache.validate_timestamps=0

TipOPcache keeps opcodes in shared memory, so requests skip compiling. The JIT mostly helps CPU-heavy work.

TrapWith file checks off, every deploy must reset OPcache or restart PHP.

Caching layers

Cache dataAPCu on one server, Redis or Memcached across servers.
Reuse a responseCache-Control

TipCache-aside reads the cache first. On a miss, it loads from the database and stores the result with a time to live.

TrapWhen a busy key expires, many requests hit the database. A rebuild lock stops this cache stampede.

Sync or async

Compare the modelsPHP-FPM blocks. Amp and Swoole run an event loop.
Pause a call stack (8.1+)Fiber::suspend();

TipOnly one fiber runs at a time, so fibers give concurrency, not parallelism.

TrapBlocking libraries stall the event loop, and long-running processes must not leak.

Found this useful? Pass it on.
Pro · $10/mo

This sheet stays free. With Pro, you can:

  • Every answer on this sheet. Read the full answer to all 82 questions it links, not just the 11 free ones.
  • My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
  • PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
  • The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.