Symlink loops prevent finding all files, but phan still exits with a successful exit code
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 5.6k
- Forks
- 365
- Avg merge
- 1h 25m
- Merged PRs (30d)
- 6
Description
If Phan runs into a symlink loop when trying to find files, it will print a warning, not find all of the files that should be processed, and then may exit with a successful exit code.
Reproduction
- Create an empty directory for the test.
composer require --dev phan/phan- Create a file with something Phan will complain about:
printf '<?php\n\nfoo();' > foo.php - Create the directory with a symlink loop. For some reason I find I have to create two symlinks for it to reproduce the bug. 🤷
mkdir subdir && ln -s . subdir/loop1 && ln -s . subdir/loop2 - For good measure, create another file with something Phan will complain about:
printf '<?php\n\nxxx();' > xxx.php - Run Phan and examine its exit code:
vendor/bin/phan --debug --directory . --exclude-directory-list vendor; echo $?
Actual results
Phan prints a warning, doesn't scan any of the files, and exits with a status of 0.
WARNING: Caught exception while listing files in '.': RecursiveDirectoryIterator::__construct(./subdir/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop2/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop2/loop2/loop2/loop2/loop2/loop2/loop2/loop2): Failed to open directory: Too many levels of symbolic links
Phan did not parse any files in the project "/tmp/test" - This may be an issue with the Phan config or CLI options.
0
In some of the real-life cases where I ran into this, Phan did process a subset of the files it was supposed to process, presumably whichever ones it found before running into the symlink loop.
Expected results
Either Phan detects the loop and still analyzes both PHP files, or it prints an error that it couldn't successfully process the provided directories and exits with a non-zero status.
To be clear, I'd be happy with it doing something like this:
ERROR: Caught exception while listing files in '.': RecursiveDirectoryIterator::__construct(./subdir/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop2/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop2/loop2/loop2/loop2/loop2/loop2/loop2/loop2): Failed to open directory: Too many levels of symbolic links
1
The non-zero exit code would make our CI that's running Phan indicate that an error occurred. Then I'd be notified to look into it, at which point I'd figure out what I needed to change in our config (probably an addition to exclude_file_list or exclude_file_regex) to avoid having Phan look into whichever path has the loop.
I'd also be happy with this:
WARNING: Caught exception while listing files in '.': RecursiveDirectoryIterator::__construct(./subdir/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop2/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop1/loop2/loop2/loop2/loop2/loop2/loop2/loop2/loop2): Failed to open directory: Too many levels of symbolic links
[... processing proceeds with all files that would have been found if it weren't for the loop ...]
Phan's analysis is complete
foo.php:3 PhanUndeclaredFunction Call to undeclared function \foo()
xxx.php:3 PhanUndeclaredFunction Call to undeclared function \xxx()
1
But making that happen would likely be more effort and I don't know that it would be worth it.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the issue with the documented symlink-loop setup and run vendor/bin/phan --debug --directory . --exclude-directory-list vendor, checking both diagnostics and the exit code. Trace the file-listing path around the RecursiveDirectoryIterator exception. Done means Phan either analyzes all reachable PHP files or reports the listing failure with a non-zero exit status.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100