$sanitized_line) { $line_number = $line_num + 1; // Skip if the line is empty in sanitized version (was a comment) if (trim($sanitized_line) === '') { continue; } // Check for php_is_subclass_of( usage (but not Manifest::is_subclass_of) if (preg_match('/\bis_subclass_of\s*\(/i', $sanitized_line) && !preg_match('/Manifest\s*::\s*is_subclass_of\s*\(/i', $sanitized_line)) { // Check if it's part of a sanity check (has Exception or shouldnt_happen within 2 lines) $is_sanity_check = false; // Check current line and next 2 lines for Exception( or shouldnt_happen( for ($i = 0; $i <= 2; $i++) { $check_line_num = $line_num + $i; // Check sanitized line for exception patterns if (isset($sanitized_lines[$check_line_num])) { $check_line = $sanitized_lines[$check_line_num]; if (preg_match('/\b(throw\s+new\s+)?[A-Za-z]*Exception\s*\(/i', $check_line) || preg_match('/\bshouldnt_happen\s*\(/i', $check_line)) { $is_sanity_check = true; break; } } } if (!$is_sanity_check) { $original_line = $original_lines[$line_num] ?? $sanitized_line; $this->add_violation( $file_path, $line_number, 'is_subclass_of() is not allowed. Use Manifest for inheritance checks.', trim($original_line), "Use Manifest methods for inheritance checks:\n\n" . "1. To check if a class extends another:\n" . " - Use: \$metadata = Manifest::php_get_metadata_by_class(\$class);\n" . " - Check: \$metadata['extends'] === 'BaseClass'\n\n" . "2. To find all classes extending a base:\n" . " - Use: Manifest::php_get_extending('BaseClass')\n\n" . "3. For sanity checks (if this truly shouldn't happen):\n" . " - Must be followed by exception within 2 lines\n" . " - Use: if (!is_subclass_of(\$class, \$base)) { shouldnt_happen('...'); }\n\n" . 'The Manifest contains all inheritance information at build time. ' . 'Runtime reflection is unnecessary and slower.', 'high' ); } } } } }