Atlas renames a PHP symbol across the repo by refusing to treat the job as find-and-replace. Atlas uses the lsp tool's findReferences to get the true reference set from the PHP language server, grep to catch the strings, docblocks, and config the compiler never sees, and the edit tool with replaceAll for the mechanical part. The edit tool refuses ambiguous single replacements, throwing Found multiple matches for oldString unless you add context or opt into replaceAll, so an unintended match in a Composer-autoloaded namespace becomes an error rather than a silent corruption. PHPUnit runs between edits, and PHP-CS-Fixer normalizes the result.
Why is renaming a PHP class riskier than a find-and-replace?
A PHP rename is riskier than find-and-replace because PHP resolves names in three ways at once: PSR-4 autoloading from composer.json, `use` statements, and dynamic strings. A class named `Order` appears as `App\Domain\Order`, as `Order::class`, and as the literal string "Order" in a container config, and a blind replace hits all of them wrongly.
Atlas's answer is the lsp tool's findReferences operation, which asks the PHP language server for the authoritative reference set. The language server knows that `App\Domain\Order` and `App\Legacy\Order` are different classes even though both are spelled `Order` after a `use` statement, so its reference list is exact where a regex is only hopeful. Start there, on the fully qualified name, and get back every `use` import, every type hint, every `instanceof`, and every `::class` reference across src/ and tests/. Also confirm that the class name matches its file path, because under PSR-4 the rename usually means moving src/Domain/Order.php to src/Domain/PurchaseOrder.php, and composer's autoloader will fail at runtime if the two disagree.
How does grep catch the PHP references the language server misses?
Run Atlas's grep for the old PHP name to catch occurrences outside the type system, in 4 places the compiler never sees: strings, comments, docblocks, and config. A Symfony services.yaml, a `@param Order $order` docblock, a Doctrine mapping XML, and a `container->get('order.repository')` string are all invisible to the PHP language server.
PHP's most dangerous references are the ones the type system does not resolve. Atlas's grep takes a real regex plus include and path filters and runs through ripgrep, so you can sweep config/*.yaml, src/**/*.php, and the docblocks separately with different patterns. Search for the bare class name, the fully qualified namespace with escaped backslashes, and the snake_case service id if your container uses one. Also grep composer.json itself: a class listed in `autoload.files` or a package script referencing the old name will silently break `composer dump-autoload`. The grep pass is what turns a rename that compiles into a rename that actually runs, because in PHP the difference between those two is a string in a YAML file.
How does the Atlas edit tool prevent a wrong PHP replacement?
Atlas's edit tool enforces uniqueness. Where a single occurrence must change, edit throws Found multiple matches for oldString unless you add context or opt into replaceAll, so renaming `getOrder` inside src/Service/Checkout.php cannot silently also rewrite an unrelated `getOrderTotal` call 3 methods down.
The mechanical part of a PHP rename is safe only when ambiguity is an error. Apply the bulk renames with edit using replaceAll where the match is unambiguous per file, for example in src/Domain/Order.php where every occurrence of the class name is the class you are renaming. Where a file mixes the target symbol with lookalikes, do not force replaceAll: give edit enough surrounding context to make the match unique and let it fail loudly if the context is still ambiguous. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, so each rewritten `use App\Domain\Order;` line is reviewed before it lands. Atlas snapshots file changes as git patches, so a rename that turns out wrong in one file can be diffed and rolled back without unwinding the whole pass.
How do you prove a PHP rename is complete?
Prove a PHP rename is complete in three steps: run `composer dump-autoload` so Composer regenerates the PSR-4 map against the new file names, run `vendor/bin/phpunit` so PHPUnit exercises the renamed class, then grep once more for the old name to prove zero remaining hits.
Composer's autoloader is the piece that catches the renames a compiler would not. If src/Domain/Order.php became src/Domain/PurchaseOrder.php but a `use App\Domain\Order;` survived somewhere, `composer dump-autoload` plus a PHPUnit run will surface a class-not-found at exactly that callsite. Run the full PHPUnit suite with Atlas's bash tool, not just the tests for the class you renamed, because a string-keyed container reference will only fail in the integration test that boots the container. Finish with `vendor/bin/php-cs-fixer fix` so PHP-CS-Fixer restores PSR-12 formatting on every touched file, and grep one final time for the old symbol. Zero remaining hits plus a green PHPUnit run is the standard of proof.
How does Atlas keep a repo-wide PHP rename reviewable?
Atlas keeps a PHP rename reviewable because every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs, and Atlas computes a unified diff for every file edit and surfaces it for approval before writing. A 60-file rename across src/ and tests/ is 60 diffs you can accept or reject.
A rename touches more files than any other refactor, so the review mechanism has to scale. Atlas reads git branches, status, and diffs, and can stage and create commits on your behalf, which lets you land the rename as one commit and inspect `git diff` in the same terminal session. If you want the reference enumeration done with no write capability at all, Atlas drafts a plan in a read-only plan agent and asks before switching to a build agent, so the findReferences list and the grep sweep of your composer.json, config, and docblocks are complete before any .php file is writable. For a private Composer package, Atlas can build its code index with local Ollama embeddings, keeping code off third-party servers.
Step by step
- 01Run atlas in the PHP project root, the directory with composer.json, so the PSR-4 autoload map and every namespace are in scope.
- 02Run the lsp tool's findReferences operation on the symbol to get the authoritative callsite list from the PHP language server, covering every `use` import, type hint, and `::class` reference.
- 03Run grep for the old name to catch occurrences outside the type system: strings in config YAML, docblock `@param` annotations, Doctrine mappings, and container service ids.
- 04Apply the mechanical renames with edit using replaceAll where the match is unambiguous per file, for example inside src/Domain/Order.php.
- 05Where a single occurrence must change, let edit enforce uniqueness: it throws Found multiple matches for oldString unless you add context or opt into replaceAll.
- 06Rename the file itself to match the class under PSR-4, then run `composer dump-autoload` so Composer regenerates the autoload map against the new path.
- 07Run `vendor/bin/phpunit` with bash so PHPUnit exercises the renamed class, including the integration tests that boot the container and resolve string-keyed services.
- 08Run `vendor/bin/php-cs-fixer fix` so PHP-CS-Fixer restores PSR-12 formatting, then grep once more for the old name to prove zero remaining hits.
Frequently asked questions
- how to rename a class across a whole PHP project safely
- Get the true reference set with Atlas's lsp tool findReferences, grep for the strings and docblocks the type system misses, apply the mechanical renames with edit using replaceAll where unambiguous, move the file to match PSR-4, then run `composer dump-autoload` and `vendor/bin/phpunit`.
- why not just find and replace a PHP class name
- PHP resolves names through PSR-4 autoloading, `use` statements, and dynamic strings at once, so a blind replace hits App\Legacy\Order along with App\Domain\Order and rewrites unrelated strings. Atlas's edit tool refuses ambiguous single replacements instead of guessing.
- what does Found multiple matches for oldString mean in Atlas
- Atlas's edit tool enforces uniqueness on a single replacement. When the old string appears more than once in the PHP file, edit throws Found multiple matches for oldString unless you add surrounding context or explicitly opt into replaceAll.
- does Atlas run PHPUnit after a rename
- Yes, through the bash tool. Run `vendor/bin/phpunit` across the full suite rather than only the renamed class's tests, because a string-keyed container reference will only fail in an integration test that boots the container.
- do I need to run composer dump-autoload after renaming a PHP class
- Yes, if the file moved. Under PSR-4 the class name and the file path must agree, so renaming src/Domain/Order.php to src/Domain/PurchaseOrder.php requires `composer dump-autoload` before Composer's autoloader can resolve the new name.
- will Atlas keep my PHP code PSR-12 compliant after a rename
- Run `vendor/bin/php-cs-fixer fix` through Atlas's bash tool on the touched files. PHP-CS-Fixer is the formatter in the documented PHP setup, alongside PHPUnit as the test runner and Composer as the package manager.
- can I review each file Atlas renames in PHP
- Yes. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, and every Atlas tool call is permission-gated against allow, ask, and deny rules. A 60-file rename is 60 diffs you can accept or reject.
- how do I start Atlas in a PHP project
- Run atlas in a project with a composer.json. Atlas reads your namespaces, autoload config, and dependencies, and you can ask it to add PHPUnit tests or apply PSR-12 formatting, reviewing the diff before it is written.
Try Atlas in your terminal
The terminal-native AI coding agent. Free core, single binary.
Install AtlasRelated guides
Rename a Symbol Across the Repo with Atlas in 2026
How to rename a symbol across a repo with Atlas in 2026: findReferences gets the true reference set, grep catches strings and docs, and edit refuses ambiguous matches.
Atlas for PHP in 2026
Atlas, the terminal-native AI coding agent, empowers PHP developers in 2026 with intelligent code understanding, secure workflows, and direct integration for Composer and PSR standards.
Extract a shared helper from duplicated code in PHP with Atlas (2026)
The same PHP logic is copy-pasted in 5 controllers with different variable names. Atlas finds it by meaning with codebase_search, extracts one helper, and proves it with PHPUnit.
Audit a PHP Repository with Parallel Subagents in 2026
Sweep your entire PHP codebase for issues without context window limits. Atlas uses parallel subagents to audit Composer packages and PSR standards, integrating with PHPUnit and PHP-CS-Fixer.
Run the PHP Test Suite and Triage Failures with Atlas in 2026
Streamline PHPUnit test failure triage in 2026 with Atlas. Quickly turn a wall of red output into a prioritized list of distinct root causes for your PHP codebase, integrating direct with Composer.
Write unit tests for untested code in PHP with Atlas (2026)
Write PHPUnit tests for untested PHP code in 2026 with Atlas: enumerate exported symbols with documentSymbol, copy the repo's test conventions, then actually run them.
Plan a multi-file change before editing in PHP with Atlas (2026)
Design a multi-file PHP change before any edit lands. Atlas's plan agent denies edit outside .atlas/plans/*.md, and plan_exit gates the handoff to the build agent.
Add a regression test for a bug fix in PHP with Atlas (2026)
Add a PHP regression test for a bug fix in 2026: Atlas proves the PHPUnit test fails red first, applies the fix with edit, and re-runs the same command to prove green.