skip to content

Code Organization

How PHP code spreads across names and files: namespace declarations with use imports, include and require, and classes autoloaded on first use. Interviewers check you know what loads when.

part ofPHPoverview, primer and where to startread it →
on this pageshow

explore

questions

15

In PHP, what does spl_autoload_register() do, and what happens when code uses a class that is not yet defined?

level: juniorimportance: must knowfreq 72%

answer

  1. a last chance before class not found
  2. callbacks receive the fully qualified name
  3. tried in order until the class exists
  4. no autoloader succeeds: Error thrown
  5. __autoload() removed in PHP 8.0

basics

~20 s

spl_autoload_register() adds a callback to PHP's autoloader queue. When code uses an undefined class, PHP calls each callback in order with the class name until one defines the class; if none does, an Error "Class not found" is thrown.

solid answer

~50 s

**Autoloading** lets PHP load a class file on demand instead of requiring every file up front. `spl_autoload_register($callback)` appends a callable to a per-request queue. When running code needs a class, interface, trait or enum that is not defined yet, for example through `new`, a static call or `class_exists()`, the engine calls each registered callback in order, passing the **fully qualified class name** without a leading backslash. A callback typically maps the name to a file path and `require`s it. As soon as the class exists after a callback returns, PHP stops and continues the original operation; if every callback runs and the class still does not exist, PHP throws `Error: Class "App\Foo" not found`. The old global `__autoload()` function, deprecated in 7.2, was removed in PHP 8.0; `spl_autoload_register()` is the only mechanism, and Composer's generated autoloader registers itself through it.

code

php · 14 lines
php
<?php
declare(strict_types=1);

spl_autoload_register(static function (string $class): void {
    echo "autoload: {$class}\n";
});

var_dump(class_exists('Shop\Cart'));   // autoload: Shop\Cart, then bool(false)

try {
    new Shop\Cart();                   // autoload: Shop\Cart
} catch (\Error $e) {
    echo $e->getMessage(), "\n";       // Class "Shop\Cart" not found
}

go deeper

for a junior

Know that spl_autoload_register() registers a callback that loads a class file the first time the class is used, and that Composer's autoloader works this way.

for a middle

Walk through the queue: callbacks run in order with the fully qualified name until the class exists, and a missing class ends in Error for new but false for class_exists().

for a senior

Debug missing classes by inspecting spl_autoload_functions() and the exact name the callbacks receive, and remember that functions are never autoloaded.

for a principal

Keep one autoloading strategy per application, generated where possible, so class loading stays predictable across entry points and deployments.

## The problem autoloading solves Before autoloading, every script began with a list of `require_once` lines, one per class it might use. The list had to be kept in sync by hand, loaded classes the request never touched, and broke whenever files moved. **Autoloading** inverts this: PHP asks your code to load a class at the moment it is first needed. ## How spl_autoload_register works `spl_autoload_register()` has the signature `spl_autoload_register(?callable $callback = null, bool $throw = true, bool $prepend = false): bool` and adds `$callback` to an internal, per-request **queue of autoloaders**. The callback receives one argument, the class name, and is expected to make the class exist, usually by requiring a file: ```php <?php declare(strict_types=1); spl_autoload_register(static function (string $class): void { $file = __DIR__ . '/src/' . str_replace('\\', '/', $class) . '.php'; if (is_file($file)) { require $file; } }); $invoice = new App\Billing\Invoice(); // triggers the callback with "App\Billing\Invoice" ``` When the engine needs an undefined class-like symbol (class, interface, trait or enum), it: 1. Takes the name **as written after namespace resolution**, strips any leading backslash and passes it to the first autoloader. 2. After the callback returns, checks whether the class now exists. If it does, it stops and resumes the original operation. 3. Otherwise it calls the next autoloader in the queue. 4. If the queue is exhausted, the operation fails: `new` or a static call throws `Error` with `Class "App\Billing\Invoice" not found`, while `class_exists()` simply returns `false`. The callback's return value is ignored; only whether the class exists afterwards matters. Registering the same callback twice is a no-op. ## A timeline of one lookup For `$cart = new Shop\Cart();` with two registered autoloaders, a host loader and a plugin loader: - The engine finds no `shop\cart` in its class table. - It calls the host loader with `"Shop\Cart"`. The loader maps it to a path, finds no file and returns. - It calls the plugin loader with the same name. That loader requires `plugins/shop/src/Cart.php`, which declares the class. - The engine sees the class now exists, skips any remaining loaders and finishes the `new` expression. - The next `new Shop\Cart()` in the same request finds the class immediately; no autoloader runs again. ## What it is not - It is **not** triggered for functions or constants. PHP has no function autoloading; files of functions must be required explicitly. - It does **not** make a class available ahead of time. The file is loaded at first use, so a class declared in an unrelated file is found only if some autoloader knows where to look. - It is **not** tied to any file layout. The callback decides the mapping; PSR-4 is a convention for that mapping, not something PHP enforces. ## The removal of __autoload() Early PHP 5 used a single global function named `__autoload($class)`. Because only one such function could exist, two libraries could not both install one. The SPL extension's `spl_autoload_register()` replaced it with a queue of callbacks, PHP 7.2 deprecated `__autoload()`, and **PHP 8.0 removed it**: declaring a function with that name no longer registers anything. Code that still has one must move its body into a callback passed to `spl_autoload_register()`. | Mechanism | Several autoloaders | Status in PHP 8.5 | |---|---|---| | `__autoload()` function | no, only one | removed in 8.0 | | `spl_autoload_register()` | yes, a queue | current | ## Related functions - `spl_autoload_unregister($callback)` removes a callback from the queue. - `spl_autoload_functions()` returns the registered callbacks in order, which helps when debugging. - `spl_autoload_call($class)` runs the queue manually for a name. - Calling `spl_autoload_register()` with no callback registers PHP's built-in `spl_autoload()`, which lowercases the class name and looks for a file with one of the extensions from `spl_autoload_extensions()` (by default `.inc,.php`) on the include path. It is rarely used in modern code. ## In a modern project Most projects never write an autoloader: Composer generates `vendor/autoload.php`, and requiring that one file registers Composer's class loader with `spl_autoload_register()`. The engine behaviour above is still what runs underneath, which is why knowing it helps when a class unexpectedly cannot be found.

  • In PHP, does the autoloader run when a namespaced function such as App\helpers\slug() is undefined?
    No. Autoloading covers class-like symbols only: classes, interfaces, traits and enums. A call to an undefined function throws `Error: Call to undefined function` without consulting any autoloader. Files of functions must be loaded explicitly, typically with `require` in a bootstrap file.
  • In PHP, what name does an autoloader receive for new \App\Model\User() inside another namespace?
    `App\Model\User`: the fully qualified name after the file's namespace and imports were applied, without a leading backslash, and in the letter case written in the code. The autoloader never sees the short or aliased name the source used.
  • In PHP, what does spl_autoload_register() with no arguments do?
    It registers PHP's built-in `spl_autoload()` as an autoloader. That implementation lowercases the class name, turns namespace separators into directory separators and tries each extension from `spl_autoload_extensions()`, `.inc,.php` by default, along the include path. Its lowercasing makes it a poor fit for PSR-4 layouts.

saying these in an interview costs you the question

  • Autoloading loads every class file at the start of the request
  • Defining a function named __autoload still registers an autoloader in PHP 8
  • Autoloading also loads undefined functions and constants
  • Only one autoloader can be registered per request
  • The autoloader's return value tells PHP whether the class was loaded
open as a page

In a PHP file declaring namespace App\Billing, how do the class names Invoice, Tax\Rate and \Invoice each resolve?

level: juniorimportance: must knowfreq 68%

basics

~10 s

Invoice becomes App\Billing\Invoice unless a use statement imports that short name; Tax\Rate becomes App\Billing\Tax\Rate unless Tax is an imported alias; \Invoice is absolute and means the global class Invoice.

open as a page

In PHP 8.5, what happens when include versus require cannot open the file, and which should load a bootstrap file?

level: juniorimportance: must knowfreq 78%

basics

~20 s

When the file cannot be opened, include emits warnings, returns false and the script continues; require emits a warning and then throws an Error, which stops the script unless caught. Anything the program cannot run without should use require.

open as a page

Under PSR-4, with the prefix Acme\Blog\ mapped to src/, which file must hold Acme\Blog\Http\PostController, and what must match?

level: middleimportance: must knowfreq 62%

basics

~10 s

The file is src/Http/PostController.php: the prefix Acme\Blog\ is replaced by src/, remaining namespace segments become directories and the class name becomes the file name, all matching the class name's letter case.

open as a page

In namespaced PHP code, why does calling strlen() work without an import while new DateTime() fails with a class-not-found Error?

level: middleimportance: must knowfreq 62%

basics

~20 s

An unqualified function or constant name that is not imported falls back to the global one at run time if no namespaced version exists; class names never fall back, so DateTime inside App means App\DateTime.

open as a page

A PHP file needs Logger classes from two vendors, Acme\Log\Logger and Beta\Logging\Logger; how do you import both?

level: juniorimportance: should knowfreq 45%

basics

~10 s

Import one under an alias with as, for example use Beta\Logging\Logger as BetaLogger;. Importing both as Logger is a compile-time fatal error, because a short name can refer to only one class per file.

open as a page

In PHP, which operations on an undefined class trigger the autoloader, and which ones, such as instanceof, never do?

level: middleimportance: should knowfreq 45%

basics

~10 s

Autoloading runs when PHP needs the class itself: new, static method or property access, class constants, extends and implements, and class_exists() by default. instanceof, catch clauses, type declarations and ::class never autoload.

open as a page

In PHP, what does Foo::class return, when is it resolved, and does it autoload or verify that Foo exists?

level: middleimportance: should knowfreq 42%

basics

~20 s

Foo::class is a string holding the fully qualified class name, expanded at compile time through the file's imports and namespace; it neither autoloads nor checks that Foo exists, so a typo still yields a string.

open as a page

In PHP, what does $config = require __DIR__ . '/config.php'; evaluate to, and how does an included file return a value?

level: middleimportance: should knowfreq 42%

basics

~20 s

An included file can end with a top-level return statement, and include or require then evaluates to that value, so config.php can return an array. Without a return the expression is int 1, and a failed include gives false.

open as a page

In PHP, when a file is included inside a method, which variables, $this and class members can the included code access?

level: middleimportance: should knowfreq 38%

basics

~20 s

Included code runs in the scope of the line that includes it: inside a method it sees that method's local variables, $this and the class's private members, while functions and classes it declares are global.

open as a page

In PHP, how does require_once decide a file was already loaded, and what does it return on the second call?

level: middleimportance: should knowfreq 55%

basics

~20 s

require_once resolves the path to a real file path and checks a per-request table of files already compiled; if the file is there it skips it and evaluates to true, otherwise it loads the file like require.

open as a page

A PHP class loads on a developer's macOS machine but throws Class not found on the Linux server; which autoloading causes do you check?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Check letter case first: the autoloader builds a file path from the class name as written, and Linux paths are case-sensitive. Then check namespace-to-directory mismatches, classes hidden in another class's file, and entry points that never register the autoloader.

open as a page

You are writing a class autoloader for a PHP plugin system without Composer; how should it behave within the spl_autoload_register() stack?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Register one callback per plugin that handles only its namespace prefix, maps names to files under the plugin directory, requires the file only if it exists and returns silently otherwise, so other autoloaders, including Composer's, still run.

open as a page

A legacy PHP site's include 'lib/db.php' calls break after a script moves into a subdirectory; why, and what is the robust fix?

level: seniorimportance: should knowfreq 45%

basics

~20 s

A relative include is resolved against include_path, whose '.' means the current working directory, and then the including file's directory, not the project root. Moving the script changes those; building paths from DIR makes them independent of both.

open as a page

In namespaced PHP code, why do some codebases write \strlen() or use function strlen, and what does it change at compile time?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

An unqualified call inside a namespace might hit a namespaced function, so the compiler must defer the lookup. Writing \strlen() or importing it lets the compiler bind the built-in directly and use specialised opcodes for functions such as strlen and count.

open as a page