projectRootPath() . '/pages'; $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pagesDir)); $missing = []; /** @var \SplFileInfo $file */ foreach ($iterator as $file) { if (!$file->isFile() || $file->getExtension() !== 'php') { continue; } $relativePath = 'pages/' . ltrim(str_replace($pagesDir, '', $file->getPathname()), '/'); if ($this->isPrgExempt($relativePath)) { continue; } $content = (string) file_get_contents($file->getPathname()); if (!$this->handlesPostData($content)) { continue; } // Router::redirect (PRG) or Router::json (AJAX) are both valid responses. if (!str_contains($content, 'Router::redirect') && !str_contains($content, 'Router::json')) { $missing[] = $relativePath; } } sort($missing); $this->assertSame( [], $missing, "POST handlers missing Router::redirect() — PRG pattern required (GR-CORE-012):\n" . implode("\n", $missing) ); } private function isPrgExempt(string $path): bool { foreach (self::PRG_EXEMPT_PATTERNS as $pattern) { if (preg_match($pattern, $path)) { return true; } } return false; } private function handlesPostData(string $content): bool { return str_contains($content, "isMethod('POST')") || str_contains($content, 'hasBody(') || str_contains($content, 'bodyAll()') || preg_match('/->body\(/', $content) === 1; } }