phan / phan/phan

Symlink loops prevent finding all files, but phan still exits with a successful exit code

Open
#4,842 0 comments 1 reaction 0 assignees View on GitHub

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
  1. Create an empty directory for the test.
  2. composer require --dev phan/phan
  3. Create a file with something Phan will complain about: printf '<?php\n\nfoo();' > foo.php
  4. 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
  5. For good measure, create another file with something Phan will complain about: printf '<?php\n\nxxx();' > xxx.php
  6. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.