PhoneNumber.php 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. <?php
  2. declare(strict_types=1);
  3. namespace app\miniapi\support;
  4. /** Shared telephone format policy. Format validation does not verify ownership or reachability. */
  5. final class PhoneNumber
  6. {
  7. public static function normalize(string $phone): string
  8. {
  9. $phone = trim($phone);
  10. if (!preg_match('/^\+?(?:[0-9 -]|\([0-9]+\))+$/D', $phone)) {
  11. throw new \InvalidArgumentException('联系电话格式无效');
  12. }
  13. $normalized = str_replace([' ', '-', '(', ')'], '', $phone);
  14. $pattern = str_starts_with($normalized, '+') ? '/^\+[1-9][0-9]{6,14}$/D' : '/^[0-9]{7,15}$/D';
  15. if (!preg_match($pattern, $normalized) || preg_match('/^0+$/D', $normalized)) {
  16. throw new \InvalidArgumentException('联系电话须包含7至15位数字');
  17. }
  18. return $normalized;
  19. }
  20. public static function mask(string $phone): string
  21. {
  22. if (trim($phone) === '') {
  23. return '';
  24. }
  25. try {
  26. $normalized = self::normalize($phone);
  27. } catch (\InvalidArgumentException) {
  28. // Historical unrecognized values must never fall back to plaintext.
  29. return '****';
  30. }
  31. $prefix = str_starts_with($normalized, '+') ? '+' : '';
  32. $digits = ltrim($normalized, '+');
  33. $length = strlen($digits);
  34. $visibleStart = $length >= 10 ? 3 : 2;
  35. $visibleEnd = $length >= 10 ? 4 : 2;
  36. return $prefix . substr($digits, 0, $visibleStart)
  37. . str_repeat('*', $length - $visibleStart - $visibleEnd)
  38. . substr($digits, -$visibleEnd);
  39. }
  40. }