Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2d2b8f4
docs(cnpj-val): create changelog for v1
juliolmuller May 15, 2026
b05b893
chore: disable linting rule
juliolmuller May 23, 2026
4075ed4
chore(cnpj-val): setup Pest over PhpUnit
juliolmuller May 23, 2026
0e04118
chore(cnpj-val): update package description
juliolmuller May 23, 2026
31abe24
feat(cnpj-val): update package namespaces
juliolmuller May 23, 2026
4bc3541
chore(cnpj-val): define package dependencies
juliolmuller May 23, 2026
15eb7d5
feat(cnpj-val): create specific exceptions and errors for package
juliolmuller May 23, 2026
4f21d3d
feat(cnpj-val): create options manager for validator
juliolmuller May 23, 2026
dc49f5d
feat(cnpj-val): update validator
juliolmuller May 23, 2026
824f178
feat(cnpj-val): update helper
juliolmuller May 23, 2026
e429231
test(cnpj-val): add unit tests with Pest notation
juliolmuller May 23, 2026
7a917ca
test(cnpj-val): drop legacy tests
juliolmuller May 23, 2026
632ed1c
docs(cnpj-val): update README for v2
juliolmuller May 23, 2026
5b8586d
docs(cnpj-val): create Portuguese version of README
juliolmuller May 23, 2026
ac7287b
docs(cnpj-val): update CHANGELOG with v2
juliolmuller May 23, 2026
a38adf1
chore(cnpj-val): drop unused dependency
juliolmuller May 23, 2026
01c9b43
test(cnpj-val): fix erroneous option name
juliolmuller May 23, 2026
21fce3c
perf(cnpj-val): optimize options instantiation
juliolmuller May 23, 2026
8677f11
perf(cnpj-val): replace multibyte string method
juliolmuller May 23, 2026
84bf18f
perf(cnpj-val): replace REGEX test by byte comparison
juliolmuller May 23, 2026
a8176f9
perf(cnpj-val): replace string concatenation by array appending
juliolmuller May 23, 2026
5463ab7
perf(cnpj-val): optimize instantiation of validator on helper function
juliolmuller May 23, 2026
0bfca6d
docs(cnpj-val): update documentations with key performance improvements
juliolmuller May 23, 2026
c60c047
docs(cnpj-val): fix subject-verb agreement
juliolmuller May 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .php-stan.config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ parameters:
identifiers:
- argument.type
- assign.propertyType
- booleanNot.alwaysFalse
- booleanNot.alwaysTrue
- callable.nonCallable
- method.alreadyNarrowedType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
cacheDirectory="vendor/.phpunit.cache/"
cacheDirectory="vendor/.pest.cache/"
beStrictAboutOutputDuringTests="true"
colors="true"
failOnRisky="true"
Expand All @@ -12,7 +12,7 @@
>
<testsuites>
<testsuite name="CNPJ Validator Test Suite">
<directory>tests/</directory>
<directory suffix=".spec.php">tests/</directory>
</testsuite>
</testsuites>
<source>
Expand All @@ -28,6 +28,6 @@
<env name="APP_ENV" value="testing"/>
</php>
<logging>
<junit outputFile="vendor/.phpunit.cache/test-results.xml"/>
<junit outputFile="vendor/.pest.cache/test-results.xml"/>
</logging>
</phpunit>
59 changes: 59 additions & 0 deletions packages/cnpj-val/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# lacus/cnpj-val

## 2.0.0

### 🎉 v2 at a glance 🎊

- 🆕 **Alphanumeric CNPJ** — Full support for the new [14-character alphanumeric CNPJ](https://www.gov.br/receitafederal/pt-br/assuntos/noticias/2023/julho/cnpj-alfa-numerico) (digits and letters).
- ⚙️ **`CnpjValidatorOptions`** — Configure `type` (`numeric` vs `alphanumeric`) and `caseSensitive` on the instance or per `isValid()` call.
- 🛡️ **Structured errors** — Typed exceptions (`CnpjValidatorTypeError`, `CnpjValidatorException` and their subclasses) for clearer error handling.

### BREAKING CHANGES

- **Namespace migration** — Public API moved from `Lacus\CnpjVal\` to `Lacus\BrUtils\Cnpj\`; update `use` imports for `CnpjValidator`, `CnpjValidatorOptions`, `CnpjValidationType`, and `cnpj_val()`.
- **Drop support to PHP v8.1**: Minimum version for the package is now **PHP 8.2** (`^8.2`). It may even run forcedly in earlier versions, but it's not recommended to keep running stale versions of PHP in production.
- **`CNPJ_LENGTH` constant** — Removed from `cnpj-val.php`; use `CnpjValidatorOptions::CNPJ_LENGTH` instead.
- **Default character set** — Default validation is **alphanumeric** (letters are kept after sanitization). For legacy numeric-only behavior, pass `type: 'numeric'` or `CnpjValidationType::Numeric`.
- **`cnpj_val()` / `isValid()` signatures** — Accept `string|list<string>` and optional `CnpjValidatorOptions` (or named `type` / `caseSensitive`); v1 accepted only a single `string`.
- **Invalid input** — Non-string / non–`string[]` values throw `CnpjValidatorInputTypeError` instead of a native `TypeError`.
- **Check-digit engine** — Validation delegates to `CnpjCheckDigits` from **`lacus/cnpj-dv`** `^1.1` (v1 calculated digits inline via `CnpjGeneratorVerifierDigit` from **`lacus/cnpj-gen`** only).

### New features

- **`CnpjValidatorOptions`** — Options object with property access, `getAll()`, `set()`, and `overrides` merging (constructor and per-call).
- **`CnpjValidationType`** — Backed enum (`Alphanumeric`, `Numeric`) for the `type` option.
- **`caseSensitive` option** — When `false`, lowercase letters are uppercased before alphanumeric validation.
- **`getOptions()`** — Returns the shared default `CnpjValidatorOptions` instance used by `CnpjValidator`.
- **Array input** — `isValid()` and `cnpj_val()` concatenate a `list<string>` (e.g. grouped or formatted segments).
- **Verifier-digit rule** — Rejects CNPJs whose last two characters are not digits (`0`–`9`).
- **Exception hierarchy** — `CnpjValidatorInputTypeError`, `CnpjValidatorOptionsTypeError`, `CnpjValidatorOptionTypeInvalidException`, plus `getName()` on base error classes.

### Improvements

- **`cnpj_val()` reuse** — The instance of `CnpjValidator` is kept alive and reused across multiple calls.
- **Dependency alignment**: now depends on `lacus/cnpj-dv` and `lacus/utils`.
- **Autoload alignment**: package autoload namespace updated to the BR Utils standard.
- **Docs refresh**: English and pt-BR READMEs updated with v2 API and usage guidance.

## 1.0.0

### Stable v1 API

First stable release of **`lacus/cnpj-val`** focused on CNPJ checksum validation.

- **API namespace**: `Lacus\CnpjVal\`.
- **Main resources**:
- `cnpj_val()`
- `CnpjValidator`
- `CNPJ_LENGTH`
- **Validation scope**:
- 14-digit CNPJ with first and second verifier digits checked against the official algorithm.
- Masked or plain input supported by stripping non-digits before validation.
- **Verifier digits**:
- Calculations reuse `CnpjGeneratorVerifierDigit` from **`lacus/cnpj-gen`** `^1.0`.
- **Return model**:
- Ordinary validation failures return `false` (no exceptions for invalid CNPJ).
- **Runtime**:
- PHP `>=8.1`.
- **Testing**:
- PHPUnit-based suite.
205 changes: 132 additions & 73 deletions packages/cnpj-val/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,28 @@
[![Last Update Date](https://img.shields.io/github/last-commit/LacusSolutions/br-utils-php)](https://github.com/LacusSolutions/br-utils-php)
[![Project License](https://img.shields.io/github/license/LacusSolutions/br-utils-php)](https://github.com/LacusSolutions/br-utils-php/blob/main/LICENSE)

Utility function/class to validate CNPJ (Brazilian employer ID).
> 🚀 **Full support for the [new alphanumeric CNPJ format](https://github.com/user-attachments/files/23937961/calculodvcnpjalfanaumerico.pdf).**
Comment thread
juliolmuller marked this conversation as resolved.

> 🌎 [Acessar documentação em português](https://github.com/LacusSolutions/br-utils-php/blob/main/packages/cnpj-val/README.pt.md)

A PHP utility to validate CNPJ (Brazilian Business Tax ID) values.

## PHP Support

| ![PHP 8.1](https://img.shields.io/badge/PHP-8.1-777BB4?logo=php&logoColor=white) | ![PHP 8.2](https://img.shields.io/badge/PHP-8.2-777BB4?logo=php&logoColor=white) | ![PHP 8.3](https://img.shields.io/badge/PHP-8.3-777BB4?logo=php&logoColor=white) | ![PHP 8.4](https://img.shields.io/badge/PHP-8.4-777BB4?logo=php&logoColor=white) |
|--- | --- | --- | --- |
| ![PHP 8.2](https://img.shields.io/badge/PHP-8.2-777BB4?logo=php&logoColor=white) | ![PHP 8.3](https://img.shields.io/badge/PHP-8.3-777BB4?logo=php&logoColor=white) | ![PHP 8.4](https://img.shields.io/badge/PHP-8.4-777BB4?logo=php&logoColor=white) | ![PHP 8.5](https://img.shields.io/badge/PHP-8.5-777BB4?logo=php&logoColor=white) |
| --- | --- | --- | --- |
| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ |

## Features

- ✅ **Alphanumeric CNPJ**: Validates 14-character CNPJ in numeric or alphanumeric format
- ✅ **Flexible input**: Accepts `string` or `list<string>`; array elements are concatenated in order
- ✅ **Format agnostic**: Strips non-alphanumeric characters (or non-digits when `type` is `numeric`) and optionally uppercases letters
- ✅ **Optional case sensitivity**: When `caseSensitive` is `false`, lowercase letters are accepted for alphanumeric CNPJ
- ✅ **Per-call override model**: Instance defaults can be overridden for one `isValid()` call only
- ✅ **Typed option validation**: Dedicated `TypeError` / `Exception` subclasses for invalid option or input usage
- ✅ **Dual API style**: Object-oriented (`CnpjValidator`) and functional (`cnpj_val()`)

## Installation

```bash
Expand All @@ -26,121 +40,166 @@ $ composer require lacus/cnpj-val

```php
<?php
// Using class-based resource
use Lacus\CnpjVal\CnpjValidator;

// Or using function-based one
use function Lacus\CnpjVal\cnpj_val;
```
use Lacus\BrUtils\Cnpj\CnpjValidator;
use Lacus\BrUtils\Cnpj\CnpjValidatorOptions;
use Lacus\BrUtils\Cnpj\Enums\CnpjValidationType;

## Usage
use function Lacus\BrUtils\Cnpj\cnpj_val;
```

### Object-Oriented Usage
## Quick start

```php
<?php

use Lacus\BrUtils\Cnpj\CnpjValidator;

$validator = new CnpjValidator();
$cnpj = '98765432000198';

echo $validator->isValid($cnpj) ? 'Valid' : 'Invalid'; // returns 'Valid'
$validator->isValid('98765432000198'); // true
$validator->isValid('98.765.432/0001-98'); // true
$validator->isValid('98765432000199'); // false

$cnpj = '98.765.432/0001-98';
echo $validator->isValid($cnpj) ? 'Valid' : 'Invalid'; // returns 'Valid'
$validator->isValid('1QB5UKALPYFP59'); // true (alphanumeric)
$validator->isValid('1QB5UKALpyfp59'); // false (default is case-sensitive)
$validator->isValid('1QB5UKALpyfp59', caseSensitive: false); // true

$cnpj = '98765432000199';
echo $validator->isValid($cnpj) ? 'Valid' : 'Invalid'; // returns 'Invalid'
$validator->isValid('96206256120884'); // true (numeric)
$validator->isValid('1QB5UKALPYFP59', type: CnpjValidationType::Numeric); // false
```

### Imperative programming
Functional helper:

```php
cnpj_val('98765432000198'); // true
cnpj_val('98.765.432/0001-98'); // true
cnpj_val('98765432000199'); // false
```

## Usage

The main entry points are the class `CnpjValidator`, the options value object `CnpjValidatorOptions`, the enum `CnpjValidationType`, and the helper `cnpj_val()`.

### `CnpjValidator`

The helper function `cnpj_val()` is just a functional abstraction. Internally it creates an instance of `CnpjValidator` and calls the `isValid()` method right away.
- **`__construct`**: `new CnpjValidator(?CnpjValidatorOptions $options = null, $type = null, $caseSensitive = null)`

If `$options` is a `CnpjValidatorOptions` instance, that same instance is stored internally (mutations later affect future `isValid()` calls with no per-call override). Otherwise, a new options object is built from named values.

- **`getOptions()`**: Returns the internal `CnpjValidatorOptions` instance.
- **`isValid`**: `isValid(string|list<string> $cnpjInput, ?CnpjValidatorOptions $options = null, $type = null, $caseSensitive = null): bool`

Per-call options are merged over instance defaults only for that call. Returns `true` when the sanitized input has exactly **14** characters, the last two are digits, and check digits match (`CnpjCheckDigits` from **`lacus/cnpj-dv`**). Otherwise returns `false` (invalid CNPJ, wrong length, ineligible base/branch, etc.) without throwing.

If the input is not a `string` or a `list` of strings, **`CnpjValidatorInputTypeError`** is thrown.

```php
$cnpj = '98765432000198';
<?php

echo cnpj_val($cnpj) ? 'Valid' : 'Invalid'; // returns 'Valid'
use Lacus\BrUtils\Cnpj\CnpjValidator;
use Lacus\BrUtils\Cnpj\Enums\CnpjValidationType;

echo cnpj_val('98.765.432/0001-98') ? 'Valid' : 'Invalid'; // returns 'Valid'
$validator = new CnpjValidator(type: CnpjValidationType::Numeric);

echo cnpj_val('98765432000199') ? 'Valid' : 'Invalid'; // returns 'Invalid'
$validator->isValid('98.765.432/0001-98'); // true
$validator->isValid('1QB5UKALPYFP59'); // false (letters stripped → length ≠ 14)
$validator->isValid('1QB5UKALpyfp59', type: CnpjValidationType::Alphanumeric, caseSensitive: false); // true
```

### Validation Examples
Default options on the instance; per-call overrides:

```php
// Valid CNPJ numbers
cnpj_val('98765432000198') // returns true
cnpj_val('98.765.432/0001-98') // returns true
cnpj_val('03603568000195') // returns true

// Invalid CNPJ numbers
cnpj_val('98765432000199') // returns false
cnpj_val('12345678901234') // returns false
cnpj_val('00000000000000') // returns false
cnpj_val('11111111111111') // returns false
cnpj_val('123') // returns false (too short)
cnpj_val('') // returns false (empty)
$validator = new CnpjValidator(caseSensitive: false);

$validator->isValid('1qb5ukalpyfp59'); // true (instance defaults)
$validator->isValid('1qb5ukalpyfp59', caseSensitive: true); // this call only: false
$validator->isValid('1qb5ukalpyfp59'); // true again
```

## Features
### `CnpjValidatorOptions`

- ✅ **Format Agnostic**: Accepts CNPJ with or without formatting (dots, slashes, dashes)
- ✅ **Strict Validation**: Validates both check digits according to Brazilian CNPJ algorithm
- ✅ **Type Safety**: Built with PHP 8.1+ strict types
- ✅ **Lightweight**: Minimal dependencies, only requires `lacus/cnpj-gen` for check digit calculation
- ✅ **Dual API**: Both object-oriented and functional programming styles supported
Holds validator settings (`type`, `caseSensitive`). Construct with named parameters and optional `overrides` (list of arrays and/or other `CnpjValidatorOptions` instances, merged in order). Exposes properties via magic `__get` / `__set`.

## API Reference
- **`getAll()`**: Returns a shallow array snapshot of all options.
- **`set(...)`**: Updates multiple fields at once; returns `$this`.

### CnpjValidator Class
### `CnpjValidationType`

#### `isValid(string $cnpjString): bool`
Backed enum for the `type` option:

Validates a CNPJ string and returns `true` if valid, `false` otherwise.
- `CnpjValidationType::Alphanumeric` (`"alphanumeric"`) — default; keeps `0`–`9` and `A`–`Z` after sanitization.
- `CnpjValidationType::Numeric` (`"numeric"`) — legacy numeric-only CNPJ; strips everything except `0`–`9`.

**Parameters:**
- `$cnpjString` (string): The CNPJ to validate (with or without formatting)
Helper methods:

**Returns:**
- `bool`: `true` if the CNPJ is valid, `false` otherwise
- `CnpjValidationType::values(): list<string>`
- `toSequenceType(): SequenceType` (from **`lacus/utils`**)

### cnpj_val() Function
String literals `'alphanumeric'` and `'numeric'` are also accepted wherever `type` is documented.

#### `cnpj_val(string $cnpjString): bool`
### Functional helper

Functional wrapper around `CnpjValidator::isValid()`.
`cnpj_val()` runs the validation on a `CnpjValidator` instance with the same arguments passed to the function. Use named arguments for options:

**Parameters:**
- `$cnpjString` (string): The CNPJ to validate (with or without formatting)
```php
cnpj_val('98765432000198'); // true
cnpj_val('1QB5UKALpyfp59', caseSensitive: false); // true
cnpj_val('1QB5UKALPYFP59', type: CnpjValidationType::Numeric); // false
```

**Returns:**
- `bool`: `true` if the CNPJ is valid, `false` otherwise
To pass a full options object as the second argument: `cnpj_val($cnpj, new CnpjValidatorOptions(type: CnpjValidationType::Numeric))`.

## Validation Algorithm
### Input formats

The package validates CNPJ using the official Brazilian algorithm:
**String:** Raw digits and/or letters, or formatted CNPJ (e.g. `98.765.432/0001-98`, `1Q.B5U.KAL/PYFP-59`). Characters are stripped according to `type`; when `caseSensitive` is `false`, letters are uppercased before alphanumeric validation.

1. **Length Check**: Ensures the CNPJ has exactly 14 digits
2. **First Check Digit**: Calculates and validates the 13th digit
3. **Second Check Digit**: Calculates and validates the 14th digit
4. **Format Tolerance**: Automatically strips non-numeric characters before validation
**Array of strings:** Each element must be a string; values are concatenated (e.g. per digit, grouped segments, or mixed with punctuation). Non-string elements throw **`CnpjValidatorInputTypeError`**.

## Error Handling
### Validation options

The validator is designed to be forgiving with input format but strict with validation:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `type` | `CnpjValidationType\|'alphanumeric'\|'numeric'\|null` | `CnpjValidationType::Alphanumeric` | Character set after sanitization: alphanumeric (`0`–`9`, `A`–`Z`) or numeric-only (`0`–`9`) |
| `caseSensitive` | `?bool` | `true` | When `false`, lowercase letters are uppercased before alphanumeric validation |

- Invalid formats (too short, too long) return `false`
- Invalid check digits return `false`
- Empty strings return `false`
- Non-numeric strings (after stripping formatting) return `false`
Invalid CNPJ (wrong length after sanitization, invalid check digits, ineligible base/branch `00000000` / `0000`, repeated digits, non-numeric verifier digits) returns **`false`** — no exception is thrown for validation failure.

### Errors & exceptions

This package uses **TypeError** for invalid option/input types and **Exception** for invalid option values. Validation failures return `false`.

- **Wrong input type** (not `string` or `list<string>`): **`CnpjValidatorInputTypeError`** — extends **`CnpjValidatorTypeError`** (extends PHP `TypeError`).
- **Invalid option types when constructing or merging options**: **`CnpjValidatorOptionsTypeError`**.
- **Invalid `type` value** (not `alphanumeric` / `numeric`): **`CnpjValidatorOptionTypeInvalidException`** — extends **`CnpjValidatorException`**.

```php
<?php

use Lacus\BrUtils\Cnpj\CnpjValidator;
use Lacus\BrUtils\Cnpj\Exceptions\CnpjValidatorInputTypeError;
use Lacus\BrUtils\Cnpj\Exceptions\CnpjValidatorOptionTypeInvalidException;

try {
(new CnpjValidator())->isValid(12345678000198);
} catch (CnpjValidatorInputTypeError $e) {
echo $e->getMessage();
}

try {
new CnpjValidator(type: 'invalid');
} catch (CnpjValidatorOptionTypeInvalidException $e) {
echo $e->getMessage();
}
```

## Dependencies
### Other available resources

- **PHP**: >= 8.1
- **lacus/cnpj-gen**: ^1.0 (for check digit calculation)
- **`CnpjValidatorOptions::CNPJ_LENGTH`**: `14` — standard CNPJ length after sanitization.

## Contribution & Support

We welcome contributions! Please see our [Contributing Guidelines](https://github.com/LacusSolutions/br-utils-php/blob/main/CONTRIBUTING.md) for details. But if you find this project helpful, please consider:
We welcome contributions! Please see our [Contributing Guidelines](https://github.com/LacusSolutions/br-utils-php/blob/main/CONTRIBUTING.md) for details. If you find this project helpful, please consider:

- ⭐ Starring the repository
- 🤝 Contributing to the codebase
Expand All @@ -149,7 +208,7 @@ We welcome contributions! Please see our [Contributing Guidelines](https://githu

## License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/LacusSolutions/br-utils-php/blob/main/LICENSE) file for details.
This project is licensed under the MIT License see the [LICENSE](https://github.com/LacusSolutions/br-utils-php/blob/main/LICENSE) file for details.

## Changelog

Expand Down
Loading
Loading