Merge pull request #16606 from ejegg/API4Regex-5.23
[civicrm-core.git] / tests / phpunit / CRM / Utils / StringTest.php
CommitLineData
6a488035 1<?php
aba1cd8b
EM
2
3/**
4 * Class CRM_Utils_StringTest
acb109b7 5 * @group headless
aba1cd8b 6 */
6a488035 7class CRM_Utils_StringTest extends CiviUnitTestCase {
6a488035 8
00be9182 9 public function setUp() {
6a488035
TO
10 parent::setUp();
11 }
12
00be9182 13 public function testStripPathChars() {
9099cab3 14 $testSet = [
6a488035
TO
15 '' => '',
16 NULL => NULL,
17 'civicrm' => 'civicrm',
18 'civicrm/dashboard' => 'civicrm/dashboard',
19 'civicrm/contribute/transact' => 'civicrm/contribute/transact',
20 'civicrm/<hack>attempt</hack>' => 'civicrm/_hack_attempt_/hack_',
21 'civicrm dashboard & force = 1,;' => 'civicrm_dashboard___force___1__',
9099cab3 22 ];
6a488035 23
6a488035
TO
24 foreach ($testSet as $in => $expected) {
25 $out = CRM_Utils_String::stripPathChars($in);
26 $this->assertEquals($out, $expected, "Output does not match");
27 }
28 }
29
00be9182 30 public function testExtractName() {
9099cab3
CW
31 $cases = [
32 [
6a488035
TO
33 'full_name' => 'Alan',
34 'first_name' => 'Alan',
9099cab3
CW
35 ],
36 [
6a488035
TO
37 'full_name' => 'Alan Arkin',
38 'first_name' => 'Alan',
39 'last_name' => 'Arkin',
9099cab3
CW
40 ],
41 [
6a488035
TO
42 'full_name' => '"Alan Arkin"',
43 'first_name' => 'Alan',
44 'last_name' => 'Arkin',
9099cab3
CW
45 ],
46 [
6a488035
TO
47 'full_name' => 'Alan A Arkin',
48 'first_name' => 'Alan',
49 'middle_name' => 'A',
50 'last_name' => 'Arkin',
9099cab3
CW
51 ],
52 [
6a488035
TO
53 'full_name' => 'Adams, Amy',
54 'first_name' => 'Amy',
55 'last_name' => 'Adams',
9099cab3
CW
56 ],
57 [
6a488035
TO
58 'full_name' => 'Adams, Amy A',
59 'first_name' => 'Amy',
60 'middle_name' => 'A',
61 'last_name' => 'Adams',
9099cab3
CW
62 ],
63 [
6a488035
TO
64 'full_name' => '"Adams, Amy A"',
65 'first_name' => 'Amy',
66 'middle_name' => 'A',
67 'last_name' => 'Adams',
9099cab3
CW
68 ],
69 ];
6a488035 70 foreach ($cases as $case) {
9099cab3 71 $actual = [];
6a488035
TO
72 CRM_Utils_String::extractName($case['full_name'], $actual);
73 $this->assertEquals($actual['first_name'], $case['first_name']);
e65f9c8f
E
74 $this->assertEquals(CRM_Utils_Array::value('last_name', $actual), CRM_Utils_Array::value('last_name', $case));
75 $this->assertEquals(CRM_Utils_Array::value('middle_name', $actual), CRM_Utils_Array::value('middle_name', $case));
6a488035
TO
76 }
77 }
78
00be9182 79 public function testEllipsify() {
6a488035 80 $maxLen = 5;
9099cab3 81 $cases = [
6a488035
TO
82 '1' => '1',
83 '12345' => '12345',
84 '123456' => '12...',
9099cab3 85 ];
6a488035
TO
86 foreach ($cases as $input => $expected) {
87 $this->assertEquals($expected, CRM_Utils_String::ellipsify($input, $maxLen));
7a0ea0f3 88 }
69a8e7e6 89 // test utf-8 string, CRM-18997
90 $input = 'Registro de eventos on-line: Taller: "Onboarding - Cómo integrar exitosamente a los nuevos talentos dentro de su organización - Formación práctica."';
91 $maxLen = 128;
06abe035 92 $this->assertEquals(TRUE, mb_check_encoding(CRM_Utils_String::ellipsify($input, $maxLen), 'UTF-8'));
6a488035
TO
93 }
94
00be9182 95 public function testRandom() {
6a488035
TO
96 for ($i = 0; $i < 4; $i++) {
97 $actual = CRM_Utils_String::createRandom(4, 'abc');
98 $this->assertEquals(4, strlen($actual));
99 $this->assertRegExp('/^[abc]+$/', $actual);
100
101 $actual = CRM_Utils_String::createRandom(6, '12345678');
102 $this->assertEquals(6, strlen($actual));
103 $this->assertRegExp('/^[12345678]+$/', $actual);
104 }
105 }
fc7a0aee 106
4cbe18b8
EM
107 /**
108 * @return array
109 */
fc7a0aee 110 public function parsePrefixData() {
9099cab3
CW
111 $cases = [];
112 $cases[] = ['administer CiviCRM', NULL, [NULL, 'administer CiviCRM']];
113 $cases[] = ['administer CiviCRM', 'com_civicrm', ['com_civicrm', 'administer CiviCRM']];
114 $cases[] = ['Drupal:access user profiles', NULL, ['Drupal', 'access user profiles']];
115 $cases[] = ['Joomla:component:perm', NULL, ['Joomla', 'component:perm']];
fc7a0aee
TO
116 return $cases;
117 }
118
119 /**
120 * @dataProvider parsePrefixData
1e1fdcf6
EM
121 * @param $input
122 * @param $defaultPrefix
123 * @param $expected
fc7a0aee
TO
124 */
125 public function testParsePrefix($input, $defaultPrefix, $expected) {
126 $actual = CRM_Utils_String::parsePrefix(':', $input, $defaultPrefix);
127 $this->assertEquals($expected, $actual);
128 }
a5b8726f 129
7fe37828
EM
130 /**
131 * @return array
132 */
00be9182 133 public function booleanDataProvider() {
39b959db 134 // array(0 => $input, 1 => $expectedOutput)
9099cab3
CW
135 $cases = [];
136 $cases[] = [TRUE, TRUE];
137 $cases[] = [FALSE, FALSE];
138 $cases[] = [1, TRUE];
139 $cases[] = [0, FALSE];
140 $cases[] = ['1', TRUE];
141 $cases[] = ['0', FALSE];
142 $cases[] = [TRUE, TRUE];
143 $cases[] = [FALSE, FALSE];
144 $cases[] = ['Y', TRUE];
145 $cases[] = ['N', FALSE];
146 $cases[] = ['y', TRUE];
147 $cases[] = ['n', FALSE];
148 $cases[] = ['Yes', TRUE];
149 $cases[] = ['No', FALSE];
150 $cases[] = ['True', TRUE];
151 $cases[] = ['False', FALSE];
152 $cases[] = ['yEs', TRUE];
153 $cases[] = ['nO', FALSE];
154 $cases[] = ['tRuE', TRUE];
155 $cases[] = ['FaLsE', FALSE];
a5b8726f
TO
156 return $cases;
157 }
158
159 /**
160 * @param $input
5a4f6742
CW
161 * @param bool $expected
162 * * @dataProvider booleanDataProvider
a5b8726f 163 */
00be9182 164 public function testStrToBool($input, $expected) {
a5b8726f
TO
165 $actual = CRM_Utils_String::strtobool($input);
166 $this->assertTrue($expected === $actual);
167 }
168
83d511e6 169 public function startEndCases() {
9099cab3
CW
170 $cases = [];
171 $cases[] = ['startsWith', 'foo', '', TRUE];
172 $cases[] = ['startsWith', 'foo', 'f', TRUE];
173 $cases[] = ['startsWith', 'foo', 'fo', TRUE];
174 $cases[] = ['startsWith', 'foo', 'foo', TRUE];
175 $cases[] = ['startsWith', 'foo', 'fooo', FALSE];
176 $cases[] = ['startsWith', 'foo', 'o', FALSE];
177 $cases[] = ['endsWith', 'foo', 'f', FALSE];
178 $cases[] = ['endsWith', 'foo', '', TRUE];
179 $cases[] = ['endsWith', 'foo', 'o', TRUE];
180 $cases[] = ['endsWith', 'foo', 'oo', TRUE];
181 $cases[] = ['endsWith', 'foo', 'foo', TRUE];
182 $cases[] = ['endsWith', 'foo', 'fooo', FALSE];
183 $cases[] = ['endsWith', 'foo*', '*', TRUE];
83d511e6
TO
184 return $cases;
185 }
186
187 /**
188 * @param string $func
189 * One of: 'startsWith' or 'endsWith'.
190 * @param $string
191 * @param $fragment
192 * @param $expectedResult
193 * @dataProvider startEndCases
194 */
195 public function testStartEndWith($func, $string, $fragment, $expectedResult) {
196 $actualResult = \CRM_Utils_String::$func($string, $fragment);
197 $this->assertEquals($expectedResult, $actualResult, "Checking $func($string,$fragment)");
198 }
199
200 public function wildcardCases() {
9099cab3
CW
201 $cases = [];
202 $cases[] = ['*', ['foo.bar.1', 'foo.bar.2', 'foo.whiz', 'bang.bang']];
203 $cases[] = ['foo.*', ['foo.bar.1', 'foo.bar.2', 'foo.whiz']];
204 $cases[] = ['foo.bar.*', ['foo.bar.1', 'foo.bar.2']];
205 $cases[] = [['foo.bar.*', 'foo.bar.2'], ['foo.bar.1', 'foo.bar.2']];
206 $cases[] = [['foo.bar.2', 'foo.w*'], ['foo.bar.2', 'foo.whiz']];
83d511e6
TO
207 return $cases;
208 }
209
210 /**
211 * @param $patterns
212 * @param $expectedResults
213 * @dataProvider wildcardCases
214 */
215 public function testFilterByWildCards($patterns, $expectedResults) {
9099cab3 216 $data = ['foo.bar.1', 'foo.bar.2', 'foo.whiz', 'bang.bang'];
83d511e6
TO
217
218 $actualResults = CRM_Utils_String::filterByWildcards($patterns, $data);
219 $this->assertEquals($expectedResults, $actualResults);
220
221 $patterns = (array) $patterns;
222 $patterns[] = 'noise';
223
224 $actualResults = CRM_Utils_String::filterByWildcards($patterns, $data, FALSE);
225 $this->assertEquals($expectedResults, $actualResults);
226
227 $actualResults = CRM_Utils_String::filterByWildcards($patterns, $data, TRUE);
9099cab3 228 $this->assertEquals(array_merge($expectedResults, ['noise']), $actualResults);
83d511e6
TO
229 }
230
6c094ca6
SM
231 /**
232 * CRM-20821
233 * CRM-14283
234 *
235 * @param string $imageURL
236 * @param book $forceHttps
237 * @param string $expected
238 *
239 * @dataProvider simplifyURLProvider
240 */
241 public function testSimplifyURL($imageURL, $forceHttps, $expected) {
242 $this->assertEquals(
243 $expected,
244 CRM_Utils_String::simplifyURL($imageURL, $forceHttps)
245 );
246 }
247
248 /**
249 * Used for testNormalizeImageURL above
250 *
251 * @return array
252 */
253 public function simplifyURLProvider() {
6c094ca6 254 $config = CRM_Core_Config::singleton();
57530e56
TO
255 $urlParts = CRM_Utils_String::simpleParseUrl($config->userFrameworkBaseURL);
256 $localDomain = $urlParts['host+port'];
257 if (empty($localDomain)) {
258 throw new \Exception("Failed to determine local base URL");
259 }
6c094ca6
SM
260 $externalDomain = 'example.org';
261
262 // Ensure that $externalDomain really is different from $localDomain
263 if ($externalDomain == $localDomain) {
264 $externalDomain = 'example.net';
265 }
266
9099cab3
CW
267 return [
268 'prototypical example' => [
6c094ca6
SM
269 "https://$localDomain/sites/default/files/coffee-mug.jpg",
270 FALSE,
271 '/sites/default/files/coffee-mug.jpg',
9099cab3
CW
272 ],
273 'external domain with https' => [
6c094ca6
SM
274 "https://$externalDomain/sites/default/files/coffee-mug.jpg",
275 FALSE,
276 "https://$externalDomain/sites/default/files/coffee-mug.jpg",
9099cab3
CW
277 ],
278 'external domain with http forced to https' => [
6c094ca6
SM
279 "http://$externalDomain/sites/default/files/coffee-mug.jpg",
280 TRUE,
281 "https://$externalDomain/sites/default/files/coffee-mug.jpg",
9099cab3
CW
282 ],
283 'external domain with http not forced' => [
6c094ca6
SM
284 "http://$externalDomain/sites/default/files/coffee-mug.jpg",
285 FALSE,
286 "http://$externalDomain/sites/default/files/coffee-mug.jpg",
9099cab3
CW
287 ],
288 'local URL' => [
6c094ca6
SM
289 "/sites/default/files/coffee-mug.jpg",
290 FALSE,
291 "/sites/default/files/coffee-mug.jpg",
9099cab3
CW
292 ],
293 'local URL without a forward slash' => [
6c094ca6
SM
294 "sites/default/files/coffee-mug.jpg",
295 FALSE,
296 "/sites/default/files/coffee-mug.jpg",
9099cab3
CW
297 ],
298 'empty input' => [
6c094ca6
SM
299 '',
300 FALSE,
301 '',
9099cab3
CW
302 ],
303 ];
6c094ca6
SM
304 }
305
306 /**
307 * @param string $url
308 * @param array $expected
309 *
310 * @dataProvider parseURLProvider
311 */
312 public function testSimpleParseUrl($url, $expected) {
313 $this->assertEquals(
314 $expected,
315 CRM_Utils_String::simpleParseUrl($url)
316 );
317 }
318
319 /**
320 * Used for testSimpleParseUrl above
321 *
322 * @return array
323 */
324 public function parseURLProvider() {
9099cab3
CW
325 return [
326 "prototypical example" => [
6c094ca6 327 "https://example.com:8000/foo/bar/?id=1#fragment",
9099cab3 328 [
6c094ca6
SM
329 'host+port' => "example.com:8000",
330 'path+query' => "/foo/bar/?id=1",
9099cab3
CW
331 ],
332 ],
333 "default port example" => [
57530e56 334 "https://example.com/foo/bar/?id=1#fragment",
9099cab3 335 [
57530e56
TO
336 'host+port' => "example.com",
337 'path+query' => "/foo/bar/?id=1",
9099cab3
CW
338 ],
339 ],
340 "empty" => [
6c094ca6 341 "",
9099cab3 342 [
6c094ca6
SM
343 'host+port' => "",
344 'path+query' => "",
9099cab3
CW
345 ],
346 ],
347 "path only" => [
6c094ca6 348 "/foo/bar/image.png",
9099cab3 349 [
6c094ca6
SM
350 'host+port' => "",
351 'path+query' => "/foo/bar/image.png",
9099cab3
CW
352 ],
353 ],
354 ];
6c094ca6
SM
355 }
356
7652022e
SL
357 public function purifyHTMLProvider() {
358 $tests = [];
359 $tests[] = ['<span onmouseover=alert(0)>HOVER</span>', '<span>HOVER</span>'];
18b586ef 360 $tests[] = ['<a href="https://civicrm.org" target="_blank" class="button-purple">hello</a>', '<a href="https://civicrm.org" target="_blank" class="button-purple" rel="noreferrer noopener">hello</a>'];
7652022e
SL
361 return $tests;
362 }
363
364 /**
365 * Test ouput of purifyHTML
366 * @param string $testString
367 * @param string $expectedString
368 * @dataProvider purifyHTMLProvider
369 */
370 public function testPurifyHTML($testString, $expectedString) {
371 $this->assertEquals($expectedString, CRM_Utils_String::purifyHTML($testString));
372 }
373
b2ce794b
TO
374 public function getGoodSerializeExamples() {
375 $strs = [];
376
377 $strs[] = ['a:1:{s:1:"a";s:1:"b";}'];
378 $strs[] = ['d:1.2;'];
379 $strs[] = ['s:3:"abc";'];
380 $strs[] = ['N;'];
381 $strs[] = ['a:7:{i:0;N;i:1;s:3:"abc";i:2;i:1;i:3;d:2.3;i:4;b:1;i:5;b:0;i:6;i:0;}'];
382
383 return $strs;
384 }
385
386 /**
387 * @param string $str
388 * A safe serialized value.
389 * @dataProvider getGoodSerializeExamples
390 */
391 public function testGoodSerialize($str) {
392 $this->assertEquals(unserialize($str), CRM_Utils_String::unserialize($str));
393 }
394
395 public function getBadSerializeExamples() {
396 $strs = [];
397
398 $strs[] = ['O:8:"stdClass":0:{}'];
399 $strs[] = ['O:9:"Exception":7:{s:10:"*message";s:3:"abc";s:17:"Exceptionstring";s:0:"";s:7:"*code";i:0;s:7:"*file";s:17:"Command line code";s:7:"*line";i:1;s:16:"Exceptiontrace";a:0:{}s:19:"Exceptionprevious";N;}'];
400
401 return $strs;
402 }
403
404 /**
405 * @param string $str
406 * An unsafe serialized value.
407 * @dataProvider getBadSerializeExamples
b2ce794b
TO
408 */
409 public function testBadSerializeExamples($str) {
ddcfa228 410 $this->assertFalse(CRM_Utils_String::unserialize($str));
b2ce794b
TO
411 }
412
6a488035 413}