PHPackages                             leoboy/desensitization - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. leoboy/desensitization

ActiveLibrary[Security](/categories/security)

leoboy/desensitization
======================

a security policy based data desensitization tool, with various transformation rules.

v1.0.3(2y ago)891MITPHPPHP &gt;=8.1CI passing

Since Jul 16Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/messikiller/desensitization)[ Packagist](https://packagist.org/packages/leoboy/desensitization)[ RSS](/packages/leoboy-desensitization/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (7)Dependencies (6)Versions (9)Used By (0)

Leoboy Desensitization
======================

[](#leoboy-desensitization)

[中文文档](README.zh-CN.md)

`leoboy/desensitization` is a PHP library for transforming sensitive values before they are displayed, returned from an API, or written to a log. It supports reusable rules, wildcard paths in nested arrays, and policies that choose a rule according to the current viewer.

[![Leoboy Desensitization](logo.svg)](logo.svg)

Requirements
------------

[](#requirements)

- PHP 8.2 or later
- Composer
- `ext-mbstring`
- Laravel 12.61.1+ or 13.12.0+ when using the Laravel integration

Installation
------------

[](#installation)

```
composer require leoboy/desensitization
```

Quick Start
-----------

[](#quick-start)

Create a desensitizer and apply a rule to one value:

```
use Leoboy\Desensitization\Desensitizer;
use Leoboy\Desensitization\Rules\Mask;

$desensitizer = new Desensitizer();

$desensitizer->invoke('abcdef', Mask::create()->padding(1)->repeat(3));
// a***f

$desensitizer->invoke('abcdef', 'mask|use:x|repeat:3|padding:1');
// axxxf

$desensitizer->invoke('abcdef', fn (string $value) => strrev($value));
// fedcba
```

`Mask` preserves the configured left and right padding only when at least one input character remains between them. If the input is too short, it returns only the mask so that the original value is never fully exposed.

Nested Arrays
-------------

[](#nested-arrays)

`desensitize()` returns a transformed copy of the input array. Definitions use dot notation, and `*` matches one array key at that level.

```
use Leoboy\Desensitization\Desensitizer;
use Leoboy\Desensitization\Rules\Mask;
use Leoboy\Desensitization\Rules\Replace;

$data = [
    'name' => 'Ada Lovelace',
    'phone' => '13800138000',
    'contacts' => [
        ['email' => 'ada@example.com'],
        ['email' => 'ops@example.com'],
    ],
];

$result = (new Desensitizer())->desensitize($data, [
    'name' => new Replace('REDACTED'),
    'phone' => Mask::create()->padding(3)->repeat(4),
    'contacts.*.email' => 'mask|use:*|repeat:3|padding:1',
]);
```

Use the configured separator literally in keys with the same text as a path. When a top-level key and a nested path are both named `a.b`, `a.b` targets the exact top-level key. Avoid using the path separator in data keys when both forms must be addressed independently.

Built-in Rules
--------------

[](#built-in-rules)

- `None`: returns the input unchanged.
- `Mask`: keeps left and right padding and replaces the middle with a repeated mask character.
- `Replace`: replaces the input with a fixed value.
- `Cut`: returns a substring.
- `Invoke`: calls a PHP callable.
- `Hash`: creates a one-way hash with an Illuminate hasher. The default is bcrypt; it is not encryption and is intentionally non-deterministic.
- `Mix`: applies several rules in sequence.

Examples:

```
use Illuminate\Hashing\BcryptHasher;
use Leoboy\Desensitization\Rules\Cut;
use Leoboy\Desensitization\Rules\Hash;
use Leoboy\Desensitization\Rules\Mix;
use Leoboy\Desensitization\Rules\Replace;

Cut::create()->start(1)->length(3);
Replace::create('-');
Hash::create()->use(new BcryptHasher())->options(['cost' => 10]);
Mix::create([Cut::create()->length(4), Replace::create('REDACTED')]);
```

Rule Strings
------------

[](#rule-strings)

Registered rules can be expressed as a compact string. Constructor parameters follow the rule name; method parameters follow `|`.

```
mask|use:x|repeat:3|padding:1
replace:REDACTED
cut:1,3
```

Available names are `cut`, `hash`, `mask`, `none`, and `replace`. Calling an unknown rule or configuration method throws a `RuleResolveException`; typos are not silently ignored.

Register an application rule with a short name:

```
$desensitizer->register(App\Rules\NationalIdRule::class, 'national-id');
```

Custom rules implement `RuleContract`:

```
use Leoboy\Desensitization\Contracts\RuleContract;

final class NationalIdRule implements RuleContract
{
    public function transform($input)
    {
        return 'REDACTED';
    }
}
```

Viewer-specific Policies
------------------------

[](#viewer-specific-policies)

Pass a `SecurityPolicyContract`, `GuardContract`, rule, callable, or registered rule string to `via()`. A policy receives the field definition and decides which rule to use for it.

```
use Leoboy\Desensitization\Contracts\AttributeContract;
use Leoboy\Desensitization\Contracts\RuleContract;
use Leoboy\Desensitization\Contracts\SecurityPolicyContract;
use Leoboy\Desensitization\Desensitizer;
use Leoboy\Desensitization\Rules\Mask;
use Leoboy\Desensitization\Rules\None;

final class ViewerPolicy implements SecurityPolicyContract
{
    public function decide(AttributeContract $attribute): RuleContract|callable|string
    {
        return match ($attribute->getType()) {
            'phone' => Mask::create()->padding(3)->repeat(4),
            'email' => 'mask|use:*|repeat:3|padding:1',
            default => new None(),
        };
    }
}

$result = (new Desensitizer())
    ->via(new ViewerPolicy())
    ->desensitize($data, [
        'phone' => 'phone',
        'contacts.*.email' => 'email',
    ]);
```

A `RuleContract` or callable placed directly in a definition always transforms that field and does not consult the policy. A string that resolves to a registered rule also transforms directly; an unregistered string is passed to the policy as the attribute type.

Configuration and Failures
--------------------------

[](#configuration-and-failures)

The defaults are:

```
[
    'wildcard_char' => '*',
    'key_dot' => '.',
    'skip_transformation_exception' => false,
]
```

Both `wildcard_char` and `key_dot` must be non-empty strings and must differ. With the default `false`, a failed transformation throws `TransformException`. When `skip_transformation_exception` is `true`, the failed value is returned unchanged. Use that option only when returning the original value is safe for the caller.

```
$desensitizer->config('key_dot', '__');
$desensitizer->config('wildcard_char', '-');
```

Transformation exception messages deliberately omit the original input value. Inspect the previous exception for technical diagnostics without logging sensitive values yourself.

Global Instances
----------------

[](#global-instances)

`Desensitizer::global()` creates one instance per desensitizer class. `globalize()` replaces that instance, and `forgetGlobal()` discards it.

```
$global = Desensitizer::global()->via('mask|padding:1');

// At the end of a request in a long-running worker, if global state was used:
Desensitizer::forgetGlobal();
```

Do not store request- or user-specific policies in a global instance in Laravel Octane, Swoole, RoadRunner, or another long-running process. Prefer a request-scoped or container-resolved instance.

Laravel
-------

[](#laravel)

The package is discovered automatically. Publish the configuration file when you need to change it:

```
php artisan vendor:publish --provider="Leoboy\Desensitization\Laravel\DesensitizationServiceProvider"
```

Resolve the Facade explicitly:

```
use Leoboy\Desensitization\Laravel\Facades\Desensitization;

$result = Desensitization::desensitize($data, [
    'phone' => 'mask|padding:3|repeat:4',
]);
```

The package also registers the `Desensitizer` Facade alias. The container binding is available as `Leoboy\Desensitization\Desensitizer::class` and `desensitizer`.

Development
-----------

[](#development)

```
composer test
composer lint
```

The CI workflow runs formatting checks, PHPStan, PHPUnit, and `composer audit` on PHP 8.2 through 8.5.

License
-------

[](#license)

This project is open-sourced under the [MIT License](LICENSE).

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance61

Regular maintenance activity

Popularity11

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

8

Last Release

749d ago

Major Versions

v0.9.3 → v1.0.02024-07-22

PHP version history (3 changes)v0.9.0-alphaPHP &gt;=8.0

v1.0.1PHP &gt;=8.2

v1.0.3PHP &gt;=8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/10354220?v=4)[messikiller](/maintainers/messikiller)[@messikiller](https://github.com/messikiller)

---

Top Contributors

[![messikiller](https://avatars.githubusercontent.com/u/10354220?v=4)](https://github.com/messikiller "messikiller (10 commits)")

---

Tags

anonymityanonymizationanonymousdata-desensitizedata-protectiondesensitizationdesensitizedesensitizerredactionsecurity-policysensitivesensitive-dataredactionsensitivedesensitizedesensitization

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/leoboy-desensitization/health.svg)

```
[![Health](https://phpackages.com/badges/leoboy-desensitization/health.svg)](https://phpackages.com/packages/leoboy-desensitization)
```

###  Alternatives

[mews/captcha

Laravel 5/6/7/8/9/10/11/12 Captcha Package

2.6k5.9M97](/packages/mews-captcha)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[illuminate/encryption

The Illuminate Encryption package.

9631.1M359](/packages/illuminate-encryption)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
