Passed
Push — main ( 62790e...23fbb2 )
by Paul
13:43 queued 05:55
created

Text::wordCount()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 16
ccs 0
cts 12
cp 0
rs 9.9
cc 4
nc 4
nop 1
crap 20
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Helpers;
4
5
use GeminiLabs\SiteReviews\Modules\Html\Builder;
6
use GeminiLabs\SiteReviews\Modules\Sanitizers\SanitizeTextHtml;
7
8
class Text
9
{
10 8
    public static function excerpt(string $text, int $limit = 55, bool $splitWords = true): string
11
    {
12 8
        $map = [];
13 8
        $text = static::normalize($text);
14
        // replace tags with placeholder
15 8
        $text = preg_replace_callback('|<([a-z+])[^>]*?>.*?</\\1>|siu', function ($match) use (&$map) {
16
            $map[] = $match[0];
17
            return '⍈';
18 8
        }, $text);
19 8
        $excerptLength = $limit;
20 8
        if ($splitWords) {
21 8
            $excerpt = static::words($text, $limit);
22 8
            $excerptLength = mb_strlen($excerpt);
23
        }
24 8
        $paragraphs = static::extractParagraphs($text, $excerptLength);
25 8
        $text = implode(PHP_EOL, $paragraphs);
26 8
        $i = 0;
27
        // replace placeholder with tags
28 8
        $text = preg_replace_callback('|⍈|u', function ($match) use (&$i, $map) {
29
            return $map[$i++];
30 8
        }, $text);
31 8
        return $text;
32
    }
33
34 20
    public static function initials(string $name, string $initialPunctuation = ''): string
35
    {
36 20
        preg_match_all('/(?<=\s|\b)\p{L}/u', (string) $name, $matches); // match the first letter of each word in the name
37 20
        $result = (string) array_reduce($matches[0], function ($carry, $word) use ($initialPunctuation) {
38 20
            $initial = mb_substr($word, 0, 1, 'UTF-8');
39 20
            $initial = mb_strtoupper($initial, 'UTF-8');
40 20
            return $carry.$initial.$initialPunctuation;
41 20
        });
42 20
        return trim($result);
43
    }
44
45
    /**
46
     * @param string $nameFormat  first|first_initial|last_initial|initials
47
     * @param string $initialType  period|period_space|space
48
     */
49 19
    public static function name(string $name, string $nameFormat = '', string $initialType = 'space'): string
50
    {
51 19
        $names = preg_split('/\W/u', $name, 0, PREG_SPLIT_NO_EMPTY);
52 19
        $firstName = array_shift($names);
53 19
        $lastName = array_pop($names);
54 19
        $nameFormat = Str::restrictTo('first,first_initial,last_initial,initials', $nameFormat, '');
55 19
        $initialType = Str::restrictTo('period,period_space,space', $initialType, 'space');
56 19
        $initialTypes = [
57 19
            'period' => '.',
58 19
            'period_space' => '. ',
59 19
            'space' => ' ',
60 19
        ];
61 19
        $initialPunctuation = $initialTypes[$initialType];
62 19
        if ('initials' == $nameFormat) {
63 3
            return static::initials($name, $initialPunctuation);
64
        }
65 16
        $firstNameInitial = static::initials($firstName).$initialPunctuation;
66 16
        $lastNameInitial = $lastName ? static::initials($lastName).$initialPunctuation : '';
67 16
        $nameFormats = [
68 16
            'first' => $firstName,
69 16
            'first_initial' => $firstNameInitial.$lastName,
70 16
            'last' => $lastName,
71 16
            'last_initial' => $firstName.' '.$lastNameInitial,
72 16
        ];
73 16
        return trim((string) Arr::get($nameFormats, $nameFormat, $name));
74
    }
75
76 8
    public static function normalize(string $text): string
77
    {
78 8
        $text = (new SanitizeTextHtml($text))->run();
79 8
        $text = strip_shortcodes($text);
80 8
        $text = excerpt_remove_blocks($text); // just in case...
81 8
        $text = str_replace(']]>', ']]&gt;', $text);
82 8
        $text = normalize_whitespace($text); // normalize EOL characters and strip duplicate whitespace.
83 8
        $text = preg_replace('/\R{1,}/u', PHP_EOL.PHP_EOL, $text); // replace all line-breaks with a double line break
84 8
        $text = wptexturize($text); // replace common plain text characters with formatted entities.
85 8
        $text = ent2ncr($text); // convert named entities into numbered entities.
86 8
        $text = convert_chars($text); // converts lone & characters into &#038;
87 8
        $text = convert_invalid_entities($text); // convert invalid Unicode references range to valid range.
88 8
        $text = convert_smilies($text); // convert text smilies to emojis.
89 8
        $text = html_entity_decode($text);
90 8
        return $text;
91
    }
92
93
    public static function text(string $text): string
94
    {
95
        $text = static::normalize($text);
96
        $text = preg_split('/\R+/um', $text); // split text by line-breaks
97
        $text = array_map('trim', $text); // trim paragraphs
98
        $text = implode(PHP_EOL.PHP_EOL, $text);
99
        return wpautop($text);
100
    }
101
102
    public static function wordCount(string $text): int
103
    {
104
        $text = wp_strip_all_tags($text, true);
105
        if (!extension_loaded('intl')) {
106
            return count(preg_split('/[^\p{L}\p{N}\']+/u', $text));
107
        }
108
        $text = \Normalizer::normalize($text);
109
        $iterator = \IntlRuleBasedBreakIterator::createWordInstance('');
110
        $iterator->setText($text);
111
        $wordCount = 0;
112
        foreach ($iterator->getPartsIterator() as $part) {
113
            if (\IntlBreakIterator::WORD_NONE !== $iterator->getRuleStatus()) {
114
                ++$wordCount;
115
            }
116
        }
117
        return $wordCount;
118
    }
119
120 8
    public static function words(string $text, int $limit = 0): string
121
    {
122 8
        $stringLength = extension_loaded('intl')
123 8
            ? static::excerptIntlSplit($text, $limit)
124 8
            : static::excerptSplit($text, $limit);
125 8
        return mb_substr($text, 0, $stringLength);
126
    }
127
128 8
    protected static function excerptIntlSplit(string $text, int $limit): int
129
    {
130 8
        $text = \Normalizer::normalize($text);
131 8
        $iterator = \IntlRuleBasedBreakIterator::createWordInstance('');
132 8
        $iterator->setText($text);
133 8
        $stringLength = 0;
134 8
        $wordCount = 0;
135 8
        foreach ($iterator->getPartsIterator() as $part) {
136 2
            $stringLength += mb_strlen($part);
137 2
            if (\IntlBreakIterator::WORD_NONE === $iterator->getRuleStatus()) {
138 2
                continue;
139
            }
140 2
            if (++$wordCount === $limit) {
141
                break;
142
            }
143
        }
144 8
        return $stringLength;
145
    }
146
147
    protected static function excerptSplit(string $text, int $limit): int
148
    {
149
        preg_match('/^\s*+(?:\S++\s*+){1,'.$limit.'}/u', $text, $matches);
150
        if (mb_strlen($text) === mb_strlen($matches[0] ?? '')) {
151
            return mb_strlen($text);
152
        }
153
        return mb_strlen(rtrim($matches[0]));
154
    }
155
156 8
    protected static function extractParagraphs(string $text, int $length): array
157
    {
158 8
        $paragraphs = preg_split('/\R+/um', $text);
159 8
        $paragraphs = array_map('trim', $paragraphs);
160 8
        foreach ($paragraphs as &$paragraph) {
161 8
            $paragraphLength = mb_strlen($paragraph);
162 8
            if ($length >= $paragraphLength) {
163 8
                $paragraph = sprintf('<p>%s</p>', $paragraph);
164 8
                $length -= $paragraphLength;
165 8
                continue;
166
            }
167
            if ($length > 0) {
168
                $hidden = mb_substr($paragraph, $length);
169
                $visible = mb_substr($paragraph, 0, $length);
170
                $paragraph = glsr(Builder::class)->p([
171
                    'class' => 'glsr-hidden-text',
172
                    'data-show-less' => __('Show less', 'site-reviews'),
173
                    'data-show-more' => __('Show more', 'site-reviews'),
174
                    'data-trigger' => glsr_get_option('reviews.excerpts_action') ?: 'expand',
175
                    'text' => sprintf('%s<span class="glsr-hidden">%s</span>', $visible, $hidden),
176
                ]);
177
                $length = 0;
178
                continue;
179
            }
180
            $paragraph = glsr(Builder::class)->p([
181
                'class' => 'glsr-hidden',
182
                'text' => $paragraph,
183
            ]);
184
        }
185 8
        return $paragraphs;
186
    }
187
}
188