acc-find-infinite-loops
Infinite Loop Detection
Analyze PHP code for potential infinite loops and unbounded execution.
Detection Patterns
1. Missing Break Conditions
// BUG: No exit condition
while (true) {
$item = $queue->pop();
process($item);
// No break when queue is empty
}
// BUG: Condition never changes
$running = true;
while ($running) {
doWork();
// $running never set to false
}
// BUG: Break inside nested condition
while ($items) {
foreach ($items as $item) {
if ($item->isDone()) {
break; // Only breaks inner loop
}
}
}
2. Incorrect Loop Variable Modification
// BUG: Wrong variable incremented
for ($i = 0; $i < count($items); $j++) { // Should be $i++
process($items[$i]);
}
// BUG: Variable modified in wrong direction
for ($i = 10; $i > 0; $i++) { // Should be $i--
process($i);
}
// BUG: Loop variable reset
$i = 0;
while ($i < 10) {
if ($condition) {
$i = 0; // Resets counter
}
$i++;
}
3. Unbounded Recursion
// BUG: No base case
function factorial(int $n): int
{
return $n * factorial($n - 1); // Never stops
}
// BUG: Base case unreachable
function traverse(Node $node): void
{
$this->traverse($node->getNext()); // What if getNext returns self?
// Missing: if ($node === null) return;
}
// BUG: Mutual recursion
function a($n) { return b($n); }
function b($n) { return a($n); } // Infinite cycle
4. Circular References
// BUG: Circular linked list traversal
while ($node !== null) {
$node = $node->next; // What if list is circular?
}
// BUG: Object graph cycle
function serialize($obj, $visited = []): string
{
foreach ($obj->getRelations() as $rel) {
$result .= serialize($rel); // May revisit objects
}
}
// FIXED:
function serialize($obj, array &$visited = []): string
{
if (in_array($obj, $visited, true)) {
return '[circular]';
}
$visited[] = $obj;
// ...
}
5. Event/Listener Loops
// BUG: Event triggers itself
class UserUpdatedListener
{
public function handle(UserUpdated $event): void
{
$user = $event->getUser();
$user->touch(); // Triggers another UserUpdated event
}
}
// BUG: Message queue requeue loop
public function handle(Message $message): void
{
try {
$this->process($message);
} catch (Exception $e) {
$this->queue->publish($message); // Requeues failed message infinitely
}
}
6. Retry Without Limit
// BUG: Infinite retry
function fetch(string $url): string
{
while (true) {
try {
return $this->httpClient->get($url);
} catch (Exception $e) {
sleep(1);
// No max retries, infinite loop on persistent failure
}
}
}
// FIXED:
function fetch(string $url, int $maxRetries = 3): string
{
$attempts = 0;
while ($attempts < $maxRetries) {
try {
return $this->httpClient->get($url);
} catch (Exception $e) {
$attempts++;
if ($attempts >= $maxRetries) {
throw $e;
}
sleep(1);
}
}
}
7. Generator Infinite Yield
// BUG: Generator never ends
function allNumbers(): Generator
{
$i = 0;
while (true) {
yield $i++;
// Fine for generators, but consuming code may not limit
}
}
// Usage BUG:
foreach (allNumbers() as $n) {
echo $n; // Infinite loop
}
8. Database Pagination Without Limit
// BUG: Potentially infinite
$offset = 0;
while (true) {
$batch = $repository->findBy([], null, 100, $offset);
if (empty($batch)) {
break;
}
process($batch);
// Missing: $offset += 100;
}
Grep Patterns
# while(true) without break
Grep: "while\s*\(\s*true\s*\)" --glob "**/*.php"
# Recursion
Grep: "function\s+(\w+)\([^)]*\)[^{]*\{[^}]*\1\s*\(" --glob "**/*.php"
# for loop with wrong increment
Grep: "for\s*\([^;]+;\s*\$\w+\s*[<>]\s*[^;]+;\s*\$(?!\1)" --glob "**/*.php"
# while without modification
Grep: "while\s*\(\s*\$\w+\s*\)" --glob "**/*.php"
Severity Classification
| Pattern | Severity |
|---|---|
| Infinite retry without limit | π΄ Critical |
| Missing recursion base case | π΄ Critical |
| Wrong loop variable | π΄ Critical |
| Circular reference traversal | π Major |
| Event self-triggering | π Major |
| while(true) without clear exit | π Major |
Prevention Patterns
Always Use Limits
const MAX_ITERATIONS = 1000;
$iterations = 0;
while ($condition && $iterations < self::MAX_ITERATIONS) {
// ...
$iterations++;
}
if ($iterations >= self::MAX_ITERATIONS) {
throw new RuntimeException('Max iterations exceeded');
}
Track Visited Nodes
function traverse($node, array &$visited = []): void
{
$id = spl_object_id($node);
if (isset($visited[$id])) {
return;
}
$visited[$id] = true;
// ...
}
Recursion Depth Limit
function process($data, int $depth = 0): mixed
{
if ($depth > 100) {
throw new RuntimeException('Max recursion depth');
}
return process($child, $depth + 1);
}
Output Format
### Infinite Loop Risk: [Description]
**Severity:** π΄/π /π‘
**Location:** `file.php:line`
**Type:** [Missing Break|Wrong Variable|Unbounded Recursion|...]
**Issue:**
[Description of how infinite loop can occur]
**Code:**
```php
// Problematic code
Fix:
// With proper termination
Trigger Condition: [When this infinite loop would occur]
More from dykyi-roman/awesome-claude-code
psr-overview-knowledge
PHP Standards Recommendations (PSR) overview knowledge base. Provides comprehensive reference for all accepted PSRs including PSR-1,3,4,6,7,11,12,13,14,15,16,17,18,20. Use for PSR selection decisions and compliance audits.
22detect-code-smells
Detects code smells in PHP codebases. Identifies God Class, Feature Envy, Data Clumps, Long Parameter List, Long Method, Primitive Obsession, Message Chains, Inappropriate Intimacy. Generates actionable reports with refactoring recommendations.
15clean-arch-knowledge
Clean Architecture knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Clean Architecture and Hexagonal Architecture audits.
15ddd-knowledge
DDD architecture knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Domain-Driven Design audits.
14testing-knowledge
Testing knowledge base for PHP 8.4 projects. Provides testing pyramid, AAA pattern, naming conventions, isolation principles, DDD testing guidelines, and PHPUnit patterns.
12bug-root-cause-finder
Root cause analysis methods for PHP bugs. Provides 5 Whys technique, fault tree analysis, git bisect guidance, and stack trace parsing.
12