$line) { $line_num = $idx + 1; // Simple check for PHP tags if (strpos($line, '') !== false) $in_php = false; // Alternative syntax checks if (preg_match('/\bif\b\s*\(.*\)\s*:/', $line)) { $stack[] = ['type' => 'if', 'line' => $line_num]; } elseif (preg_match('/\belseif\b\s*\(.*\)\s*:/', $line)) { // elseif is part of the current if block, so it doesn't change nesting level } elseif (preg_match('/\belse\b\s*:/', $line)) { // else is part of the current if block, so it doesn't change nesting level } elseif (preg_match('/foreach\s*\(.*\)\s*:/', $line)) { $stack[] = ['type' => 'foreach', 'line' => $line_num]; } if (strpos($line, 'endif;') !== false) { if (empty($stack)) { echo "Unexpected endif; at line $line_num\n"; } else { $last = array_pop($stack); if ($last['type'] !== 'if') { echo "Mismatched endif; at line $line_num (expected endforeach; for {$last['type']} at line {$last['line']})\n"; } } } if (strpos($line, 'endforeach;') !== false) { if (empty($stack)) { echo "Unexpected endforeach; at line $line_num\n"; } else { $last = array_pop($stack); if ($last['type'] !== 'foreach') { echo "Mismatched endforeach; at line $line_num (expected endif; for {$last['type']} at line {$last['line']})\n"; } } } } foreach ($stack as $unclosed) { echo "Unclosed {$unclosed['type']} starting at line {$unclosed['line']}\n"; }