Skip to content

Commit

Permalink
feat(performance): improved strategies and processors
Browse files Browse the repository at this point in the history
  • Loading branch information
diego-ninja committed Dec 9, 2024
1 parent 9577793 commit 757c82a
Show file tree
Hide file tree
Showing 66 changed files with 1,846 additions and 926 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ $rules = [
Uses local dictionaries for offline profanity checking.

```php
use Ninja\Censor\Enums\Service;
use Ninja\Censor\Enums\Provider;

$result = Censor::with(Service::Local, 'text to check');
$result = Censor::with(Provider::Local, 'text to check');
```

### PurgoMalum
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"pestphp/pest": "^2 || ^3",
"phpstan/phpstan": "^2",
"phpstan/phpstan-deprecation-rules": "^2",
"phpstan/phpstan-strict-rules": "^2"
"phpstan/phpstan-strict-rules": "^2",
"laravel/octane": "^2.6"
},
"autoload": {
"psr-4": {
Expand Down
70 changes: 69 additions & 1 deletion config/censor.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use Ninja\Censor\Processors\DefaultProcessor;

return [

/*
Expand Down Expand Up @@ -42,7 +44,7 @@
| Define the default profanity service to use
|
*/
'default_service' => \Ninja\Censor\Enums\Service::PurgoMalum,
'default_service' => \Ninja\Censor\Enums\Provider::Local,

/*
|--------------------------------------------------------------------------
Expand Down Expand Up @@ -93,6 +95,71 @@
'z' => '(z|z\.|z\-|Ζ)',
],

/*
|--------------------------------------------------------------------------
| Word suffixes
|--------------------------------------------------------------------------
|
| Define the list of word suffixes used to generate the regular expression
| to match the profanity
|
*/
'suffixes' => [
'ing',
'ed',
'er',
's',
'ers',
"'s",
'es',
'est',
'ly',
'ier',
'iest',
],


/*
|--------------------------------------------------------------------------
| Word prefixes
|--------------------------------------------------------------------------
|
| Define the list of word prefixes used to generate the regular expression
| to match the profanity
|
*/
'prefixes' => [
'un',
're',
'dis',
'mis',
'pre',
'over',
'under',
'sub',
'super',
'anti',
'auto',
'bi',
'co',
'de',
'en',
'ex',
'fore',
'in',
'inter',
'mid',
'non',
'out',
'post',
'semi',
'tri',
'un',
'under',
'up',
'with',
],

/*
|--------------------------------------------------------------------------
| Whitelisted words
Expand Down Expand Up @@ -137,6 +204,7 @@
'purgomalum' => [],
'local' => [
'levenshtein_threshold' => env('CENSOR_LEVENSHTEIN_THRESHOLD', 1),
'processor' => \Ninja\Censor\Processors\OctaneProcessor::class,
],
],

Expand Down
43 changes: 43 additions & 0 deletions src/Cache/PatternCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Ninja\Censor\Cache;

final class PatternCache
{
/** @var array<string, string> */
private array $patterns = [];

/** @var array<string, int> */
private array $lastUsed = [];

public function __construct(
private readonly int $maxSize = 1000
) {}

public function get(string $key): ?string
{
if (isset($this->patterns[$key])) {
$this->lastUsed[$key] = time();

return $this->patterns[$key];
}

return null;
}

public function set(string $key, string $pattern): void
{
if (count($this->patterns) >= $this->maxSize) {
$oldest = array_key_first(
array_filter(
$this->lastUsed,
fn ($time) => $time === min(array_values($this->lastUsed))
)
);
unset($this->patterns[$oldest], $this->lastUsed[$oldest]);
}

$this->patterns[$key] = $pattern;
$this->lastUsed[$key] = time();
}
}
4 changes: 2 additions & 2 deletions src/Censor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Ninja\Censor\Contracts\ProfanityChecker;
use Ninja\Censor\Contracts\Result;
use Ninja\Censor\Enums\Service;
use Ninja\Censor\Enums\Provider;

class Censor
{
Expand Down Expand Up @@ -32,7 +32,7 @@ public function clean(string $text): string
return $service->check($text)->replaced();
}

public function with(Service $service, string $text): ?Result
public function with(Provider $service, string $text): ?Result
{
/** @var ProfanityChecker $checker */
$checker = app($service->value);
Expand Down
57 changes: 51 additions & 6 deletions src/CensorServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Validator;
use Ninja\Censor\Contracts\Processor;
use Ninja\Censor\Contracts\ProfanityChecker;
use Ninja\Censor\Enums\Service;
use Ninja\Censor\Dictionary\LazyDictionary;
use Ninja\Censor\Enums\Provider;
use Ninja\Censor\Factories\ProfanityCheckerFactory;
use Ninja\Censor\Index\TrieIndex;
use Ninja\Censor\Processors\AbstractProcessor;
use Ninja\Censor\Processors\DefaultProcessor;
use Ninja\Censor\Support\PatternGenerator;

final class CensorServiceProvider extends ServiceProvider
Expand Down Expand Up @@ -46,10 +51,10 @@ public function boot(): void

public function register(): void
{
$this->registerCheckers();
$this->registerProfanityProviders();

/** @var Service $default */
$default = config('censor.default_service', Service::Local);
/** @var Provider $default */
$default = config('censor.default_service', Provider::Local);
$this->app->bind(ProfanityChecker::class, function () use ($default): ProfanityChecker {
/** @var ProfanityChecker $service */
$service = app($default->value);
Expand All @@ -64,16 +69,49 @@ public function register(): void
return new PatternGenerator($replacements);
});

$this->app->singleton(LazyDictionary::class, function (): LazyDictionary {
/** @var string[] $languages */
$languages = config('censor.languages', [config('app.locale')]);

return LazyDictionary::withLanguages($languages);
});

$this->app->singleton(Whitelist::class, function (): Whitelist {
/** @var string[] $whitelist */
$whitelist = config('censor.whitelist', []);

return (new Whitelist)->add($whitelist);
});

$this->app->singleton(TrieIndex::class, function (): TrieIndex {
$words = app(LazyDictionary::class)->getWords();

return new TrieIndex($words);
});

$this->app->singleton(Processor::class, function (): AbstractProcessor {
/** @var class-string<AbstractProcessor> $processorClass */
$processorClass = config('censor.services.local.processor', DefaultProcessor::class);

return new $processorClass(
app(PatternGenerator::class),
app(Whitelist::class),
app(LazyDictionary::class),
app(TrieIndex::class)
);

});

$this->app->bind('censor', function () {
return new Censor;
});

$this->mergeConfigFrom(__DIR__.'/../config/censor.php', 'censor');
}

private function registerCheckers(): void
private function registerProfanityProviders(): void
{
$services = Service::values();
$services = Provider::values();
foreach ($services as $service) {
/** @var array<string,mixed> $config */
$config = config(sprintf('censor.services.%s', $service->value));
Expand All @@ -84,5 +122,12 @@ private function registerCheckers(): void
});
}
}

$this->app->singleton(Provider::Local->value, function () {
return new \Ninja\Censor\Checkers\Censor(
generator: app(PatternGenerator::class),
processor: app(Processor::class)
);
});
}
}
Loading

0 comments on commit 757c82a

Please sign in to comment.