lacus / br-utils
Utilities to deal with Brazilian-related data
Requires
- php: ^8.2
- lacus/cnpj-utils: ~2.0.0
- lacus/cpf-utils: ~1.1.0
Requires (Dev)
- pestphp/pest: ^3.8
- phpunit/phpunit: ^11.5
README
🚀 Full support for the new alphanumeric CNPJ format.
A PHP toolkit to handle the main operations with Brazilian-related data: CPF (Individual's Taxpayer ID) and CNPJ (Business Tax ID). It provides a top-level BrUtils wrapper around lacus/cpf-utils and lacus/cnpj-utils, exposing all bundled resources under unified namespaces.
PHP Support
| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ |
Features
- ✅ Unified top-level API: One
BrUtilsinstance with$cpfand$cnpjdomain accessors - ✅ Bundled domains:
lacus/cpf-utilsandlacus/cnpj-utilsinstalled together - ✅ Alphanumeric CNPJ: Full support for the new alphanumeric CNPJ format (introduced in 2026)
- ✅ Configurable defaults: Set formatter, generator, and (for CNPJ) validator options on each domain instance
- ✅ Per-call overrides: Override any component option for a single method call
- ✅ Dual API style: Top-level façade (
BrUtils), domain aggregators (CpfUtils,CnpjUtils), standalone components, and functional helpers - ✅ Shared namespaces: CPF symbols under
Lacus\BrUtils\Cpf\; CNPJ symbols underLacus\BrUtils\Cnpj\ - ✅ Typed error handling: Dedicated exception hierarchies from bundled packages (CNPJ v2
TypeError/Exceptionmodel; CPF v1InvalidArgumentExceptionfor invalid options)
Installation
# using Composer
$ composer require lacus/br-utils
This installs lacus/br-utils together with lacus/cpf-utils and lacus/cnpj-utils (which in turn pulls in the CNPJ component packages). You do not need separate composer require calls for the domain packages when using lacus/br-utils.
Import
Pick the API that fits your use case.
Top-level façade:
<?php use Lacus\BrUtils;
Domain aggregators:
<?php use Lacus\BrUtils\Cpf\CpfUtils; use Lacus\BrUtils\Cnpj\CnpjUtils;
CPF components (object-oriented):
<?php use Lacus\BrUtils\Cpf\CpfFormatter; use Lacus\BrUtils\Cpf\CpfFormatterOptions; use Lacus\BrUtils\Cpf\CpfGenerator; use Lacus\BrUtils\Cpf\CpfGeneratorOptions; use Lacus\BrUtils\Cpf\CpfValidator;
CNPJ components (object-oriented):
<?php use Lacus\BrUtils\Cnpj\CnpjFormatter; use Lacus\BrUtils\Cnpj\CnpjFormatterOptions; use Lacus\BrUtils\Cnpj\CnpjGenerator; use Lacus\BrUtils\Cnpj\CnpjGeneratorOptions; use Lacus\BrUtils\Cnpj\CnpjValidator; use Lacus\BrUtils\Cnpj\CnpjValidatorOptions; use Lacus\BrUtils\Cnpj\Enums\CnpjType as CnpjGenerationType; use Lacus\BrUtils\Cnpj\Enums\CnpjValidationType;
Functional helpers:
<?php use function Lacus\BrUtils\Cpf\cpf_fmt; use function Lacus\BrUtils\Cpf\cpf_gen; use function Lacus\BrUtils\Cpf\cpf_val; use function Lacus\BrUtils\Cnpj\cnpj_fmt; use function Lacus\BrUtils\Cnpj\cnpj_gen; use function Lacus\BrUtils\Cnpj\cnpj_val;
Quick start
With BrUtils (all-in-one):
<?php use Lacus\BrUtils; $utils = new BrUtils(); $cpf = '11144477735'; $cnpj = '03603568000195'; $utils->cpf->format($cpf); // '111.444.777-35' $utils->cpf->isValid($cpf); // true $utils->cpf->generate(); // e.g. '11508890048' $utils->cnpj->format($cnpj); // '03.603.568/0001-95' $utils->cnpj->isValid($cnpj); // true $utils->cnpj->generate(); // e.g. '1GJTR3J3XSSA96'
With domain aggregators:
<?php use Lacus\BrUtils\Cpf\CpfUtils; use Lacus\BrUtils\Cnpj\CnpjUtils; $cpf = '11144477735'; $cnpj = '03603568000195'; (new CpfUtils())->format($cpf); // '111.444.777-35' (new CnpjUtils())->format($cnpj); // '03.603.568/0001-95' (new CpfUtils())->isValid($cpf); // true (new CnpjUtils())->isValid($cnpj); // true
With functional helpers:
<?php use function Lacus\BrUtils\Cpf\cpf_fmt; use function Lacus\BrUtils\Cpf\cpf_val; use function Lacus\BrUtils\Cnpj\cnpj_fmt; use function Lacus\BrUtils\Cnpj\cnpj_val; $cpf = '11144477735'; $cnpj = '03603568000195'; cpf_fmt($cpf); // '111.444.777-35' cpf_val($cpf); // true cnpj_fmt($cnpj); // '03.603.568/0001-95' cnpj_val($cnpj); // true
Usage
You can work in four equivalent ways:
BrUtils— single instance with shared defaults across both CPF and CNPJ domains.- Domain aggregators —
CpfUtilsandCnpjUtilsdirectly (same classes used internally byBrUtils). - Component classes —
CpfFormatter,CnpjGenerator, and so on. - Functional helpers —
cpf_fmt(),cnpj_gen(), and related functions for one-off calls.
All approaches expose the same options and behavior within each domain. For full option tables and component-specific details, see the README of each bundled package.
BrUtils
-
__construct:new BrUtils($cpf = [], $cnpj = [])Each
$cpf/$cnpjargument may be a pre-builtCpfUtils/CnpjUtilsinstance or a configuration array spread into the corresponding utils constructor. Within that array, each resource key (formatter,generator, andvalidatorfor CNPJ) accepts either an options object or an associative array of option values.Example:
new BrUtils(cpf: ['formatter' => ['hidden' => true]], cnpj: ['validator' => ['type' => CnpjValidationType::Numeric]]). -
$cpf,$cnpj: Property-style access to the domain utils instances (CpfUtilsandCnpjUtils). -
getCpfUtils(),getCnpjUtils(): Return the internal domain instances for direct use.
<?php use Lacus\BrUtils; use Lacus\BrUtils\Cnpj\Enums\CnpjType as CnpjGenerationType; use Lacus\BrUtils\Cnpj\Enums\CnpjValidationType; $utils = new BrUtils(); $utils->cpf->format('11144477735'); // '111.444.777-35' $utils->cpf->isValid('11144477735'); // true $utils->cpf->generate(); // e.g. '11508890048' $utils->cnpj->format('03603568000195'); // '03.603.568/0001-95' $utils->cnpj->format('12ABC34500DE99'); // '12.ABC.345/00DE-99' $utils->cnpj->isValid('1QB5UKALPYFP59'); // true $utils->cnpj->generate(format: true); // e.g. 'V1.J0V.8WE/DVZ7-50' $utils->cnpj->generate( // e.g. '15381773354961' type: CnpjGenerationType::Numeric, );
Instance defaults and per-call overrides
$utils = new BrUtils( cpf: [ 'formatter' => ['hidden' => true, 'hiddenKey' => '#'], 'generator' => ['format' => true], ], cnpj: [ 'formatter' => ['hidden' => true, 'hiddenKey' => '#'], 'generator' => ['format' => true], 'validator' => ['type' => CnpjValidationType::Numeric], ], ); $cpf = '11144477735'; $cnpj = '03603568000195'; $utils->cpf->format($cpf); // '111.###.###-##' $utils->cpf->format($cpf, hidden: false); // '111.444.777-35' $utils->cpf->generate(format: false); // e.g. '58450042259' $utils->cnpj->format($cnpj); // '03.603.###/####-##' $utils->cnpj->format($cnpj, hidden: false); // '03.603.568/0001-95' $utils->cnpj->isValid('1QB5UKALPYFP59'); // false $utils->cnpj->isValid( // true '1QB5UKALPYFP59', type: CnpjValidationType::Alphanumeric, );
Passing a CnpjFormatterOptions, CnpjGeneratorOptions, or CnpjValidatorOptions instance to the BrUtils constructor stores that object by reference — mutating it later affects subsequent calls with no per-call override.
CPF operations
CPF methods are accessed via $utils->cpf, CpfUtils, or the cpf_*() helpers. CPF uses the v1 API from lacus/cpf-utils: string-only input, positional/named formatter and generator options, and no validator settings.
Formatting (format / cpf_fmt)
| Parameter | Type | Default | Description |
|---|---|---|---|
escape |
?bool |
false |
When true, HTML-escapes the final string |
hidden |
?bool |
false |
When true, replaces the inclusive index range [hiddenStart, hiddenEnd] on the normalized 11-digit string before punctuation is applied |
hiddenKey |
?string |
'*' |
Replacement for each hidden position |
hiddenStart |
?int |
3 |
Start index 0–10 (inclusive) |
hiddenEnd |
?int |
10 |
End index 0–10 (inclusive) |
dotKey |
?string |
'.' |
Separator between digit groups |
dashKey |
?string |
'-' |
Separator before the last two digits |
onFail |
?\Closure |
see below | Closure(mixed $value, Exception $e): string — used when sanitized length ≠ 11 |
Default onFail returns the original input unchanged. Invalid length does not throw from format().
$cpf = '11144477735'; $utils->cpf->format($cpf); // '111.444.777-35' $utils->cpf->format($cpf, hidden: true, hiddenKey: '#'); // '111.###.###-##' $utils->cpf->format($cpf, dotKey: '', dashKey: '_'); // '111444777_35' cpf_fmt($cpf, hidden: true); // '111.***.***-**'
Generation (generate / cpf_gen)
| Parameter | Type | Default | Description |
|---|---|---|---|
format |
?bool |
false |
When true, returns formatted CPF (000.000.000-00); otherwise returns compact 11-digit output |
prefix |
?string |
'' |
Base seed for generation. Non-digit characters are stripped; only the first 9 digits (indexes 0–8) are used |
$utils->cpf->generate(); // e.g. '11508890048' $utils->cpf->generate(format: true); // e.g. '661.134.831-00' $utils->cpf->generate(prefix: '123456789'); // '12345678909' cpf_gen(prefix: '123456789', format: true); // '123.456.789-09'
Validation (isValid / cpf_val)
Accepts formatted or unformatted CPF strings. Returns true or false without throwing for invalid CPF.
$utils->cpf->isValid('11144477735'); // true $utils->cpf->isValid('111.444.777-35'); // true $utils->cpf->isValid('11144477736'); // false cpf_val('11144477735'); // true
CNPJ operations
CNPJ methods are accessed via $utils->cnpj, CnpjUtils, or the cnpj_*() helpers. CNPJ uses the v2 API from lacus/cnpj-utils.
Formatting (format / cnpj_fmt)
Supports the same options as lacus/cnpj-fmt. Input accepts string or list<string>.
| Parameter | Type | Default | Description |
|---|---|---|---|
hidden |
?bool |
false |
When true, replaces the inclusive index range [hiddenStart, hiddenEnd] on the normalized 14-character string before punctuation is applied |
hiddenKey |
?string |
'*' |
Replacement for each hidden position (may be multi-character or empty); must not use disallowed key characters |
hiddenStart |
?int |
5 |
Start index 0–13 (inclusive) |
hiddenEnd |
?int |
13 |
End index 0–13 (inclusive); if hiddenStart > hiddenEnd, they are swapped |
dotKey |
?string |
'.' |
Separator between groups XX / XXX / XXX |
slashKey |
?string |
'/' |
Separator before the branch block |
dashKey |
?string |
'-' |
Separator before the last two characters |
escape |
?bool |
false |
When true, HTML-escapes the final string |
encode |
?bool |
false |
When true, URL-encodes the final string |
onFail |
?\Closure |
see below | Closure(mixed $value, CnpjFormatterException $e): string — used when sanitized length ≠ 14 |
Default onFail returns an empty string. Wrong input types throw CnpjFormatterInputTypeError.
$cnpj = '03603568000195'; $utils->cnpj->format($cnpj); // '03.603.568/0001-95' $utils->cnpj->format('12ABC34500DE99'); // '12.ABC.345/00DE-99' $utils->cnpj->format( // '03.603.###/####-##' $cnpj, hidden: true, hiddenKey: '#', ); $utils->cnpj->format( // '03603568|0001_95' $cnpj, dotKey: '', slashKey: '|', dashKey: '_', ); cnpj_fmt($cnpj); // '03.603.568/0001-95'
Generation (generate / cnpj_gen)
Supports the same options as lacus/cnpj-gen.
| Parameter | Type | Default | Description |
|---|---|---|---|
format |
?bool |
false |
When true, returns formatted CNPJ (XX.XXX.XXX/XXXX-XX); otherwise returns compact 14-character output |
prefix |
?string |
'' |
Base seed for generation. Non-alphanumeric chars are stripped, letters are uppercased, and only first 12 chars (indexes 0–11) are used; characters at index 12+ are ignored |
type |
CnpjGenerationType|'alphanumeric'|'alphabetic'|'numeric'|null |
CnpjGenerationType::Alphanumeric |
Character family used for generated base positions |
prefix validation rules:
- base ID
00000000is rejected (when first 8 chars are present) - branch ID
0000is rejected (when chars 9–12 are present) - 12 repeated numeric digits are rejected (e.g.
111111111111)
$utils->cnpj->generate(); // e.g. '1GJTR3J3XSSA96' $utils->cnpj->generate(format: true); // e.g. 'V1.J0V.8WE/DVZ7-50' $utils->cnpj->generate( // e.g. '12345678855883' prefix: '12345678', type: CnpjGenerationType::Numeric, );
Validation (isValid / cnpj_val)
Supports the same options as lacus/cnpj-val. Input accepts string or list<string>.
| Parameter | Type | Default | Description |
|---|---|---|---|
type |
CnpjValidationType|'alphanumeric'|'numeric'|null |
CnpjValidationType::Alphanumeric |
Character set after sanitization |
caseSensitive |
?bool |
true |
When false, lowercase letters are uppercased before alphanumeric validation |
$utils->cnpj->isValid('98765432000198'); // true $utils->cnpj->isValid('98765432000199'); // false $utils->cnpj->isValid('1QB5UKALPYFP59'); // true $utils->cnpj->isValid('1QB5UKALpyfp59'); // false $utils->cnpj->isValid( // true '1QB5UKALpyfp59', caseSensitive: false, ); $utils->cnpj->isValid( // false '1QB5UKALPYFP59', type: CnpjValidationType::Numeric, ); cnpj_val('98765432000198'); // true cnpj_val('1QB5UKALpyfp59', caseSensitive: false); // true cnpj_val( // false '1QB5UKALPYFP59', type: CnpjValidationType::Numeric, );
Invalid CNPJ returns false without throwing. Wrong input types throw CnpjValidatorInputTypeError.
Domain aggregators (standalone)
Use CpfUtils or CnpjUtils directly when you only need one domain:
<?php use Lacus\BrUtils\Cpf\CpfUtils; use Lacus\BrUtils\Cnpj\CnpjUtils; use Lacus\BrUtils\Cnpj\Enums\CnpjValidationType; $cpfUtils = new CpfUtils( formatter: ['hidden' => true], generator: ['format' => true], ); $cnpjUtils = new CnpjUtils( formatter: ['hidden' => true], generator: ['format' => true], validator: ['type' => CnpjValidationType::Numeric], ); $cpfUtils->format('11144477735'); // '111.***.***-**' $cnpjUtils->format('03603568000195'); // '03.603.***/****-**'
Accessing components
Each domain aggregator exposes its internal formatter, generator, and validator:
$utils = new BrUtils(); $utils->cpf->getFormatter()->format( // '111.***.***-**' '11144477735', hidden: true, ); $utils->cpf->getGenerator()->generate(format: true); // e.g. '545.507.690-68' $utils->cpf->getValidator()->isValid('11144477735'); // true $utils->cnpj->getFormatter()->format('12ABC34500DE99'); // '12.ABC.345/00DE-99' $utils->cnpj->getGenerator()->generate(format: true); // e.g. '8O.BE5.2KL/UI0Y-06' $utils->cnpj->getValidator()->isValid('03603568000195'); // true
Use getCpfUtils() / getCnpjUtils() on BrUtils, or the component getters on each domain utils instance, when you already have a configured instance and want the underlying component without creating a new one.
Mixing styles
Use BrUtils where a shared configuration helps, and standalone components or helpers elsewhere — they are the same underlying classes:
<?php use Lacus\BrUtils; use Lacus\BrUtils\Cnpj\CnpjFormatter; use Lacus\BrUtils\Cnpj\Enums\CnpjValidationType; use function Lacus\BrUtils\Cpf\cpf_fmt; use function Lacus\BrUtils\Cnpj\cnpj_val; $utils = new BrUtils(cnpj: ['validator' => ['type' => CnpjValidationType::Numeric]]); // Via façade $utils->cpf->format('11144477735'); // '111.444.777-35' // Via component returned by the façade $utils->cnpj->getFormatter()->format('12ABC34500DE99'); // '12.ABC.345/00DE-99' // Via a separate component instance (new CnpjFormatter())->format('03603568000195'); // '03.603.568/0001-95' // Via functional helpers cpf_fmt('11144477735'); // '111.444.777-35' cnpj_val('98.765.432/0001-98'); // true
Errors & exceptions
BrUtils does not define its own exception types; it propagates errors from the bundled packages:
- CPF formatting / generation:
InvalidArgumentExceptionfor invalid option types or values (e.g. out-of-rangehiddenStart, prefix longer than 9 digits). - CNPJ formatting:
CnpjFormatterInputTypeError,CnpjFormatterOptionsTypeError,CnpjFormatterOptionsHiddenRangeInvalidException,CnpjFormatterOptionsForbiddenKeyCharacterException, and related classes. - CNPJ generation:
CnpjGeneratorOptionsTypeError,CnpjGeneratorOptionPrefixInvalidException,CnpjGeneratorOptionTypeInvalidException, and related classes. - CNPJ validation:
CnpjValidatorInputTypeError,CnpjValidatorOptionsTypeError,CnpjValidatorOptionTypeInvalidException, and related classes.
Invalid option types on CNPJ are TypeError subclasses; invalid option values are Exception subclasses. CPF and CNPJ validation failures return false. CPF formatting length failure is handled by onFail (default: return input); CNPJ formatting length failure uses onFail (default: return '').
<?php use Lacus\BrUtils; use Lacus\BrUtils\Cnpj\Exceptions\CnpjFormatterInputTypeError; use Lacus\BrUtils\Cnpj\Exceptions\CnpjValidatorInputTypeError; $brUtils = new BrUtils(); try { $brUtils->cnpj->format(12345); // throws CnpjFormatterInputTypeError } catch (CnpjFormatterInputTypeError $e) { echo $e->getMessage(); } try { $brUtils->cnpj->isValid(12345678000198); // throws CnpjValidatorInputTypeError } catch (CnpjValidatorInputTypeError $e) { echo $e->getMessage(); } $cpfOut = $brUtils->cpf->format( // 'invalid' 'short', onFail: static fn ($value) => 'invalid' ); $cnpjOut = $brUtils->cnpj->format( // 'invalid' 'short', onFail: static fn () => 'invalid', );
For exhaustive exception lists and edge-case behavior, see each bundled package README.
Bundled packages
| Package | Main resources | README |
|---|---|---|
lacus/cpf-utils |
CpfUtils, CpfFormatter, CpfGenerator, CpfValidator, cpf_fmt(), cpf_gen(), cpf_val() |
docs |
lacus/cnpj-utils |
CnpjUtils, CnpjFormatter, CnpjGenerator, CnpjValidator, CnpjType, CnpjValidationType, cnpj_fmt(), cnpj_gen(), cnpj_val() |
docs |
All CPF symbols are available under Lacus\BrUtils\Cpf\; all CNPJ symbols under Lacus\BrUtils\Cnpj\. Interactive demos: CPF and CNPJ.
API
BrUtils: Top-level façade with$cpf/$cnpjproperty access andgetCpfUtils()/getCnpjUtils()CpfUtils: Domain aggregator for CPF format, generate, and validateCnpjUtils: Domain aggregator for CNPJ format, generate, and validateCpfFormatter,CpfFormatterOptions,CpfGenerator,CpfGeneratorOptions,CpfValidator: CPF component classesCnpjFormatter,CnpjFormatterOptions,CnpjGenerator,CnpjGeneratorOptions,CnpjValidator,CnpjValidatorOptions: CNPJ component classesCnpjGenerationType,CnpjValidationType: CNPJ generation and validation enumscpf_fmt(),cpf_gen(),cpf_val(): CPF functional helpers (Lacus\BrUtils\Cpf\)cnpj_fmt(),cnpj_gen(),cnpj_val(): CNPJ functional helpers (Lacus\BrUtils\Cnpj\)- Exceptions: CPF —
InvalidArgumentExceptionfor invalid options; CNPJ — fullTypeError/Exceptionhierarchies from bundled packages (see linked READMEs)
Contribution & Support
We welcome contributions! Please see our Contributing Guidelines for details. If you find this project helpful, please consider:
- ⭐ Starring the repository
- 🤝 Contributing to the codebase
- 💡 Suggesting new features
- 🐛 Reporting bugs
License
This project is licensed under the MIT License — see the LICENSE file for details.
Changelog
See CHANGELOG for a list of changes and version history.
Made with ❤️ by Lacus Solutions