map(fn (string $path) => realpath($path) ?: $path)->all(); $roleValuePattern = '(owner|manager|operator|readonly)'; $patterns = [ // $membership->role === 'owner' / !== 'owner' '/->role\s*(===|==|!==|!=)\s*[\"\']?'.$roleValuePattern.'[\"\']?/i', // $role === 'owner' '/\$role\s*(===|==|!==|!=)\s*[\"\']?'.$roleValuePattern.'[\"\']?/i', // case 'owner': '/\bcase\s*[\"\']?'.$roleValuePattern.'[\"\']?\s*:/i', // match (...) { 'owner' => ... } '/\bmatch\b[\s\S]*?\{[\s\S]*?[\"\']?'.$roleValuePattern.'[\"\']?\s*=>/i', ]; $filesystem = new Filesystem; $violations = []; foreach ($filesystem->allFiles(app_path()) as $file) { $path = $file->getRealPath(); if ($path === false) { continue; } if (in_array($path, $allowedFiles, true)) { continue; } $contents = $filesystem->get($path); foreach ($patterns as $pattern) { if (preg_match($pattern, $contents) === 1) { $violations[] = $path; break; } } } if ($violations !== []) { throw new RuntimeException('Role-string checks must live in RoleCapabilityMap / TenantMembershipManager only. Offenders: '.implode(', ', $violations)); } expect($violations)->toBeEmpty(); });