• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 18389505594

09 Oct 2025 09:21PM UTC coverage: 84.39% (+0.03%) from 84.362%
18389505594

Pull #9751

github

web-flow
Merge 3d707799b into d945236b2
Pull Request #9751: refactor(app): Standardize subdomain detection logic

16 of 16 new or added lines in 3 files covered. (100.0%)

43 existing lines in 6 files now uncovered.

21236 of 25164 relevant lines covered (84.39%)

195.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.26
/system/Validation/FormatRules.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Validation;
15

16
use DateTime;
17

18
/**
19
 * Format validation Rules.
20
 *
21
 * @see \CodeIgniter\Validation\FormatRulesTest
22
 */
23
class FormatRules
24
{
25
    /**
26
     * Alpha
27
     *
28
     * @param string|null $str
29
     */
30
    public function alpha($str = null): bool
31
    {
32
        if (! is_string($str)) {
18✔
33
            $str = (string) $str;
3✔
34
        }
35

36
        return ctype_alpha($str);
17✔
37
    }
38

39
    /**
40
     * Alpha with spaces.
41
     *
42
     * @param string|null $value Value.
43
     *
44
     * @return bool True if alpha with spaces, else false.
45
     */
46
    public function alpha_space($value = null): bool
47
    {
48
        if ($value === null) {
13✔
49
            return true;
1✔
50
        }
51

52
        if (! is_string($value)) {
12✔
53
            $value = (string) $value;
×
54
        }
55

56
        // @see https://regex101.com/r/LhqHPO/1
57
        return (bool) preg_match('/\A[A-Z ]+\z/i', $value);
12✔
58
    }
59

60
    /**
61
     * Alphanumeric with underscores and dashes
62
     *
63
     * @see https://regex101.com/r/XfVY3d/1
64
     *
65
     * @param string|null $str
66
     */
67
    public function alpha_dash($str = null): bool
68
    {
69
        if ($str === null) {
7✔
70
            return false;
1✔
71
        }
72

73
        if (! is_string($str)) {
6✔
74
            $str = (string) $str;
×
75
        }
76

77
        return preg_match('/\A[a-z0-9_-]+\z/i', $str) === 1;
6✔
78
    }
79

80
    /**
81
     * Alphanumeric, spaces, and a limited set of punctuation characters.
82
     * Accepted punctuation characters are: ~ tilde, ! exclamation,
83
     * # number, $ dollar, % percent, & ampersand, * asterisk, - dash,
84
     * _ underscore, + plus, = equals, | vertical bar, : colon, . period
85
     * ~ ! # $ % & * - _ + = | : .
86
     *
87
     * @param string|null $str
88
     *
89
     * @return bool
90
     *
91
     * @see https://regex101.com/r/6N8dDY/1
92
     */
93
    public function alpha_numeric_punct($str)
94
    {
95
        if ($str === null) {
35✔
96
            return false;
1✔
97
        }
98

99
        if (! is_string($str)) {
34✔
100
            $str = (string) $str;
×
101
        }
102

103
        return preg_match('/\A[A-Z0-9 ~!#$%\&\*\-_+=|:.]+\z/i', $str) === 1;
34✔
104
    }
105

106
    /**
107
     * Alphanumeric
108
     *
109
     * @param string|null $str
110
     */
111
    public function alpha_numeric($str = null): bool
112
    {
113
        if (! is_string($str)) {
9✔
114
            $str = (string) $str;
1✔
115
        }
116

117
        return ctype_alnum($str);
9✔
118
    }
119

120
    /**
121
     * Alphanumeric w/ spaces
122
     *
123
     * @param string|null $str
124
     */
125
    public function alpha_numeric_space($str = null): bool
126
    {
127
        if (! is_string($str)) {
7✔
128
            $str = (string) $str;
1✔
129
        }
130

131
        // @see https://regex101.com/r/0AZDME/1
132
        return (bool) preg_match('/\A[A-Z0-9 ]+\z/i', $str);
7✔
133
    }
134

135
    /**
136
     * Any type of string
137
     *
138
     * Note: we specifically do NOT type hint $str here so that
139
     * it doesn't convert numbers into strings.
140
     *
141
     * @param string|null $str
142
     */
143
    public function string($str = null): bool
144
    {
145
        return is_string($str);
8✔
146
    }
147

148
    /**
149
     * Decimal number
150
     *
151
     * @param string|null $str
152
     */
153
    public function decimal($str = null): bool
154
    {
155
        if (! is_string($str)) {
19✔
156
            $str = (string) $str;
2✔
157
        }
158

159
        // @see https://regex101.com/r/HULifl/2/
160
        return (bool) preg_match('/\A[-+]?\d{0,}\.?\d+\z/', $str);
19✔
161
    }
162

163
    /**
164
     * String of hexidecimal characters
165
     *
166
     * @param string|null $str
167
     */
168
    public function hex($str = null): bool
169
    {
170
        if (! is_string($str)) {
7✔
171
            $str = (string) $str;
1✔
172
        }
173

174
        return ctype_xdigit($str);
7✔
175
    }
176

177
    /**
178
     * Integer
179
     *
180
     * @param string|null $str
181
     */
182
    public function integer($str = null): bool
183
    {
184
        if (! is_string($str)) {
20✔
185
            $str = (string) $str;
5✔
186
        }
187

188
        return (bool) preg_match('/\A[\-+]?\d+\z/', $str);
20✔
189
    }
190

191
    /**
192
     * Is a Natural number  (0,1,2,3, etc.)
193
     *
194
     * @param string|null $str
195
     */
196
    public function is_natural($str = null): bool
197
    {
198
        if (! is_string($str)) {
9✔
199
            $str = (string) $str;
1✔
200
        }
201

202
        return ctype_digit($str);
9✔
203
    }
204

205
    /**
206
     * Is a Natural number, but not a zero  (1,2,3, etc.)
207
     *
208
     * @param string|null $str
209
     */
210
    public function is_natural_no_zero($str = null): bool
211
    {
212
        if (! is_string($str)) {
39✔
213
            $str = (string) $str;
4✔
214
        }
215

216
        return $str !== '0' && ctype_digit($str);
39✔
217
    }
218

219
    /**
220
     * Numeric
221
     *
222
     * @param string|null $str
223
     */
224
    public function numeric($str = null): bool
225
    {
226
        if (! is_string($str)) {
34✔
227
            $str = (string) $str;
8✔
228
        }
229

230
        // @see https://regex101.com/r/bb9wtr/2
231
        return (bool) preg_match('/\A[\-+]?\d*\.?\d+\z/', $str);
34✔
232
    }
233

234
    /**
235
     * Compares value against a regular expression pattern.
236
     *
237
     * @param string|null $str
238
     */
239
    public function regex_match($str, string $pattern): bool
240
    {
241
        if (! is_string($str)) {
12✔
242
            $str = (string) $str;
×
243
        }
244

245
        if (! str_starts_with($pattern, '/')) {
12✔
246
            $pattern = "/{$pattern}/";
2✔
247
        }
248

249
        return (bool) preg_match($pattern, $str);
12✔
250
    }
251

252
    /**
253
     * Validates that the string is a valid timezone as per the
254
     * timezone_identifiers_list function.
255
     *
256
     * @see http://php.net/manual/en/datetimezone.listidentifiers.php
257
     *
258
     * @param string|null $str
259
     */
260
    public function timezone($str = null): bool
261
    {
262
        if (! is_string($str)) {
7✔
263
            $str = (string) $str;
1✔
264
        }
265

266
        return in_array($str, timezone_identifiers_list(), true);
7✔
267
    }
268

269
    /**
270
     * Valid Base64
271
     *
272
     * Tests a string for characters outside of the Base64 alphabet
273
     * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045
274
     *
275
     * @param string|null $str
276
     */
277
    public function valid_base64($str = null): bool
278
    {
279
        if ($str === null) {
7✔
280
            return false;
1✔
281
        }
282

283
        if (! is_string($str)) {
6✔
284
            $str = (string) $str;
×
285
        }
286

287
        $decoded = base64_decode($str, true);
6✔
288

289
        if ($decoded === false) {
6✔
290
            return false;
2✔
291
        }
292

293
        return base64_encode($decoded) === $str;
4✔
294
    }
295

296
    /**
297
     * Valid JSON
298
     *
299
     * @param string|null $str
300
     */
301
    public function valid_json($str = null): bool
302
    {
303
        if (! is_string($str)) {
17✔
304
            $str = (string) $str;
1✔
305
        }
306

307
        json_decode($str);
17✔
308

309
        return json_last_error() === JSON_ERROR_NONE;
17✔
310
    }
311

312
    /**
313
     * Checks for a correctly formatted email address
314
     *
315
     * @param string|null $str
316
     */
317
    public function valid_email($str = null): bool
318
    {
319
        if (! is_string($str)) {
37✔
320
            $str = (string) $str;
4✔
321
        }
322

323
        // @see https://regex101.com/r/wlJG1t/1/
324
        if (function_exists('idn_to_ascii') && defined('INTL_IDNA_VARIANT_UTS46') && preg_match('#\A([^@]+)@(.+)\z#', $str, $matches)) {
37✔
325
            $str = $matches[1] . '@' . idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46);
18✔
326
        }
327

328
        return (bool) filter_var($str, FILTER_VALIDATE_EMAIL);
37✔
329
    }
330

331
    /**
332
     * Validate a comma-separated list of email addresses.
333
     *
334
     * Example:
335
     *     valid_emails[one@example.com,two@example.com]
336
     *
337
     * @param string|null $str
338
     */
339
    public function valid_emails($str = null): bool
340
    {
341
        if (! is_string($str)) {
14✔
342
            $str = (string) $str;
2✔
343
        }
344

345
        foreach (explode(',', $str) as $email) {
14✔
346
            $email = trim($email);
14✔
347

348
            if ($email === '') {
14✔
349
                return false;
3✔
350
            }
351

352
            if ($this->valid_email($email) === false) {
11✔
353
                return false;
5✔
354
            }
355
        }
356

357
        return true;
6✔
358
    }
359

360
    /**
361
     * Validate an IP address (human readable format or binary string - inet_pton)
362
     *
363
     * @param string|null $ip
364
     * @param string|null $which IP protocol: 'ipv4' or 'ipv6'
365
     */
366
    public function valid_ip($ip = null, ?string $which = null): bool
367
    {
368
        if (! is_string($ip)) {
35✔
369
            $ip = (string) $ip;
1✔
370
        }
371

372
        if ($ip === '') {
35✔
373
            return false;
1✔
374
        }
375

376
        $option = match (strtolower($which ?? '')) {
34✔
377
            'ipv4'  => FILTER_FLAG_IPV4,
4✔
378
            'ipv6'  => FILTER_FLAG_IPV6,
15✔
379
            default => 0,
26✔
380
        };
34✔
381

382
        return filter_var($ip, FILTER_VALIDATE_IP, $option) !== false
34✔
383
            || (! ctype_print($ip) && filter_var(inet_ntop($ip), FILTER_VALIDATE_IP, $option) !== false);
34✔
384
    }
385

386
    /**
387
     * Checks a string to ensure it is (loosely) a URL.
388
     *
389
     * Warning: this rule will pass basic strings like
390
     * "banana"; use valid_url_strict for a stricter rule.
391
     *
392
     * @param string|null $str
393
     */
394
    public function valid_url($str = null): bool
395
    {
396
        if ($str === null || $str === '') {
35✔
397
            return false;
3✔
398
        }
399

400
        if (! is_string($str)) {
32✔
UNCOV
401
            $str = (string) $str;
×
402
        }
403

404
        if (preg_match('/\A(?:([^:]*)\:)?\/\/(.+)\z/', $str, $matches)) {
32✔
405
            if (! in_array($matches[1], ['http', 'https'], true)) {
14✔
406
                return false;
4✔
407
            }
408

409
            $str = $matches[2];
10✔
410
        }
411

412
        $str = 'http://' . $str;
28✔
413

414
        return filter_var($str, FILTER_VALIDATE_URL) !== false;
28✔
415
    }
416

417
    /**
418
     * Checks a URL to ensure it's formed correctly.
419
     *
420
     * @param string|null $str
421
     * @param string|null $validSchemes comma separated list of allowed schemes
422
     */
423
    public function valid_url_strict($str = null, ?string $validSchemes = null): bool
424
    {
425
        if (in_array($str, [null, '', '0'], true)) {
37✔
426
            return false;
3✔
427
        }
428

429
        if (! is_string($str)) {
34✔
UNCOV
430
            $str = (string) $str;
×
431
        }
432

433
        // parse_url() may return null and false
434
        $scheme       = strtolower((string) parse_url($str, PHP_URL_SCHEME));
34✔
435
        $validSchemes = explode(
34✔
436
            ',',
34✔
437
            strtolower($validSchemes ?? 'http,https'),
34✔
438
        );
34✔
439

440
        return in_array($scheme, $validSchemes, true)
34✔
441
            && filter_var($str, FILTER_VALIDATE_URL) !== false;
34✔
442
    }
443

444
    /**
445
     * Checks for a valid date and matches a given date format
446
     *
447
     * @param string|null           $str
448
     * @param non-empty-string|null $format
449
     */
450
    public function valid_date($str = null, ?string $format = null): bool
451
    {
452
        if (! is_string($str)) {
71✔
453
            $str = (string) $str;
1✔
454
        }
455

456
        if ($str === '') {
71✔
457
            return false;
1✔
458
        }
459

460
        if ($format === null || $format === '') {
70✔
461
            return strtotime($str) !== false;
16✔
462
        }
463

464
        $date   = DateTime::createFromFormat($format, $str);
54✔
465
        $errors = DateTime::getLastErrors();
54✔
466

467
        if ($date === false) {
54✔
468
            return false;
18✔
469
        }
470

471
        // PHP 8.2 or later.
472
        if ($errors === false) {
36✔
473
            return true;
28✔
474
        }
475

476
        return $errors['warning_count'] === 0 && $errors['error_count'] === 0;
8✔
477
    }
478
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc