Violin   F
last analyzed

Complexity

Total Complexity 116

Size/Duplication

Total Lines 795
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 192
dl 0
loc 795
rs 2
c 0
b 0
f 0
wmc 116

62 Methods

Rating   Name   Duplication   Size   Complexity  
A toKebabCase() 0 3 1
A startsWith() 0 4 1
A ensureLeft() 0 7 2
A indexOfLast() 0 6 4
A replaceWithPairs() 0 3 1
A prepend() 0 3 1
A against() 0 3 1
A toUnderscoreCase() 0 3 1
A slugify() 0 7 1
A toCamelCase() 0 27 2
A upperCaseFirst() 0 3 1
A trim() 0 3 1
A isLowerCase() 0 3 1
A removeLeft() 0 7 2
A truncate() 0 17 5
A startsWithAny() 0 8 3
A ensureRight() 0 7 2
A trimLeft() 0 3 1
A collapseWhitespace() 0 3 1
A endsWith() 0 4 1
A lowerCaseFirst() 0 3 1
A indexOf() 0 6 4
A toUpperCase() 0 3 2
A removeRight() 0 7 2
A toAscii() 0 7 2
A surround() 0 3 1
A substringAfterFirst() 0 11 3
A delimit() 0 7 1
A toPascalCase() 0 3 1
A substr() 0 4 2
A trimRight() 0 3 1
A last() 0 3 1
A substringBeforeFirst() 0 11 3
A tidy() 0 15 1
A __construct() 0 2 1
A reverse() 0 11 3
A toUpperCamelCase() 0 3 1
A containsAny() 0 8 3
A toSnakeCase() 0 3 2
A toHyphenCase() 0 3 2
A isUpperCase() 0 3 1
A substringBeforeLast() 0 11 3
A hasLowerCase() 0 3 1
A contains() 0 3 1
A isEmpty() 0 3 1
A toLowerCase() 0 3 2
A hasUpperCase() 0 3 1
A append() 0 3 1
A containsAll() 0 10 3
A substringAfterLast() 0 10 3
A isBlank() 0 3 1
A toDashCase() 0 3 1
A tune() 0 23 5
A count() 0 13 4
A first() 0 3 1
A endsWithAny() 0 8 3
A fork() 0 5 1
A isEncodingValid() 0 3 1
A getEncoding() 0 6 2
A isStringable() 0 11 6
A __toString() 0 3 1
A isMultibyte() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like Violin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Violin, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace BenTools\Violin;
4
5
use BenTools\Violin\RegularExpressionHandler as Regex;
6
7
final class Violin implements \Countable
8
{
9
10
    const ASCII = 'ASCII';
11
12
    /**
13
     * @var string
14
     */
15
    private $str;
16
17
    /**
18
     * @var string
19
     */
20
    private $encoding;
21
22
    /**
23
     * @var int
24
     */
25
    private $count;
26
27
    /**
28
     * @return bool
29
     */
30
    public function isEmpty(): bool
31
    {
32
        return null === ($this->str[0] ?? null);
33
    }
34
35
    /**
36
     * @return bool
37
     */
38
    public function isBlank(): bool
39
    {
40
        return $this->trim()->isEmpty();
41
    }
42
43
    /**
44
     * @return int
45
     */
46
    public function count(): int
47
    {
48
        if (null !== $this->count) {
49
            return $this->count;
50
        }
51
52
        if ($this->isEmpty()) {
53
            $this->count = 0;
54
            return $this->count;
55
        }
56
57
        $this->count = $this->isMultibyte() ? \mb_strlen($this->str) : \strlen($this->str);
58
        return $this->count;
59
    }
60
61
    /**
62
     * @return bool
63
     * @throws \InvalidArgumentException
64
     */
65
    public function hasUpperCase(): bool
66
    {
67
        return $this->against('/.*[[:upper:]]/u')->hasMatches();
68
    }
69
70
    /**
71
     * @return bool
72
     * @throws \InvalidArgumentException
73
     */
74
    public function isUpperCase(): bool
75
    {
76
        return $this->against('/^[[:upper:]]*$/u')->hasMatches();
77
    }
78
79
    /**
80
     * @return Violin
81
     */
82
    public function toUpperCase(): self
83
    {
84
        return $this->isMultibyte() ? $this->fork(\mb_strtoupper($this->str), $this->encoding) : $this->fork(\strtoupper($this->str), $this->encoding);
85
    }
86
87
    /**
88
     * @return Violin
89
     */
90
    public function upperCaseFirst(): self
91
    {
92
        return $this->fork($this->first()->toUpperCase() . $this->last(\count($this) - 1));
93
    }
94
95
    /**
96
     * @return bool
97
     * @throws \InvalidArgumentException
98
     */
99
    public function hasLowerCase(): bool
100
    {
101
        return $this->against('/.*[[:lower:]]/u')->hasMatches();
102
    }
103
104
    /**
105
     * @return bool
106
     * @throws \InvalidArgumentException
107
     */
108
    public function isLowerCase(): bool
109
    {
110
        return $this->against('/^[[:lower:]]*$/u')->hasMatches();
111
    }
112
113
    /**
114
     * @return Violin
115
     */
116
    public function toLowerCase(): self
117
    {
118
        return $this->isMultibyte() ? $this->fork(\mb_strtolower($this->str), $this->encoding) : $this->fork(\strtolower($this->str), $this->encoding);
119
    }
120
121
    /**
122
     * @return Violin
123
     */
124
    public function lowerCaseFirst(): self
125
    {
126
        return $this->fork($this->first()->toLowerCase() . $this->last(\count($this) - 1));
127
    }
128
129
    /**
130
     * @return Violin
131
     */
132
    public function toPascalCase(): self
133
    {
134
        return $this->toCamelCase()->upperCaseFirst();
135
    }
136
137
    /**
138
     * @return Violin
139
     */
140
    public function toUpperCamelCase(): self
141
    {
142
        return $this->toPascalCase();
143
    }
144
145
    /**
146
     * @param bool $screaming
147
     * @return Violin
148
     * @throws \InvalidArgumentException
149
     */
150
    public function toSnakeCase(bool $screaming = false): self
151
    {
152
        return true === $screaming ? $this->delimit('_')->toUpperCase() : $this->delimit('_');
153
    }
154
155
    /**
156
     * @param bool $screaming
157
     * @return Violin
158
     * @throws \InvalidArgumentException
159
     */
160
    public function toUnderscoreCase(bool $screaming = false): self
161
    {
162
        return $this->toSnakeCase($screaming);
163
    }
164
165
    /**
166
     * @param bool $screaming
167
     * @return Violin
168
     * @throws \InvalidArgumentException
169
     */
170
    public function toHyphenCase(bool $screaming = false): self
171
    {
172
        return true === $screaming ? $this->delimit('-')->toUpperCase() : $this->delimit('-');
173
    }
174
175
    /**
176
     * @param bool $screaming
177
     * @return Violin
178
     * @throws \InvalidArgumentException
179
     */
180
    public function toDashCase(bool $screaming = false): self
181
    {
182
        return $this->toHyphenCase($screaming);
183
    }
184
185
    /**
186
     * @param bool $screaming
187
     * @return Violin
188
     * @throws \InvalidArgumentException
189
     */
190
    public function toKebabCase(bool $screaming = false): self
191
    {
192
        return $this->toHyphenCase($screaming);
193
    }
194
195
    /**
196
     * @return Violin
197
     */
198
    public function toCamelCase(): self
199
    {
200
        $encoding = $this->encoding;
201
        $str = $this->trim();
202
        $str->str = \preg_replace('/^[-_]+/', '', $str->str);
203
204
        $str->str = \preg_replace_callback(
205
            '/[-_\s]+(.)?/u',
206
            function ($match) use ($encoding) {
207
                if (isset($match[1])) {
208
                    return (string) $this->fork($match[1], $encoding)->toUpperCase();
209
                }
210
211
                return '';
212
            },
213
            $str->str
214
        );
215
216
        $str->str = \preg_replace_callback(
217
            '/[\d]+(.)?/u',
218
            function ($match) use ($encoding) {
219
                return (string) $this->fork($match[0], $encoding)->toUpperCase();
220
            },
221
            $str->str
222
        );
223
224
        return $str->lowerCaseFirst();
225
    }
226
227
    /**
228
     * @param int      $start
229
     * @param int|null $length
230
     * @return Violin
231
     */
232
    public function substr(int $start, int $length = null): self
233
    {
234
        $length = $length ?? \count($this);
235
        return $this->isMultibyte() ? $this->fork(\mb_substr($this->str, $start, $length)) : $this->fork(\substr($this->str, $start, $length));
236
    }
237
238
    /**
239
     * @param      $separator
240
     * @param bool $include
241
     * @return Violin|null
242
     */
243
    public function substringAfterFirst($separator, bool $include = false): ?self
244
    {
245
        if (false === ($offset = $this->indexOf($separator))) {
0 ignored issues
show
introduced by
The condition false === $offset = $this->indexOf($separator) is always false.
Loading history...
246
            return null;
247
        }
248
249
        $substr = $this->substr($offset, \count($this) - $offset);
250
        if (false === $include) {
251
            return $substr->removeLeft($separator);
252
        }
253
        return $substr;
254
    }
255
256
    /**
257
     * @param      $separator
258
     * @param bool $include
259
     * @return Violin|null
260
     * @throws \InvalidArgumentException
261
     */
262
    public function substringAfterLast($separator, bool $include = false): ?self
263
    {
264
        if (false === ($from = $this->indexOfLast($separator))) {
0 ignored issues
show
introduced by
The condition false === $from = $this->indexOfLast($separator) is always false.
Loading history...
265
            return null;
266
        }
267
268
        if (false === $include) {
269
            $from = $from + \count(self::tune($separator));
270
        }
271
        return $this->substr($from, \count($this) - $from);
272
    }
273
274
    /**
275
     * @param      $separator
276
     * @param bool $include
277
     * @return Violin|null
278
     * @throws \InvalidArgumentException
279
     */
280
    public function substringBeforeFirst($separator, bool $include = false): ?self
281
    {
282
        if (false === ($from = $this->indexOf($separator))) {
0 ignored issues
show
introduced by
The condition false === $from = $this->indexOf($separator) is always false.
Loading history...
283
            return null;
284
        }
285
286
        if (true === $include) {
287
            $from = $from + \count(self::tune($separator));
288
        }
289
290
        return $this->substr(0, $from);
291
    }
292
293
    /**
294
     * @param      $separator
295
     * @param bool $include
296
     * @return Violin|null
297
     * @throws \InvalidArgumentException
298
     */
299
    public function substringBeforeLast($separator, bool $include = false): ?self
300
    {
301
        if (false === ($from = $this->indexOfLast($separator))) {
0 ignored issues
show
introduced by
The condition false === $from = $this->indexOfLast($separator) is always false.
Loading history...
302
            return null;
303
        }
304
305
        if (true === $include) {
306
            $from = $from + \count(self::tune($separator));
307
        }
308
309
        return $this->substr(0, $from);
310
    }
311
312
    /**
313
     * @param int $numberOfChars
314
     * @return Violin
315
     */
316
    public function first(int $numberOfChars = 1): self
317
    {
318
        return $this->substr(0, $numberOfChars);
319
    }
320
321
    /**
322
     * @param int $numberOfChars
323
     * @return Violin
324
     */
325
    public function last(int $numberOfChars = 1): self
326
    {
327
        return $this->substr(-$numberOfChars);
328
    }
329
330
    /**
331
     * @param $str
332
     * @return bool
333
     */
334
    public function startsWith($str): bool
335
    {
336
        $str = self::tune($str);
337
        return (string) $this->substr(0, \count($str)) === (string) $str;
338
    }
339
340
    /**
341
     * @param iterable $strings
342
     * @return bool
343
     */
344
    public function startsWithAny(iterable $strings): bool
345
    {
346
        foreach ($strings as $string) {
347
            if ($this->startsWith($string)) {
348
                return true;
349
            }
350
        }
351
        return false;
352
    }
353
354
    /**
355
     * @param $str
356
     * @return bool
357
     * @throws \InvalidArgumentException
358
     */
359
    public function endsWith($str): bool
360
    {
361
        $str = self::tune($str);
362
        return $this->indexOfLast($str) === (\count($this) - \count($str));
363
    }
364
365
    /**
366
     * @param iterable $strings
367
     * @return bool
368
     */
369
    public function endsWithAny(iterable $strings): bool
370
    {
371
        foreach ($strings as $string) {
372
            if ($this->endsWith($string)) {
373
                return true;
374
            }
375
        }
376
        return false;
377
    }
378
379
    /**
380
     * @param $str
381
     * @return Violin
382
     */
383
    public function ensureLeft($str): self
384
    {
385
        if ($this->startsWith($str)) {
386
            return $this;
387
        }
388
389
        return $this->fork($str . $this);
390
    }
391
392
    /**
393
     * @param $str
394
     * @return Violin
395
     */
396
    public function ensureRight($str): self
397
    {
398
        if ($this->endsWith($str)) {
399
            return $this;
400
        }
401
402
        return $this->fork($this . $str);
403
    }
404
405
    /**
406
     * @param $str
407
     * @return Violin
408
     */
409
    public function removeLeft($str): self
410
    {
411
        if ($this->startsWith($str)) {
412
            return $this->last(-\count(self::tune($str)));
413
        }
414
415
        return $this;
416
    }
417
418
    /**
419
     * @param $str
420
     * @return Violin
421
     */
422
    public function removeRight($str): self
423
    {
424
        if ($this->endsWith($str)) {
425
            return $this->first(-\count(self::tune($str)));
426
        }
427
428
        return $this;
429
    }
430
431
    /**
432
     * @param string $charlist
433
     * @return Violin
434
     */
435
    public function trim(string $charlist = " \t\n\r\0\x0B"): self
436
    {
437
        return $this->fork(\trim($this->str, $charlist));
438
    }
439
440
    /**
441
     * @param string $charlist
442
     * @return Violin
443
     */
444
    public function trimLeft(string $charlist = " \t\n\r\0\x0B"): self
445
    {
446
        return $this->fork(\ltrim($this->str, $charlist));
447
    }
448
449
    /**
450
     * @param string $charlist
451
     * @return Violin
452
     */
453
    public function trimRight(string $charlist = " \t\n\r\0\x0B"): self
454
    {
455
        return $this->fork(\rtrim($this->str, $charlist));
456
    }
457
458
    /**
459
     * @param string $str
460
     * @return Violin
461
     */
462
    public function append(string $str): self
463
    {
464
        return $this->fork($this . $str);
465
    }
466
467
    /**
468
     * @param string $str
469
     * @return Violin
470
     */
471
    public function prepend(string $str): self
472
    {
473
        return $this->fork($str . $this);
474
    }
475
476
    /**
477
     * @return Violin
478
     */
479
    public function reverse(): self
480
    {
481
        if (!$this->isMultibyte()) {
482
            return $this->fork(\strrev($this->str));
483
        }
484
485
        $reversed = '';
486
        for ($offset = \count($this); $offset >= 0; $offset--) {
487
            $reversed .= \mb_substr($this->str, $offset, 1);
488
        }
489
        return $this->fork($reversed, $this->encoding);
490
    }
491
492
    /**
493
     * @param string $needle
494
     * @param int    $offset
495
     * @param bool   $caseSensitive
496
     * @return bool|int
497
     */
498
    public function indexOf(string $needle, int $offset = 0, bool $caseSensitive = true)
499
    {
500
        if (false === $caseSensitive) {
501
            return $this->isMultibyte() ? \mb_stripos($this->str, $needle, $offset) : \stripos($this->str, $needle, $offset);
502
        }
503
        return $this->isMultibyte() ? \mb_strpos($this->str, $needle, $offset) : \strpos($this->str, $needle, $offset);
504
    }
505
506
    /**
507
     * @param string $needle
508
     * @param int    $offset
509
     * @return bool|int
510
     */
511
    public function indexOfLast(string $needle, int $offset = 0, bool $caseSensitive = false)
512
    {
513
        if (false === $caseSensitive) {
514
            return $this->isMultibyte() ? \mb_strripos($this->str, $needle, $offset) : \strripos($this->str, $needle, $offset);
515
        }
516
        return $this->isMultibyte() ? \mb_strrpos($this->str, $needle, $offset) : \strrpos($this->str, $needle, $offset);
517
    }
518
519
    /**
520
     * @param string $needle
521
     * @param bool   $caseSensitive
522
     * @return bool
523
     */
524
    public function contains(string $needle, bool $caseSensitive = false): bool
525
    {
526
        return false !== $this->indexOf($needle, 0, $caseSensitive);
527
    }
528
529
    /**
530
     * @param iterable $needles
531
     * @param bool     $caseSensitive
532
     * @return bool
533
     */
534
    public function containsAny(iterable $needles, bool $caseSensitive = false): bool
535
    {
536
        foreach ($needles as $needle) {
537
            if (true === $this->contains($needle, $caseSensitive)) {
538
                return true;
539
            }
540
        }
541
        return false;
542
    }
543
544
    /**
545
     * @param iterable $needles
546
     * @param bool     $caseSensitive
547
     * @return bool
548
     */
549
    public function containsAll(iterable $needles, bool $caseSensitive = false): bool
550
    {
551
        foreach ($needles as $needle) {
552
            $hasLooped = true;
553
            if (false === $this->contains($needle, $caseSensitive)) {
554
                return false;
555
            }
556
        }
557
558
        return $hasLooped ?? false;
559
    }
560
561
    /**
562
     * @param array $pairs
563
     * @return Violin
564
     * @throws \InvalidArgumentException
565
     */
566
    public function replaceWithPairs(array $pairs): self
567
    {
568
        return self::tune(\strtr($this->str, $pairs));
569
    }
570
571
    /**
572
     * @param string|null $locale
573
     * @param string      $replaceUnsupportedBy
574
     * @return Violin
575
     */
576
    public function toAscii(string $locale = null, string $replaceUnsupportedBy = ''): self
577
    {
578
        if (self::ASCII === $this->getEncoding()) {
579
            return $this;
580
        }
581
582
        return $this->fork(AsciiConverter::convert($this->str, $locale, $replaceUnsupportedBy), self::ASCII);
583
    }
584
585
    /**
586
     * @param $pattern
587
     * @return RegularExpressionHandler
588
     * @throws \InvalidArgumentException
589
     */
590
    public function against($pattern): Regex
591
    {
592
        return Regex::make($pattern, $this);
593
    }
594
595
    /**
596
     * @return Violin
597
     * @throws \InvalidArgumentException
598
     */
599
    public function collapseWhitespace(): self
600
    {
601
        return $this->against('/[[:space:]]+/')->replaceWith(' ');
602
    }
603
604
    /**
605
     * @param string $delimiter
606
     * @return Violin
607
     * @throws \InvalidArgumentException
608
     */
609
    public function delimit(string $delimiter): self
610
    {
611
        return $this
612
            ->trim()
613
            ->against('/\B([A-Z])/u')->replaceWith('-\1')
614
            ->toLowerCase()
615
            ->against('/[-_\s]+/u')->replaceWith($delimiter)
616
            ;
617
    }
618
619
    /**
620
     * @param string      $delimiter
621
     * @param string|null $locale
622
     * @param null|string $replaceUnsupportedBy
623
     * @return Violin
624
     */
625
    public function slugify(string $delimiter, string $locale = null, ?string $replaceUnsupportedBy = ''): self
626
    {
627
        $str = $this->toAscii($locale, $replaceUnsupportedBy);
0 ignored issues
show
Bug introduced by
It seems like $replaceUnsupportedBy can also be of type null; however, parameter $replaceUnsupportedBy of BenTools\Violin\Violin::toAscii() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

627
        $str = $this->toAscii($locale, /** @scrutinizer ignore-type */ $replaceUnsupportedBy);
Loading history...
628
        $str->str = \strtr($str->str, ['@' => $delimiter]);
629
        $pattern = '/[^a-zA-Z\d\s' . \preg_quote($delimiter, '/') . ']/u';
630
        $str->str = \preg_replace($pattern, '', $str);
631
        return $str->toLowerCase()->delimit($delimiter)->removeLeft($delimiter)->removeRight($delimiter);
632
    }
633
634
    /**
635
     * @param string $str
636
     * @param string $separator
637
     * @return Violin
638
     */
639
    public function surround(string $str, string $separator = ''): self
640
    {
641
        return $this->fork($str . $separator . $this . $separator . $str);
642
    }
643
644
    /**
645
     * @return Violin
646
     */
647
    public function tidy(): self
648
    {
649
        $str = \preg_replace([
650
            '/\x{2026}/u',
651
            '/[\x{201C}\x{201D}]/u',
652
            '/[\x{2018}\x{2019}]/u',
653
            '/[\x{2013}\x{2014}]/u',
654
        ], [
655
            '...',
656
            '"',
657
            "'",
658
            '-',
659
        ], $this->str);
660
661
        return $this->fork($str);
662
    }
663
664
    /**
665
     * @param int    $totalLength
666
     * @param string $endWith
667
     * @param bool   $safe
668
     * @return Violin
669
     * @throws \InvalidArgumentException
670
     */
671
    public function truncate(int $totalLength, string $endWith = '...', bool $safe = false): self
672
    {
673
        $cnt = \count($this);
674
        if ($cnt <= $totalLength) {
675
            return $this;
676
        }
677
678
        $substr = $this->first($totalLength - \count(self::tune($endWith)));
679
680
        if (true === $safe && $this->indexOf(' ') !== $totalLength) {
681
            $lastPos = $substr->indexOfLast(' ');
682
            if (false !== $lastPos) {
0 ignored issues
show
introduced by
The condition false !== $lastPos is always true.
Loading history...
683
                $substr = $this->first($lastPos);
684
            }
685
        }
686
687
        return $this->fork($substr . $endWith);
688
    }
689
690
691
    /**
692
     * Violin constructor disabled - use the tune() static method instead.
693
     * Instanciating from a clone instead of new Violin() is about 3x faster.
694
     */
695
    private function __construct()
696
    {
697
    }
698
699
    /**
700
     * @param $str
701
     * @return Violin
702
     * @throws \InvalidArgumentException
703
     */
704
    public static function tune($str): self
705
    {
706
        if ($str instanceof Violin) {
707
            return $str;
708
        }
709
710
        if (!self::isStringable($str)) {
711
            throw new \InvalidArgumentException(
712
                \sprintf('Expected stringable object, got %s', \is_object($str) ? \get_class($str) : \gettype($str))
713
            );
714
        }
715
716
        static $v;
717
        if (!isset($v)) {
718
            $v = new self;
719
            $v->str = (string) $str;
720
            return $v;
721
        }
722
        $v = clone $v;
723
        $v->str = (string) $str;
724
        $v->encoding = null;
725
        $v->count = null;
726
        return $v;
727
    }
728
729
    /**
730
     * @param $str
731
     * @return bool
732
     */
733
    public static function isStringable($str): bool
734
    {
735
        if (\is_string($str) || \is_numeric($str) || null === $str) {
736
            return true;
737
        }
738
739
        if (\is_object($str) && \is_callable([$str, '__toString'])) {
740
            return true;
741
        }
742
743
        return false;
744
    }
745
746
    /**
747
     * @param string      $str
748
     * @param string|null $encoding
749
     * @return Violin
750
     */
751
    private function fork(string $str, string $encoding = null): self
752
    {
753
        $v = self::tune($str);
754
        $v->encoding = $encoding;
755
        return $v;
756
    }
757
758
    /**
759
     * @return string
760
     */
761
    public function __toString(): string
762
    {
763
        return $this->str;
764
    }
765
766
    /**
767
     * @param string $str
768
     * @param string $encoding
769
     * @return bool
770
     */
771
    public static function isEncodingValid(string $str, string $encoding): bool
772
    {
773
        return \mb_check_encoding($str, $encoding);
774
    }
775
776
    /**
777
     * @return string
778
     */
779
    public function getEncoding(): string
780
    {
781
        if (null === $this->encoding) {
782
            $this->encoding = \mb_detect_encoding($this->str);
783
        }
784
        return $this->encoding;
785
    }
786
787
    /**
788
     * @return bool
789
     */
790
    private function isMultibyte(): bool
791
    {
792
        if (null !== $this->encoding) {
793
            return self::ASCII !== $this->encoding;
794
        }
795
796
        if (0 === preg_match('/[^\x20-\x7f]/', $this->str)) {
797
            $this->encoding = self::ASCII;
798
            return false;
799
        }
800
801
        return true;
802
    }
803
}
804