#!/usr/bin/env php null, 'label' => null, 'description' => null]; // Skip whitespace to opening parenthesis while ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_WHITESPACE) { $i++; } // Should be at opening parenthesis if ($i >= $count || $tokens[$i] !== '(') { return null; } $i++; // Skip whitespace to category name while ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_WHITESPACE) { $i++; } // Extract category name (first argument - string) if ($i < $count && is_array($tokens[$i]) && ($tokens[$i][0] === T_CONSTANT_ENCAPSED_STRING)) { $category['name'] = trim($tokens[$i][1], '\'"'); $i++; } else { return null; // No category name found } // Now look for the array with label and description // Skip to 'array' keyword or '[' while ($i < $count) { $token = $tokens[$i]; if (is_array($token) && $token[0] === T_ARRAY) { // Found 'array(' syntax $i++; // Skip to opening parenthesis while ($i < $count && $tokens[$i] !== '(') { $i++; } if ($i < $count) { $i++; // Move past '(' $extracted = extract_array_contents($tokens, $i); $category = array_merge($category, $extracted); } break; } elseif ($token === '[') { // Found '[' short array syntax $i++; $extracted = extract_array_contents($tokens, $i); $category = array_merge($category, $extracted); break; } $i++; } return $category; } /** * Extract label and description from array tokens. * * @param array $tokens All tokens * @param int $start_pos Position after array opening * @return array Array with label and description keys */ function extract_array_contents($tokens, $start_pos) { $result = ['label' => null, 'description' => null]; $i = $start_pos; $count = count($tokens); $depth = 1; // Track nested arrays/parentheses while ($i < $count && $depth > 0) { $token = $tokens[$i]; // Track depth for nested structures if ($token === '(' || $token === '[') { $depth++; } elseif ($token === ')' || $token === ']') { $depth--; if ($depth === 0) break; } // Look for 'label' or 'description' keys if (is_array($token) && $token[0] === T_CONSTANT_ENCAPSED_STRING) { $key = trim($token[1], '\'"'); if ($key === 'label' || $key === 'description') { // Skip to the value (past '=>' and whitespace) $i++; // Skip whitespace while ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_WHITESPACE) { $i++; } // Skip '=>' (T_DOUBLE_ARROW) if ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_DOUBLE_ARROW) { $i++; } // Skip more whitespace while ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_WHITESPACE) { $i++; } // Check for __() translation function if ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_STRING && $tokens[$i][1] === '__') { // Skip to opening parenthesis of __() while ($i < $count && $tokens[$i] !== '(') { $i++; } if ($i < $count) { $depth++; // Track the opening paren $i++; // Move past '(' } // Skip whitespace while ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_WHITESPACE) { $i++; } // Extract string value if ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_CONSTANT_ENCAPSED_STRING) { $result[$key] = trim($tokens[$i][1], '\'"'); } } elseif ($i < $count && is_array($tokens[$i]) && $tokens[$i][0] === T_CONSTANT_ENCAPSED_STRING) { // Direct string value $result[$key] = trim($tokens[$i][1], '\'"'); } // Continue to next iteration without incrementing again continue; } } $i++; } return $result; } /** * Validate category data. * * @param array $category Category data * @param array &$errors Error messages array * @param array &$warnings Warning messages array */ function validate_category($category, &$errors, &$warnings) { $name = $category['name']; // Validate category name format (kebab-case) if (!preg_match('/^[a-z0-9-]+$/', $name)) { $errors[] = "Invalid category name format: '{$name}'. Must be kebab-case (lowercase, hyphens allowed)"; } // Check required fields if (empty($category['label'])) { $errors[] = "Missing required field 'label' in category '{$name}'"; } else { // Validate label quality if (strlen(trim($category['label'])) < 2) { $warnings[] = "Label for category '{$name}' is too short. Provide a meaningful label."; } } if (empty($category['description'])) { $errors[] = "Missing required field 'description' in category '{$name}'"; } else { // Validate description quality $desc_length = strlen(trim($category['description'])); if ($desc_length < 15) { $warnings[] = "Description for category '{$name}' is too short ({$desc_length} chars). Provide detailed information about what abilities belong in this category."; } if (stripos($category['description'], 'TODO') !== false) { $warnings[] = "Description for category '{$name}' contains TODO placeholder. Replace with actual description."; } } } /** * Output validation results. * * @param string $file_path File being validated * @param array $errors Error messages * @param array $warnings Warning messages */ function output_results($file_path, $errors, $warnings) { echo str_repeat('=', 70) . "\n"; echo "Validation Results: {$file_path}\n"; echo str_repeat('=', 70) . "\n\n"; if (!empty($errors)) { echo "ERRORS (" . count($errors) . "):\n"; foreach ($errors as $error) { echo " ✗ {$error}\n"; } echo "\n"; } if (!empty($warnings)) { echo "WARNINGS (" . count($warnings) . "):\n"; foreach ($warnings as $warning) { echo " ⚠ {$warning}\n"; } echo "\n"; } if (empty($errors) && empty($warnings)) { echo "✓ All validations passed! No issues found.\n\n"; } elseif (empty($errors)) { echo "✓ Validation passed with warnings.\n\n"; } else { echo "✗ Validation failed. Please fix the errors above.\n\n"; } }