25
25
26
26
class Sqids implements SqidsInterface
27
27
{
28
+ private const MIN_LENGTH_LIMIT = 255 ;
29
+
28
30
final public const DEFAULT_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ' ;
29
31
final public const DEFAULT_MIN_LENGTH = 0 ;
30
32
final public const DEFAULT_BLOCKLIST = [
@@ -237,7 +239,7 @@ class Sqids implements SqidsInterface
237
239
238
240
protected MathInterface $ math ;
239
241
240
- protected ?string $ blocklistRegex = null ;
242
+ protected ?string $ blocklistRegex ;
241
243
242
244
/** @throws InvalidArgumentException */
243
245
public function __construct (
@@ -263,25 +265,12 @@ public function __construct(
263
265
throw new InvalidArgumentException ('Alphabet must contain unique characters ' );
264
266
}
265
267
266
- $ minLengthLimit = 255 ;
267
- if ($ minLength < 0 || $ minLength > $ minLengthLimit ) {
268
- throw new InvalidArgumentException (
269
- 'Minimum length has to be between 0 and ' . $ minLengthLimit ,
270
- );
271
- }
272
-
273
- $ filteredBlocklist = [];
274
- foreach ($ blocklist as $ word ) {
275
- if (strlen ((string ) $ word ) >= 3 ) {
276
- $ filteredBlocklist [] = strtr (preg_quote ((string ) $ word , '/ ' ), self ::LEET );
277
- }
278
- }
279
- if ($ filteredBlocklist ) {
280
- $ this ->blocklistRegex = '/( ' . implode ('| ' , $ filteredBlocklist ) . ')/i ' ;
268
+ if ($ minLength < 0 || $ minLength > self ::MIN_LENGTH_LIMIT ) {
269
+ throw new InvalidArgumentException ('Minimum length has to be between 0 and ' . self ::MIN_LENGTH_LIMIT );
281
270
}
282
271
272
+ $ this ->blocklistRegex = $ this ->buildBlocklistRegex ();
283
273
$ this ->alphabet = $ this ->shuffle ($ alphabet );
284
- $ this ->blocklist = $ filteredBlocklist ;
285
274
}
286
275
287
276
/**
@@ -471,4 +460,48 @@ protected function getMathExtension(): MathInterface
471
460
472
461
throw new RuntimeException ('Missing math extension for Sqids, install either bcmath or gmp. ' );
473
462
}
463
+
464
+ protected function buildBlocklistRegex (): ?string
465
+ {
466
+ $ wordsMatchingExactly = [];
467
+ $ wordsMatchingBeginningOrEnd = [];
468
+ $ wordMatchingAnywhere = [];
469
+
470
+ foreach ($ this ->blocklist as $ word ) {
471
+ $ word = (string ) $ word ;
472
+ if (strlen ($ word ) <= 3 ) {
473
+ $ wordsMatchingExactly [] = preg_quote ($ word , '/ ' );
474
+ } else {
475
+ $ word = preg_quote ($ word , '/ ' );
476
+ $ leet = strtr ($ word , self ::LEET );
477
+ if (!preg_match ('/\d/ ' , $ word )) {
478
+ $ wordMatchingAnywhere [] = $ word ;
479
+ } elseif ($ leet === $ word ) {
480
+ $ wordsMatchingBeginningOrEnd [] = $ word ;
481
+ }
482
+
483
+ if ($ leet !== $ word ) {
484
+ $ wordsMatchingBeginningOrEnd [] = $ leet ;
485
+ }
486
+ }
487
+ }
488
+
489
+ $ regexParts = [];
490
+ if ($ wordsMatchingExactly ) {
491
+ $ regexParts [] = '^( ' . implode ('| ' , $ wordsMatchingExactly ) . ')$ ' ;
492
+ }
493
+ if ($ wordsMatchingBeginningOrEnd ) {
494
+ $ regexParts [] = '^( ' . implode ('| ' , $ wordsMatchingBeginningOrEnd ) . ') ' ;
495
+ $ regexParts [] = '( ' . implode ('| ' , $ wordsMatchingBeginningOrEnd ) . ')$ ' ;
496
+ }
497
+ if ($ wordMatchingAnywhere ) {
498
+ $ regexParts [] = '( ' . implode ('| ' , $ wordMatchingAnywhere ) . ') ' ;
499
+ }
500
+
501
+ if ($ regexParts ) {
502
+ return '/( ' . implode ('| ' , $ regexParts ) . ')/i ' ;
503
+ }
504
+
505
+ return null ;
506
+ }
474
507
}
0 commit comments