Cnpj.php 2.4 KB

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