EnumUtil.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <?php
  2. namespace app\utils;
  3. use ReflectionClass;
  4. /**
  5. * 智能枚举验证器
  6. * 自动发现并验证请求参数中的枚举字段
  7. */
  8. class EnumUtil
  9. {
  10. /**
  11. * 自动验证请求参数中的枚举字段
  12. *
  13. * @param string|object $enumClassOrInstance 枚举类名或实例
  14. * @param array $requestData 请求数据
  15. * @return array 验证结果 ['valid' => bool, 'errors' => array]
  16. */
  17. public static function autoValidate($enumClassOrInstance, array $requestData): array
  18. {
  19. $errors = [];
  20. try {
  21. $reflection = new ReflectionClass($enumClassOrInstance);
  22. $constants = $reflection->getConstants();
  23. foreach ($constants as $constName => $enumValues) {
  24. if (!is_array($enumValues)) {
  25. continue;
  26. }
  27. // 跳过MAP格式的枚举定义(包含code字段的)
  28. if (self::isMapFormat($enumValues)) {
  29. continue;
  30. }
  31. // 将枚举常量名转换为可能的请求字段名
  32. $fieldName = self::constToFieldName($constName);
  33. // 检查请求数据中是否存在该字段
  34. if (array_key_exists($fieldName, $requestData)) {
  35. $value = $requestData[$fieldName];
  36. $result = self::validateEnumValue($value, $enumValues, $constName);
  37. if (!$result['valid']) {
  38. $errors[] = $result['error'];
  39. }
  40. }
  41. }
  42. } catch (\ReflectionException $e) {
  43. return [
  44. 'valid' => false,
  45. 'errors' => ["枚举验证异常: " . $e->getMessage()]
  46. ];
  47. }
  48. if (!empty($errors)) {
  49. return [
  50. 'valid' => false,
  51. 'errors' => $errors
  52. ];
  53. }
  54. return [
  55. 'valid' => true,
  56. 'errors' => []
  57. ];
  58. }
  59. /**
  60. * 将枚举常量名转换为字段名
  61. *
  62. * @param string $constName 常量名称
  63. * @return string 字段名
  64. */
  65. private static function constToFieldName(string $constName): string
  66. {
  67. // 将下划线命名转换为蛇形命名(小写+下划线)
  68. return strtolower($constName);
  69. }
  70. /**
  71. * 验证单个枚举值
  72. *
  73. * @param mixed $value 要验证的值
  74. * @param array $enumValues 枚举值数组
  75. * @param string $constName 常量名称
  76. * @return array 验证结果 ['valid' => bool, 'error' => string|null]
  77. */
  78. private static function validateEnumValue($value, array $enumValues, string $constName): array
  79. {
  80. if (!is_array($enumValues) || empty($enumValues)) {
  81. return [
  82. 'valid' => false,
  83. 'error' => '枚举值配置错误'
  84. ];
  85. }
  86. // 提取所有有效值(简单格式的键)
  87. $validValues = array_keys($enumValues);
  88. // 处理字符串数字和整数的类型转换
  89. $normalizedValue = is_numeric($value) ? (int)$value : $value;
  90. if (!in_array($normalizedValue, $validValues, true)) {
  91. $fieldName = self::constToFieldName($constName);
  92. return [
  93. 'valid' => false,
  94. 'error' => "{$fieldName}参数错误,值:{$value}"
  95. ];
  96. }
  97. return [
  98. 'valid' => true,
  99. 'error' => null
  100. ];
  101. }
  102. /**
  103. * 获取枚举类的所有字段映射
  104. *
  105. * @param string|object $enumClassOrInstance 枚举类名或实例
  106. * @return array 字段映射 [字段名 => 枚举值数组]
  107. */
  108. public static function getFieldMappings($enumClassOrInstance): array
  109. {
  110. $mappings = [];
  111. try {
  112. $reflection = new ReflectionClass($enumClassOrInstance);
  113. $constants = $reflection->getConstants();
  114. foreach ($constants as $constName => $enumValues) {
  115. if (is_array($enumValues) && !self::isMapFormat($enumValues)) {
  116. $fieldName = self::constToFieldName($constName);
  117. $mappings[$fieldName] = $enumValues;
  118. }
  119. }
  120. } catch (\ReflectionException $e) {
  121. // 忽略异常,返回空数组
  122. }
  123. return $mappings;
  124. }
  125. /**
  126. * 检查枚举值数组是否为MAP格式(包含code字段)
  127. *
  128. * @param array $enumValues 枚举值数组
  129. * @return bool
  130. */
  131. private static function isMapFormat(array $enumValues): bool
  132. {
  133. // 如果是空数组,跳过
  134. if (empty($enumValues)) {
  135. return false;
  136. }
  137. // 检查第一个元素是否包含code字段
  138. $firstValue = reset($enumValues);
  139. return is_array($firstValue) && isset($firstValue['code']);
  140. }
  141. }