Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
13 / 13
CRAP
100.00% covered (success)
100.00%
1 / 1
Mbstring
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
13 / 13
22
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInstance
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 strlen
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 substr
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 strtolower
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 strtoupper
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 strstr
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 preg_match
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 preg_match_all
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 preg_split
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 preg_replace_callback
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 preg_replace
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 appendU
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * The string wrapper class using a mbstring extension.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Anatoliy Belsky <ab@php.net>
12 * @copyright 2009-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2009-04-06
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Strings;
21
22use phpMyFAQ\Language;
23
24/**
25 * Class Mbstring
26 *
27 * @package phpMyFAQ\Strings
28 */
29class Mbstring extends AbstractString
30{
31    /**
32     * Instance.
33     */
34    private static ?Mbstring $mbstring = null;
35
36    /**
37     * Constructor.
38     */
39    final private function __construct()
40    {
41    }
42
43    /**
44     * Create and return an instance.
45     */
46    public static function getInstance(string $language = 'en'): Mbstring
47    {
48        if (!self::$mbstring instanceof Mbstring) {
49            self::$mbstring = new self();
50            self::$mbstring->encoding = self::DEFAULT_ENCODING;
51            self::$mbstring->language = Language::isASupportedLanguage($language) ? $language : self::DEFAULT_LANGUAGE;
52        }
53
54        return self::$mbstring;
55    }
56
57    /**
58     * Get string character count.
59     *
60     * @param string $str String
61     */
62    public function strlen(string $str): int
63    {
64        return mb_strlen($str, $this->encoding);
65    }
66
67    /**
68     * Get a part of string.
69     *
70     * @param string   $str String
71     * @param int      $start Start
72     * @param int|null $length Length
73     */
74    public function substr(string $str, int $start, ?int $length = null): string
75    {
76        $length = $length === null || $length === 0 ? mb_strlen($str, $this->encoding) : $length;
77
78        return mb_substr($str, $start, $length, $this->encoding);
79    }
80
81    /**
82     * Make a string lower case.
83     *
84     * @param string $str String
85     */
86    public function strtolower(string $str): string
87    {
88        return mb_strtolower($str, $this->encoding);
89    }
90
91    /**
92     * Make a string upper case.
93     *
94     * @param string $str String
95     */
96    public function strtoupper(string $str): string
97    {
98        return mb_strtoupper($str, $this->encoding);
99    }
100
101    /**
102     * Get the first occurrence of a string within another.
103     *
104     * @param string $haystack Haystack
105     * @param string $needle Needle
106     * @param bool $part Part
107     */
108    public function strstr(string $haystack, string $needle, bool $part = false): string|false
109    {
110        return mb_strstr($haystack, $needle, $part, $this->encoding);
111    }
112
113    /**
114     * Match a regexp.
115     *
116     * @param array<array-key, mixed>|null $matches
117     * @return int|false
118     */
119    public function preg_match(
120        string $pattern,
121        string $subject,
122        &$matches = null,
123        int $flags = 0,
124        int $offset = 0,
125    ): int|false {
126        return preg_match($this->appendU($pattern), $subject, $matches, $flags, $offset);
127    }
128
129    /**
130     * Match a regexp globally.
131     *
132     * @param array<array-key, mixed>|null $matches
133     * @param int $flags
134     * @param int $offset
135     * @return int|false
136     */
137    public function preg_match_all(
138        string $pattern,
139        string $subject,
140        &$matches = null,
141        $flags = 0,
142        $offset = 0,
143    ): int|false {
144        return preg_match_all($this->appendU($pattern), $subject, $matches, $flags, $offset);
145    }
146
147    /**
148     * Split string by a regexp.
149     *
150     * @param int $limit
151     * @param int $flags
152     *
153     */
154    public function preg_split(string $pattern, string $subject, $limit = -1, $flags = 0): array|bool
155    {
156        return preg_split($this->appendU($pattern), $subject, $limit, $flags);
157    }
158
159    /**
160     * Search and replace by a regexp using a callback.
161     *
162     * @param string|string[] $pattern
163     * @param callable(array<array-key, string>): string $callback
164     * @param string|string[] $subject
165     *
166     * @return string|string[]|null
167     */
168    public function preg_replace_callback(
169        string|array $pattern,
170        callable $callback,
171        string|array $subject,
172        int $limit = -1,
173        int &$count = 0,
174    ): string|array|null {
175        if (is_array($pattern)) {
176            foreach ($pattern as &$item) {
177                $item = $this->appendU($item);
178            }
179            return preg_replace_callback($pattern, $callback, $subject, $limit, $count);
180        }
181
182        $pattern = $this->appendU($pattern);
183        return preg_replace_callback($pattern, $callback, $subject, $limit, $count);
184    }
185
186    /**
187     * Search and replace by a regexp.
188     *
189     * @param string|string[] $pattern
190     * @param string|string[] $replacement
191     * @param string|string[] $subject
192     *
193     * @return string|string[]|null
194     */
195    public function preg_replace(
196        string|array $pattern,
197        string|array $replacement,
198        string|array $subject,
199        int $limit = -1,
200        int &$count = 0,
201    ): string|array|null {
202        if (is_array($pattern)) {
203            foreach ($pattern as &$item) {
204                $item = $this->appendU($item);
205            }
206            return preg_replace($pattern, $replacement, $subject, $limit, $count);
207        }
208
209        $pattern = $this->appendU($pattern);
210        return preg_replace($pattern, $replacement, $subject, $limit, $count);
211    }
212
213    /**
214     * Append a "u" to the string.
215     * The string is supposed to be a regex prepared to use with a preg_* function.
216     */
217    private function appendU(string $str): string
218    {
219        return parent::isUTF8($str) ? $str . 'u' : $str;
220    }
221}