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?
answer
- same code, different file system
- PHP names case-insensitive, paths not
- namespace must match the directory
- one class per file
- is the autoloader registered in this entry point?
basics
~20 sCheck 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.
solid answer
~40 sThe usual cause is **case**. PHP matches loaded class names case-insensitively, but a PSR-4 autoloader builds a **file path** from the name exactly as written. macOS file systems are usually case-insensitive, so `new App\Models\userRepository()` finds `src/Models/UserRepository.php` locally, while Linux looks for `userRepository.php` and misses. The same applies to directory names such as `models/` versus `Models`. Next suspects: a file whose `namespace` line does not match its directory, a class declared inside another class's file (it loads only if that file happened to load first), and a CLI or worker entry point that never requires the autoloader. Reproduce with the exact name from the error message, check what the autoloader receives, and let static analysis or a case-sensitive CI file system catch it before deploy.
code
php · 14 lines<?php
declare(strict_types=1);
// File on disk: src/Models/UserRepository.php declaring App\Models\UserRepository
spl_autoload_register(static function (string $class): void {
$file = __DIR__ . '/src/' . str_replace(['App\\', '\\'], ['', '/'], $class) . '.php';
if (is_file($file)) {
require $file;
}
});
$a = new App\Models\userRepository(); // Linux: looks for src/Models/userRepository.php
// -> Error: Class "App\Models\userRepository" not found
// macOS (case-insensitive): loads UserRepository.phpgo deeper
Remember that file and folder names must match the class and namespace exactly, including capital letters.
Explain why loaded class lookups ignore case while autoloading depends on the file system, and walk through namespace-to-path mismatches.
Reproduce with the exact name from the error, check registration per entry point, and add case-sensitive CI plus static analysis so it cannot recur.
Make production parity a pipeline property: build and test on the same file-system semantics and generate any class maps as part of the release.
## Why "works on my machine" is common here Autoloading turns a class name into a file path at run time. PHP and the file system disagree about one thing: **letter case**. - PHP looks up classes that are already loaded **case-insensitively**: after `UserRepository` is loaded, `new userrepository()` works. - When the class is not yet loaded, the autoloader receives the name **exactly as written in the code** and builds a path from it, and whether that path matches a file depends on the file system. - macOS file systems are usually **case-insensitive** by default; Linux file systems are **case-sensitive**. So a reference spelled `userRepository`, or a directory created as `models/` for the namespace segment `Models`, works on a Mac and fails on the server with `Error: Class "App\Models\userRepository" not found`. ## The checklist 1. **Case of the reference.** Compare the name in the error message, character by character, with the class declaration and the file name. The message shows the name as the code wrote it. 2. **Case of directories and files.** Every directory must match its namespace segment, and the file must match the class name, as PSR-4 requires. Version control on a case-insensitive machine may not have recorded a rename that only changed case. 3. **Namespace line versus location.** A file at `src/Billing/Invoice.php` must declare `namespace App\Billing;` and `class Invoice`. A typo in the `namespace` line means the file loads but defines a different class, so the lookup still fails after the `require`. 4. **One class per file.** A helper class declared inside another class's file is found only if that file was loaded earlier in the request. A different request path in production exposes it. 5. **Autoloader registration.** A CLI command, queue worker or cron script with its own entry point may never require the autoloader file, so nothing is registered and every class lookup fails. 6. **Generated class maps.** Tools that generate a class map can serve a stale list after a deploy; regenerating it is part of the deploy, and belongs to the tool's own configuration. ## Diagnosing systematically A short script run on the server answers most questions: ```php <?php declare(strict_types=1); require __DIR__ . '/vendor/autoload.php'; $class = 'App\Models\UserRepository'; var_dump(count(spl_autoload_functions())); // any autoloaders registered? var_dump(class_exists($class)); // triggers autoloading var_dump(is_file(__DIR__ . '/src/Models/UserRepository.php')); ``` If `is_file()` is true but `class_exists()` is false, the file does not declare that class name: check the `namespace` line and the class name inside it. If `is_file()` is false, the path or its case is wrong. If no autoloaders are registered, the entry point is the problem. ## Reading the error message precisely The message `Class "App\Models\userRepository" not found` carries three clues: - The **namespace** shows where the autoloader looked: compare it with the directory path. - The **case** is exactly what the code wrote, so a lowercase or mixed-case segment points at the offending reference. - The **stack trace** shows which file and line made the reference; that is the line to fix, not the class file. ## Preventing it - **Static analysis in CI.** An analyser that resolves every class reference reports wrong case and unknown classes before deployment. - **Case-sensitive CI.** Running tests on Linux, or on a case-sensitive volume, reproduces production behaviour. - **Consistent references.** Use `Foo::class` and IDE-completed imports rather than hand-typed strings; a string class name in configuration is invisible to most tooling. - **Enforce one class per file** with a coding-standard rule, so no class depends on another file loading first. ## What is not an autoloading problem Some errors look similar but have other causes: | Symptom | Real cause | |---|---| | `ParseError` from the class file | syntax error; the file was found and compiled | | `Call to undefined function` | functions are never autoloaded; a file of functions was not required | | Class found but old code runs | stale compiled code after deploy, not autoloading | | `Cannot redeclare class ... (previously declared in ...)` | the same class defined in two files, both loaded | Separating these from real class-not-found errors saves time: autoloading problems always end in `Class "..." not found` (or a `false` from `class_exists()`).
- In PHP, why does new userrepository() work on Linux once UserRepository is already loaded?PHP's class table is keyed by the lowercased name, so a loaded class is found whatever case the code uses. The autoloader, and with it the file system, is only involved while the class is not loaded yet. That is why a case slip can pass in one request and fail in another, depending on which reference runs first.
- In PHP, how can a class file be found and required but the class still be reported as not found?The file declares a different name than the one requested, most often because its `namespace` line has a typo or was not updated after a move. The `require` succeeds, the engine checks the class table, the requested name is still missing, and after the remaining autoloaders it throws `Error: Class "..." not found`.
saying these in an interview costs you the question
- PHP class names are case-sensitive, so the reference must be wrong everywhere
- File name case never matters because PHP ignores case in class names
- If the file exists, the class cannot be reported as not found
- A second class in the same file is autoloaded with its own name
- Every CLI script automatically shares the web entry point's autoloader