PHPackages                             matula/sifter - 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. [Validation &amp; Sanitization](/categories/validation)
4. /
5. matula/sifter

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

matula/sifter
=============

Detect spammy input on Laravel forms

1.2.0(2mo ago)516MITPHPPHP ^8.2

Since Sep 11Pushed 2mo agoCompare

[ Source](https://github.com/matula/sifter)[ Packagist](https://packagist.org/packages/matula/sifter)[ RSS](/packages/matula-sifter/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (10)Versions (6)Used By (0)

Sifter for Laravel
==================

[](#sifter-for-laravel)

Detect spammy or bot-like input in Laravel forms using lightweight, configurable heuristics. Sifter ships with several checks (vowel ratio, repetitive chars, excessive capitals, numeric characters, entropy, and consecutive runs) and integrates with Laravel validation out of the box.

About
-----

[](#about)

I've been seeing accounts registered with the name field like "BBXQxwcf" or "DlaKsvRqiFA", and they even subvert Cloudflare and a honeypot. These are obvious spam accounts, so I decided to create a simple validation to help tackle most of these.
Keep in mind, this is a brute-force validation and only targeting English characters and names. You can use the config file to get more lenient or strict about the rules, but the defaults are what I found to catch most of spam. Personally, I do a programmatic check just on the 'name' field, and redirect to a `404` if it fails... hoping that will dissuade more attempts.

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

[](#requirements)

- PHP 8.2+
- Laravel 11.x, 12.x, or 13.x

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

[](#installation)

```
composer require matula/sifter
```

Sifter uses Laravel package auto-discovery; no manual provider registration is required.

Publish Configuration (optional)
--------------------------------

[](#publish-configuration-optional)

```
php artisan vendor:publish --provider="Matula\\Sifter\\SifterServiceProvider" --tag=config
```

This creates `config/sifter.php`. Toggle globally with `SIFTER_ENABLED` and tune each check under the `checks` key.

Usage
-----

[](#usage)

### As a validation rule

[](#as-a-validation-rule)

Add the shortcut rule or the class-based rule.

```
// app/Http/Controllers/ExampleController.php
$request->validate([
    'name' => ['required', 'string', 'sifted'],
    // or: new \\Matula\\Sifter\\Rules\\Sifted(),
]);
```

On failure, the rule reports that the value is not valid.

If you'd like to `abort` without doing a programmatic check, you can add a 404 argument like `sifted:404`. This will instead show a 404 page instead of redirecting back with an error.

*Technically, it will accept ANY numeric value and run abort with that number (even 2xx). I may update that later, but it fits my purpose for now.*

### Programmatic check

[](#programmatic-check)

```
use Matula\\Sifter\\Sifter;

$sifter = app(Sifter::class);

if ($sifter->isSpam($input)) {
    // handle spammy input
}

// Get more detail for logging/debugging
$results = $sifter->analyze($input); // [ [ 'name' => 'vowel_ratio', 'message' => '...', 'meta' => [...] ], ... ]
```

Configuration Overview
----------------------

[](#configuration-overview)

`config/sifter.php` (excerpt):

```
return [
  'enabled' => env('SIFTER_ENABLED', true),
  'checks' => [
    'excessive_capitals'      => ['enabled' => true, 'max_count' => 3],
    'vowel_ratio'             => ['enabled' => true, 'min_ratio' => 0.2],
    'consecutive_consonants'  => ['enabled' => true, 'max_consecutive' => 4],
    'consecutive_vowels'      => ['enabled' => true, 'max_consecutive' => 2],
    'repetitive_chars'        => ['enabled' => true, 'max_repetitive' => 2],
    'numeric_chars'           => ['enabled' => true],
    'high_entropy'            => ['enabled' => true, 'max_ratio' => 0.8],
  ],
];
```

Custom Checks (extensible)
--------------------------

[](#custom-checks-extensible)

Implement the `CheckInterface` and tag it so Sifter picks it up.

```
namespace App\\Sifter;

use Matula\\Sifter\\Checks\\CheckInterface;

class MyCheck implements CheckInterface {
    public function name(): string { return 'my_check'; }
    public function evaluate(string $input, array $config): ?array {
        return str_contains($input, 'zzz') ? [
            'message' => 'Contains forbidden sequence',
            'meta' => [],
        ] : null;
    }
}
```

Register and tag in a service provider:

```
$this->app->singleton(MyCheck::class);
$this->app->tag([MyCheck::class], 'sifter.check');
```

Enable and configure under `config/sifter.php` → `checks['my_check']`.

Local Development
-----------------

[](#local-development)

- Install deps: `composer install`
- Run tests (Pest): `composer test`

License
-------

[](#license)

MIT

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance84

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity52

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 ~106 days

Total

3

Last Release

83d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/74b80305f00a721b4fd9b1246d7667d461215e77f19a424b49fe89ca0eb581a0?d=identicon)[matula](/maintainers/matula)

---

Top Contributors

[![matula](https://avatars.githubusercontent.com/u/429870?v=4)](https://github.com/matula "matula (8 commits)")

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/matula-sifter/health.svg)

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

###  Alternatives

[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M151](/packages/laravel-mcp)[yajra/laravel-oci8

Oracle DB driver for Laravel via OCI8

8793.2M25](/packages/yajra-laravel-oci8)[spatie/laravel-honeypot

Preventing spam submitted through forms

1.6k6.8M75](/packages/spatie-laravel-honeypot)[propaganistas/laravel-disposable-email

Disposable email validator

6023.0M6](/packages/propaganistas-laravel-disposable-email)[axlon/laravel-postal-code-validation

Worldwide postal code validation for Laravel

3893.6M1](/packages/axlon-laravel-postal-code-validation)[wendelladriel/laravel-validated-dto

Data Transfer Objects with validation for Laravel applications

762649.9k18](/packages/wendelladriel-laravel-validated-dto)

PHPackages © 2026

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