Cpf.php 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. <?php
  2. namespace App\ValueObjects;
  3. use App\Casts\CpfCast;
  4. use Closure;
  5. use Illuminate\Contracts\Database\Eloquent\Castable;
  6. use InvalidArgumentException;
  7. use JsonSerializable;
  8. use Stringable;
  9. final readonly class Cpf implements Castable, JsonSerializable, Stringable
  10. {
  11. private string $value;
  12. public function __construct(string $value)
  13. {
  14. $digits = preg_replace('/\D/', '', $value);
  15. if ($digits === null || ! self::isValid($digits)) {
  16. throw new InvalidArgumentException(__('validation.cpf'));
  17. }
  18. $this->value = $digits;
  19. }
  20. public static function castUsing(array $arguments): string
  21. {
  22. return CpfCast::class;
  23. }
  24. public static function rule(): Closure
  25. {
  26. return static function (string $attribute, mixed $value, Closure $fail): void {
  27. if (! is_string($value)) {
  28. $fail(__('validation.cpf'));
  29. return;
  30. }
  31. try {
  32. new self($value);
  33. } catch (InvalidArgumentException) {
  34. $fail(__('validation.cpf'));
  35. }
  36. };
  37. }
  38. public function value(): string
  39. {
  40. return $this->value;
  41. }
  42. public function formatted(): string
  43. {
  44. return preg_replace(
  45. '/(\d{3})(\d{3})(\d{3})(\d{2})/',
  46. '$1.$2.$3-$4',
  47. $this->value,
  48. );
  49. }
  50. public function __toString(): string
  51. {
  52. return $this->value;
  53. }
  54. public function jsonSerialize(): string
  55. {
  56. return $this->value;
  57. }
  58. private static function isValid(string $digits): bool
  59. {
  60. if (strlen($digits) !== 11 || preg_match('/^(\d)\1{10}$/', $digits)) {
  61. return false;
  62. }
  63. for ($position = 9; $position < 11; $position++) {
  64. $sum = 0;
  65. for ($index = 0; $index < $position; $index++) {
  66. $sum += (int) $digits[$index] * (($position + 1) - $index);
  67. }
  68. $checkDigit = (($sum * 10) % 11) % 10;
  69. if ((int) $digits[$position] !== $checkDigit) {
  70. return false;
  71. }
  72. }
  73. return true;
  74. }
  75. }