skip to content

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%

answer

  1. same code, different file system
  2. PHP names case-insensitive, paths not
  3. namespace must match the directory
  4. one class per file
  5. is the autoloader registered in this entry point?

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.

solid answer

~40 s

The 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
<?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.php

go deeper

for a junior

Remember that file and folder names must match the class and namespace exactly, including capital letters.

for a middle

Explain why loaded class lookups ignore case while autoloading depends on the file system, and walk through namespace-to-path mismatches.

for a senior

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.

for a principal

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