symfony/routing · error · InvalidArgumentException

The file " " does not contain PHP code. Did you forget to…

Error message

The file "%s" does not contain PHP code. Did you forget to add the "<?php" start tag at the beginning of the file?

What it means

findClass tokenizes the routing file and, if the entire file is a single T_INLINE_HTML token, no PHP was found — typically the file lacks the '<?php' opening tag. It throws InvalidArgumentException pointing at the file.

Solutions

  1. Add '<?php' as the very first characters of the file (no leading HTML or BOM)
  2. Verify the file actually contains the route class, not HTML/markdown
  3. Check the file path passed to the loader points at a PHP class file

Example fix

// before (file content)
<html><body>oops</body></html>
// after
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/blog')]
class BlogController {}
Defensive patterns

Strategy: validation

Validate before calling

$head = substr((string) file_get_contents($file, false, null, 0, 20), ltrim($head, "\xEF\xBB\xBF") ); if (!str_starts_with(ltrim($head), '<?php')) { throw new \InvalidArgumentException("$file lacks <?php opening tag"); }

Type guard

function isPhpSource(string $file): bool { return str_starts_with(ltrim(substr(file_get_contents($file, false, null, 0, 64), "\xEF\xBB\xBF")), '<?php'); }

Try / catch

try { $routes = $loader->load($file); } catch (\InvalidArgumentException $e) { if (str_contains($e->getMessage(), 'does not contain PHP code')) { /* check opening tag */ } throw $e; }

Prevention

When it happens

Trigger: A route class file saved without an opening <?php tag (or starting with a BOM/HTML before it), passed to the attribute route loader.

Common situations: Files created by editors/templates that emit plain text; heredoc-generated config files; renaming a template file to .php; files starting with a byte-order mark.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of symfony/routing@83fa223250 (2026-09-14). Data as JSON: /api/errors/9c841730de7dce5a. Report an issue: GitHub.

Appendix: source

Thrown at Loader/AttributeFileLoader.php:79

        return $collection;
    }

    public function supports(mixed $resource, ?string $type = null): bool
    {
        return \is_string($resource) && 'php' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'attribute' === $type);
    }

    /**
     * Returns the full class name for the first class in the file.
     */
    protected function findClass(string $file): string|false
    {
        $class = false;
        $namespace = false;
        $tokens = token_get_all(file_get_contents($file));

        if (1 === \count($tokens) && \T_INLINE_HTML === $tokens[0][0]) {
            throw new \InvalidArgumentException(\sprintf('The file "%s" does not contain PHP code. Did you forget to add the "<?php" start tag at the beginning of the file?', $file));
        }

        $nsTokens = [\T_NS_SEPARATOR => true, \T_STRING => true, \T_NAME_QUALIFIED => true];
        for ($i = 0; isset($tokens[$i]); ++$i) {
            $token = $tokens[$i];
            if (!isset($token[1])) {
                continue;
            }

            if ($class && \T_STRING === $token[0]) {
                return $namespace.'\\'.$token[1];
            }

            if (true === $namespace && isset($nsTokens[$token[0]])) {
                $namespace = $token[1];
                while (isset($tokens[++$i][1], $nsTokens[$tokens[$i][0]])) {
                    $namespace .= $tokens[$i][1];
                }

View on GitHub (pinned to 83fa223250)