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

jkrrv / ScripturNum / 18861633312

28 Oct 2025 01:59AM UTC coverage: 99.715% (-0.3%) from 100.0%
18861633312

push

github

jkrrv
Better exception handling

3 of 5 new or added lines in 1 file covered. (60.0%)

700 of 702 relevant lines covered (99.72%)

338.4 hits per line

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

99.56
/src/ScripturNum.php
1
<?php
2

3

4
namespace ScripturNum;
5

6
use TypeError;
7

8
/**
9
 * The ScripturNum class, which represents a single continuous passage.
10
 *
11
 * @property-read ?int $int
12
 * @property-read ?int $book
13
 * @property-read ?int $startCh
14
 * @property-read ?int $startV
15
 * @property-read ?int $endCh
16
 * @property-read ?int $endV
17
 */
18
class ScripturNum
19
{
20
        protected $int;
21
        protected $book;
22
        protected $startCh;
23
        protected $startV;
24
        protected $endCh;
25
        protected $endV;
26

27
        const BOOK_MASK = 4278190080;
28
        const START_MASK = 16773120;
29
        const END_MASK = 4095;
30

31
        protected static $bibleClass = Bible::class;
32

33
        /**
34
         * ScripturNum constructor.
35
         *
36
         * @param int|string $intOrString ScripturNum int or a human-readable string.
37
         *
38
         * @throws ScripturNumException  Thrown if the provided int or string can't be understood.
39
         */
40
        public function __construct($intOrString)
41
        {
42
                if (is_numeric($intOrString)) {
1,881 ✔
43
                        $int = (int)$intOrString;
561 ✔
44
                } else {
45
                        try {
46
                                $int = self::stringToInt($intOrString);
1,507 ✔
47
                        } catch (TypeError $e) {
11 ✔
48
                                throw new ScripturNumException("Invalid value provided to ScripturNum constructor.");
11 ✔
49
                        }
50
                }
51
                self::intToRefNums($int, $this->book, $this->startCh, $this->startV, $this->endCh, $this->endV);
1,870 ✔
52
                $this->int = $int;
1,837 ✔
53
        }
501 ✔
54

55
        /**
56
         * @param $what
57
         *
58
         * @since 2.0.0
59
         *
60
         * @return int
61
         */
62
        public function __get($what)
63
        {
64
                switch ($what) {
17 ✔
65
                        case "int":
187 ✔
66
                                return $this->getInt();
11 ✔
67

68
                        case "book":
187 ✔
69
                                return $this->book;
187 ✔
70

71
                        case "startCh":
99 ✔
72
                                return $this->startCh;
99 ✔
73

74
                        case "startV":
99 ✔
75
                                return $this->startV;
11 ✔
76

77
                        case "endCh":
99 ✔
78
                                return $this->endCh;
99 ✔
79

80
                        case "endV":
11 ✔
81
                                return $this->endV;
11 ✔
82
                }
83
                throw new \Error("The requested property does not exist.");
11 ✔
84
        }
85

86

87
        protected static $stringSettings = [
88
                'abbrev' => [
89
                        'space' => '',
90
                        'cvsep' => '.',
91
                        'range' => '-',
92
                        'names' => 1,
93
                        'plurl' => false
94
                ],
95
                'long'   => [
96
                        'space' => ' ',
97
                        'cvsep' => ':',
98
                        'range' => '-',
99
                        'names' => 0,
100
                        'plurl' => true
101
                ],
102
        ];
103

104

105
        /**
106
         * Update string settings.
107
         *
108
         * @param       $key
109
         * @param array $settings
110
         *
111
         * @return void
112
         */
113
        public static function setStringSettings($key, array $settings)
114
        {
115
                if ( ! isset(static::$stringSettings[$key])) {
88 ✔
116
                        static::$stringSettings[$key] = [];
77 ✔
117
                }
118

119
                foreach (reset(static::$stringSettings) as $k => $v) {
88 ✔
120
                        if (isset($settings[$k])) {
88 ✔
121
                                static::$stringSettings[$key][$k] = $settings[$k];
79 ✔
122
                        }
123
                }
124
        }
24 ✔
125

126

127
        /**
128
         * Get the ScripturNum integer.
129
         *
130
         * @return int The ScripturNum integer
131
         */
132
        public function getInt(): int
133
        {
134
                return $this->int;
1,111 ✔
135
        }
136

137

138
        /**
139
         * Generic toString.  Uses the long form.
140
         *
141
         * @return string
142
         * @throws ScripturNumException
143
         */
144
        public function __toString(): string
145
        {
146
                return $this->getLongString();
220 ✔
147
        }
148

149

150
        /**
151
         * Get a human-readable abbreviation for the passage.  By default, these are meant for usage in short links.
152
         *
153
         * @return string An abbreviation
154
         * @throws ScripturNumException
155
         */
156
        public function getAbbrev(): string
157
        {
158
                return $this->toString('abbrev');
121 ✔
159
        }
160

161

162
        /**
163
         * Get a human-readable name of the passage.  By default, these are meant for humans to read.
164
         *
165
         * @return string The name of the passage, as one might pronounce it.
166
         * @throws ScripturNumException
167
         */
168
        public function getLongString(): string
169
        {
170
                return $this->toString('long');
528 ✔
171
        }
172

173

174
        /**
175
         * Get a string.  Publicly-accessible.
176
         *
177
         * @since 2.0.0
178
         *
179
         * @throws ScripturNumException
180
         */
181
        public function toString($options): string
182
        {
183
                $s = $this->getStringWithSettings($options);
836 ✔
184
                if (isset($options['callback']) && is_callable($options['callback'])) {
737 ✔
185
                        $s = call_user_func($options['callback'], $s, $this);
11 ✔
186
                }
187
                return $s;
737 ✔
188
        }
189

190

191
        /**
192
         * Returns a human-readable string with the settings defined in a given set of settings.
193
         *
194
         * @param string|array $options The setting set to use, or an array of options.
195
         *
196
         * @return string The human-intelligible string.
197
         * @throws ScripturNumException  If a setting is invalid.
198
         */
199
        protected function getStringWithSettings($options): string
200
        {
201
                $settingKey = 'long';
836 ✔
202
                if (is_string($options)) {
836 ✔
203
                        $settingKey = $options;
748 ✔
204
                        $options = [];
748 ✔
205
                } else if (is_array($options) && isset($options['settings'])) {
99 ✔
206
                        $settingKey = $options['settings'];
22 ✔
207
                }
208

209
                if ( ! isset(static::$stringSettings[$settingKey])) {
836 ✔
210
                        throw new ScripturNumException('Invalid key for creating a string.');
33 ✔
211
                }
212

213
                if ( ! isset(static::$stringSettings[$settingKey]['space'])) {
803 ✔
214
                        throw new ScripturNumException('Invalid space character.');
11 ✔
215
                }
216

217
                if ( ! isset(static::$stringSettings[$settingKey]['cvsep'])) {
792 ✔
218
                        throw new ScripturNumException('Invalid chapter-verse separation character.');
11 ✔
219
                }
220

221
                if ( ! isset(static::$stringSettings[$settingKey]['range'])) {
781 ✔
222
                        throw new ScripturNumException('Invalid range character.');
11 ✔
223
                }
224

225
                if ( ! isset(static::$stringSettings[$settingKey]['names']) ||
770 ✔
226
                     ! is_numeric(static::$stringSettings[$settingKey]['names'])) {
770 ✔
227
                        throw new ScripturNumException('Invalid name offset.');
11 ✔
228
                }
229

230
                if ( ! isset(static::$stringSettings[$settingKey]['plurl'])) {
759 ✔
231
                        throw new ScripturNumException('Plurality is not defined.');
22 ✔
232
                }
233

234

235
                $s = static::$stringSettings[$settingKey]['space'];
737 ✔
236
                $c = static::$stringSettings[$settingKey]['cvsep'];
737 ✔
237
                $r = static::$stringSettings[$settingKey]['range'];
737 ✔
238
                $n = (int)static::$stringSettings[$settingKey]['names'];
737 ✔
239
                $p = !! static::$stringSettings[$settingKey]['plurl'];
737 ✔
240

241
                if (!isset($options['excludeBook']) || !$options['excludeBook']) {
737 ✔
242
                        $b = static::getBookNames();
737 ✔
243

244
                        if ($n > count($b[$this->book - 1])) {
737 ✔
245
                                $n = count($b[$this->book - 1]) - 1;
11 ✔
246
                        }
247

248
                        $b = $b[$this->book - 1][$n];
737 ✔
249

250
                        if ($this->startCh !== $this->endCh && $p) {
737 ✔
251
                                $b = self::pluralizeBookName($b);
569 ✔
252
                        }
253
                } else {
254
                        $b = "";
33 ✔
255
                        $s = "";
33 ✔
256
                }
257

258
                if ($this->isWholeBook()) {
737 ✔
259
                        return $b;
66 ✔
260
                } elseif ($this->isWholeChapters()) {
671 ✔
261
                        if ($this->startCh === $this->endCh) {
385 ✔
262
                                return $b . $s . $this->startCh;
198 ✔
263
                        }
264

265
                        return $b . $s . $this->startCh . $r . $this->endCh;
220 ✔
266
                } else {
267
                        $startC = $this->startCh;
341 ✔
268
                        $endC = $this->endCh;
341 ✔
269
                        if (isset($options['excludeCh']) && !!$options['excludeCh']) {
341 ✔
270
                                $startC = "";
11 ✔
271
                                $endC = "";
11 ✔
272
                                $c = "";
11 ✔
273
                        }
274

275
                        if ($this->bookHasSingleChapter()) {
341 ✔
276
                                if ($this->startV === $this->endV) {
55 ✔
277
                                        return $b . $s . $this->startV;
33 ✔
278
                                }
279

280
                                return $b . $s . $this->startV . $r . $this->endV;
22 ✔
281
                        } elseif ($this->startCh === $this->endCh) {
286 ✔
282
                                if ($this->startV === $this->endV) {
198 ✔
283
                                        return $b . $s . $startC . $c . $this->startV;
143 ✔
284
                                }
285

286
                                return $b . $s . $startC . $c . $this->startV . $r . $this->endV;
88 ✔
287
                        }
288

289
                        return $b . $s . $startC . $c . $this->startV . $r . $endC . $c . $this->endV;
88 ✔
290
                }
291
        }
292

293

294
        /**
295
         * Returns true if the passage is an entire chapter.
296
         *
297
         * @return bool
298
         */
299
        public function isWholeChapters(): bool
300
        {
301
                $v = Bible::getVerseCounts();
682 ✔
302

303
                return ($this->startV === 1 && $this->endV === $v[$this->book - 1][$this->endCh - 1]);
682 ✔
304
        }
305

306
        /**
307
         * Returns a ScripturNum for the current range, expanded to the whole chapter.
308
         *
309
         * @since 2.0.0
310
         *
311
         * @return static
312
         * @throws ScripturNumException
313
         */
314
        public function getWholeChapters(): ScripturNum
315
        {
316
                if ($this->isWholeChapters()) {
33 ✔
317
                        return $this;
11 ✔
318
                }
319

320
                $i = static::refNumsToInt($this->book, $this->startCh, null, $this->endCh, null);
22 ✔
321
                return new static($i);
22 ✔
322
        }
323

324
        /**
325
         * Returns a ScripturNum for the chapter after the current highest chapter.
326
         *
327
         * @since 2.0.0
328
         *
329
         * @return static
330
         * @throws ScripturNumException
331
         */
332
        public function getNextChapter(): ScripturNum
333
        {
334
                $v = Bible::getVerseCounts();
55 ✔
335
                if (! isset($v[$this->book]) && $this->endCh === count($v[$this->book - 1])) {
55 ✔
336
                        throw new ScripturNumException("There are no more chapters in the Bible.");
11 ✔
337
                }
338
                if (! isset($v[$this->book - 1][$this->endCh])) {
44 ✔
339
                        $i = static::refNumsToInt($this->book + 1, 1, null, 1, null);
22 ✔
340
                } else {
341
                        $ch = $this->endCh + 1;
22 ✔
342
                        $i = static::refNumsToInt($this->book, $ch, null, $ch, null);
22 ✔
343
                }
344
                return new static($i);
44 ✔
345
        }
346

347
        /**
348
         * Returns a ScripturNum for the chapter prior to the current lowest chapter.
349
         *
350
         * @since 2.0.0
351
         *
352
         * @return static
353
         * @throws ScripturNumException
354
         */
355
        public function getPrevChapter(): ScripturNum
356
        {
357
                if ($this->startCh === 1 && $this->book === 1) {
55 ✔
358
                        throw new ScripturNumException("There are no more chapters in the Bible.");
11 ✔
359
                }
360
                $v = Bible::getVerseCounts();
44 ✔
361
                if ($this->startCh === 1) {
44 ✔
362
                        $ch = count($v[$this->book - 2]);
22 ✔
363
                        $i = static::refNumsToInt($this->book - 1, $ch, null, $ch, null);
22 ✔
364
                } else {
365
                        $ch = $this->startCh - 1;
22 ✔
366
                        $i = static::refNumsToInt($this->book, $ch, null, $ch, null);
22 ✔
367
                }
368
                return new static($i);
44 ✔
369
        }
370

371
        /**
372
         * Returns true if the passage is a whole book.
373
         *
374
         * @return bool
375
         */
376
        public function isWholeBook(): bool
377
        {
378
                $v = Bible::getVerseCounts();
737 ✔
379

380
                return ($this->startCh === 1
737 ✔
381
                        && $this->startV === 1
737 ✔
382
                        && $this->endCh === count($v[$this->book - 1])
737 ✔
383
                        && $this->endV === $v[$this->book - 1][$this->endCh - 1]);
737 ✔
384
        }
385

386

387
        /**
388
         * Returns true if the passage is just a single verse.
389
         *
390
         * @since 2.0.0
391
         *
392
         * @return bool
393
         */
394
        public function isSingleVerse(): bool
395
        {
396
                return ($this->startCh === $this->endCh
99 ✔
397
                        && $this->startV === $this->endV);
99 ✔
398
        }
399

400

401
        /**
402
         * Returns true if the book only has one chapter (e.g. Jude)
403
         *
404
         * @return bool
405
         */
406
        public function bookHasSingleChapter(): bool
407
        {
408
                return Bible::bookHasSingleChapter($this->book - 1);
341 ✔
409
        }
410

411

412
        /**
413
         * @param $bookName
414
         *
415
         * @return int
416
         * @throws ScripturNumException
417
         */
418
        protected static function bookNameToBookNum($bookName): int
419
        {
420
                $books = static::getBookNames();
1,727 ✔
421
                foreach ($books as $book => $bookNames) {
1,727 ✔
422
                        if (Bible::in_arrayi($bookName, $bookNames)) {
1,727 ✔
423
                                return $book + 1;
1,700 ✔
424
                        }
425
                }
426
                throw new ScripturNumException('Book name is invalid.');
33 ✔
427
        }
428

429
        /**
430
         * @return string[][]
431
         *
432
         * @since 2.0.0
433
         *
434
         * @see Bible::getBookNames()
435
         */
436
        protected static function getBookNames(): array
437
        {
438
                return call_user_func([static::$bibleClass, 'getBookNames']);
1,848 ✔
439
        }
440

441
        /**
442
         * @return string[]
443
         *
444
         * @since 2.0.0
445
         *
446
         * @see Bible::getCommonTerms()
447
         */
448
        protected static function getCommonTerms(): array
449
        {
450
                return call_user_func([static::$bibleClass, 'getCommonTerms']);
165 ✔
451
        }
452

453
        /**
454
         * @return string[]
455
         *
456
         * @since 2.0.0
457
         *
458
         * @see Bible::getConjunctions()
459
         */
460
        protected static function getConjunctions(): array
461
        {
462
                return call_user_func([static::$bibleClass, 'getConjunctions']);
242 ✔
463
        }
464

465
        /**
466
         * @param string $bookNameSingular
467
         *
468
         * @return string
469
         *
470
         * @since 2.0.0
471
         *
472
         * @see Bible::pluralizeBookName()
473
         */
474
        protected static function pluralizeBookName(string $bookNameSingular): string
475
        {
476
                return call_user_func([static::$bibleClass, 'pluralizeBookName'], $bookNameSingular);
275 ✔
477
        }
478

479
        /**
480
         * @param string $bookStr
481
         * @param ?int   $startCh
482
         * @param ?int   $startV
483
         * @param ?int   $endCh
484
         * @param ?int   $endV
485
         *
486
         * @return ScripturNum
487
         * @throws ScripturNumException
488
         */
489
        public static function newFromParsed(string $bookStr,
490
                $startCh = null, $startV = 1,
491
                $endCh = null, $endV = null): ScripturNum
492
        {
493
                $book = self::bookNameToBookNum($bookStr);
11 ✔
494
                self::validateRefNums($book, $startCh, $startV, $endCh, $endV);
11 ✔
495
                $int  = self::refNumsToInt($book, $startCh, $startV, $endCh, $endV);
11 ✔
496
                $c    = static::class;
11 ✔
497

498
                return new $c($int);
11 ✔
499
        }
500

501

502
        /**
503
         * @param int      $book The book of the Bible the range is within. 1-rel.
504
         * @param int      $startCh The chapter of the start of the range. 1-rel.
505
         * @param int|null $startV The verse of the start of the range.  1-rel. Defaults to 1.
506
         * @param int|null $endCh The end chapter of the range.  If null or not provided, assumed to be the same as the
507
         *     start chapter.
508
         * @param int|null $endV The end verse of the range.  If null or not provided, assumed to be the end of the
509
         *     chapter.
510
         *
511
         * @return ScripturNum The ScripturNum object that represents this range of scripture.
512
         *
513
         * @throws ScripturNumException A chapter was requested that does not exist within the requested book.
514
         * @throws ScripturNumException A verse was requested that does not exist within the requested range.
515
         */
516
        public static function newFromInts(int $book,
517
                int $startCh, $startV = null,
518
        $endCh = null, $endV = null): ScripturNum
519
        {
520
                self::validateRefNums($book, $startCh, $startV, $endCh, $endV);
22 ✔
521
                $int = self::refNumsToInt($book, $startCh, $startV, $endCh, $endV);
22 ✔
522
                $c   = static::class;
22 ✔
523

524
                return new $c($int);
22 ✔
525
        }
526

527

528
        /**
529
         * Takes a string that represents a single passage and returns it as an int.
530
         *
531
         * @param string $string A human-readable scripture reference that should be converted to an int.
532
         *
533
         * @return int The int.
534
         *
535
         * @throws ScripturNumException
536
         */
537
        public static function stringToInt(string $string): int
538
        {
539
                // Standardize dashes
540
                $string = str_replace(['&ndash;', '–'], '-', $string);
1,496 ✔
541

542
                // Remove duplicate spaces (Can't remove all spaces because spaces may occur in book names like Song of Songs
543
                $string = preg_replace("/\s\s+/", ' ', $string);
1,496 ✔
544

545
                // Remove spaces among the numerical parts.
546
                $string = preg_replace("/(\d+)\s*([-.:])\s*(\d+)/i", '$1$2$3', $string);
1,496 ✔
547

548
                // Look for right-most space or alpha char.  This should separate book name from numerical ref.
549
                preg_match('/.*([a-zA-Z\s])/', $string, $matches, PREG_OFFSET_CAPTURE);
1,496 ✔
550
                $spaceIndex = (int)$matches[1][1] + 1;
1,496 ✔
551
                $book       = trim(substr($string, 0, $spaceIndex));
1,496 ✔
552
                $ref        = substr($string, $spaceIndex);
1,496 ✔
553

554
                // Parse numbers
555
                self::refNumStringToRefNums($ref, $startCh, $startV, $endCh, $endV);
1,496 ✔
556

557
                // Change book name to number
558
                $book = self::bookNameToBookNum($book);
1,496 ✔
559

560
                // Assemble and return the int
561
                self::validateRefNums($book, $startCh, $startV, $endCh, $endV);
1,496 ✔
562
                return self::refNumsToInt($book, $startCh, $startV, $endCh, $endV);
1,496 ✔
563
        }
564

565
        /**
566
         * Takes a reference string and returns a plurality of ints for corresponding passages.  This should only be passed
567
         * values that are known to be references, not just any text. Use self::extractFromString for that.
568
         *
569
         * @param string     $string A human-readable scripture reference that should be converted to one or more ints.
570
         * Allows for commas and semicolons.
571
         * @param bool|array $exceptions Pass an array to this parameter, and it will be populated with any exceptions that
572
         * occur.  By passing an array, this will not throw the exception, and by not throwing the exception, execution
573
         * continues and you may be apprised of multiple errors that may exist.
574
         *
575
         * @return int[] The ints.
576
         * @throws ScripturNumException
577
         * @see self::extractFromString();
578
         *
579
         * @since 2.0.0
580
         *
581
         */
582
        public static function stringToInts(string $string, &$exceptions = false): array
583
        {
584
                $cj = static::getConjunctions();
220 ✔
585

586
                // standardize punctuation bits
587
                $string = str_replace([$cj['and'], '&', $cj['chapter'], $cj['through'], '&endash;', '–'],
220 ✔
588
                                      [',', ',', '', '-', '-', '-'],
220 ✔
589
                                      $string);
220 ✔
590

591
                // Remove duplicate spaces (Can't remove all spaces because spaces may occur in book names like Song of Songs
592
                $string = preg_replace("/\s\s+/", ' ', $string);
220 ✔
593

594
                // Look for right-most alpha char.  This should separate book name from numerical ref.
595
                preg_match('/.*([a-zA-Z])/', $string, $matches, PREG_OFFSET_CAPTURE);
220 ✔
596
                $spaceIndex = (int)$matches[1][1] + 1; // Space index may not actually be a space.
220 ✔
597
                $book       = trim(substr($string, 0, $spaceIndex));
220 ✔
598
                $ref        = substr($string, $spaceIndex);
220 ✔
599

600
                // Change book name to number
601
                try {
602
                        $book = self::bookNameToBookNum($book);
220 ✔
603
                } catch (ScripturNumException $e) {
22 ✔
604
                        if (is_array($exceptions)) {
22 ✔
605
                                $exceptions[] = $e;
11 ✔
606
                        } else {
607
                                throw $e;
11 ✔
608
                        }
609
                        return [];
11 ✔
610
                }
611

612
                $ints = [];
198 ✔
613

614
                // Remove all spaces from reference.
615
                $ref = preg_replace("/\s*/", '', $ref);
198 ✔
616

617
                $levelsOfCommas = (2 * (strpos($ref, ';') > -1) + (strpos($ref, ',') > -1));
198 ✔
618
                foreach (explode(";", $ref) as $sA) {
198 ✔
619
                        if ($levelsOfCommas > 1) {
198 ✔
620
                                unset($startCh, $startV, $endCh, $endV);
22 ✔
621
                        }
622
                        foreach (explode(",", $sA) as $s) {
198 ✔
623
                                $s = trim($s);
198 ✔
624
                                if ($s === "" && isset($endCh))
198 ✔
625
                                        continue;
11 ✔
626
                                try {
627
                                        // Parse numbers
628
                                        self::refNumStringToRefNums($s, $startCh, $startV, $endCh, $endV, true);
198 ✔
629

630
                                        // Assemble and return the int
631
                                        self::validateRefNums($book, $startCh, $startV, $endCh, $endV);
198 ✔
632
                                        $ints[] = self::refNumsToInt($book, $startCh, $startV, $endCh, $endV);
176 ✔
633

634
                                } catch (ScripturNumException $e) {
44 ✔
635
                                        if (is_array($exceptions)) {
44 ✔
636
                                                $exceptions[] = $e;
22 ✔
637
                                        } else {
638
                                                throw $e;
22 ✔
639
                                        }
640
                                        continue;
52 ✔
641
                                }
642
                        }
643
                }
644

645
                return $ints;
187 ✔
646
        }
647

648
        /**
649
         * Validate that reference numbers can be matched to verses that exist.
650
         *
651
         * @since 2.0.0  Previously, this functionality was handled within the refNumsToInt function.
652
         *
653
         * @param int  $book
654
         * @param ?int $startCh
655
         * @param ?int $startV
656
         * @param ?int $endCh
657
         * @param ?int $endV
658
         *
659
         * @return void
660
         * @throws ScripturNumException
661
         */
662
        protected static function validateRefNums(int $book, &$startCh, &$startV, &$endCh, &$endV)
663
        {
664
                $book--;
1,749 ✔
665
                if ($startCh > count(Bible::getVerseCounts()[$book]) ||
1,749 ✔
666
            $endCh > count(Bible::getVerseCounts()[$book]
1,749 ✔
667
                        )) { // invalid request OR request for a single-chapter book.
1,272 ✔
668
                        if (Bible::bookHasSingleChapter($book) && $startV === null && $endV === null) { // single-chapter book.
132 ✔
669
                                $startV  = $startCh;
99 ✔
670
                                $endV    = $endCh;
99 ✔
671
                                $startCh = 1;
99 ✔
672
                                $endCh   = 1;
99 ✔
673
                        } else {
674
                                throw new ScripturNumException("A chapter was requested that does not exist within the requested book.");
33 ✔
675
                        }
676
                }
677
                if ($startCh === null && $endCh === null) { // whole book
1,716 ✔
678
                        $startCh = 1;
33 ✔
679
                        $endCh   = count(Bible::getVerseCounts()[$book]);
33 ✔
680
                }
681
                if (($startV - 1) > Bible::getVerseCounts()[$book][$startCh - 1] || ($endV - 1) > Bible::getVerseCounts()[$book][$endCh - 1]) {
1,716 ✔
682
                        throw new ScripturNumException("A verse was requested that does not exist within the requested chapter.");
44 ✔
683
                }
684
        }
462 ✔
685

686
        /**
687
         * Take reference indexes and convert them to the ScripturNum int.  Assumes numbers are already validated by either
688
         * safely existing or being validated against self::validateRefNums()
689
         *
690
         * @param int $book
691
         * @param ?int $startCh
692
         * @param ?int $startV
693
         * @param ?int $endCh
694
         * @param ?int $endV
695
         *
696
         * @return int
697
         */
698
        protected static function refNumsToInt(int $book, $startCh, $startV, $endCh, $endV): int
699
        {
700
                $v = Bible::getVerseCounts();
1,727 ✔
701
                $book--;
1,727 ✔
702
                $int = ($book) << 24;
1,727 ✔
703
                if ($startCh === null && $endCh === null) { // whole book
1,727 ✔
704
                        $startCh = 1;
11 ✔
705
                        $endCh   = count($v[$book]);
11 ✔
706
                }
707
                if ($endCh === null) { // single chapter
1,727 ✔
708
                        $endCh = $startCh;
22 ✔
709
                }
710
                if ($startCh !== null) $startCh--;
1,727 ✔
711
                if ($startV !== null) $startV--;
1,727 ✔
712
                if ($endCh !== null) $endCh--;
1,727 ✔
713
                if ($endV !== null) $endV--;
1,727 ✔
714
                if ($endV === null) {
1,727 ✔
715
                        $endV = $v[$book][$endCh] - 1;
1,353 ✔
716
                }
717

718
                $ch = 0;
1,727 ✔
719
                while ($ch < ($startCh)) {
1,727 ✔
720
                        $startV += $v[$book][$ch];
1,309 ✔
721
                        $ch++;
1,309 ✔
722
                }
723
                $ch = 0;
1,727 ✔
724
                while ($ch < ($endCh)) {
1,727 ✔
725
                        $endV += $v[$book][$ch];
1,485 ✔
726
                        $ch++;
1,485 ✔
727
                }
728

729
                $int += ($startV << 12);
1,727 ✔
730
                $int += ($endV);
1,727 ✔
731

732
                return $int;
1,727 ✔
733
        }
734

735
        /**
736
         * This function reads through a single ref string (e.g. 3:5-6:9) one character at a time to parse it into a known
737
         * reference.
738
         *
739
         * @param string $string The string to parse.
740
         * @param        $chapterStart
741
         * @param        $verseStart
742
         * @param        $chapterEnd
743
         * @param        $verseEnd
744
         * @param bool   $useHints If true, will consider the values provided to the chapter and verse parameters in
745
         *     parsing the string.  Default false.
746
         *
747
         * @throws ScripturNumException
748
         */
749
        protected static function refNumStringToRefNums(string $string, &$chapterStart = null, &$verseStart = null, &$chapterEnd = null, &$verseEnd = null, bool $useHints = false)
750
        {
751
                if (preg_match('/[a-zA-Z]/', $string)) {
1,771 ✔
752
                        throw new ScripturNumException("Parse Ref only handles the numerical part of the reference.  Alphabetical characters are not permitted.");
11 ✔
753
                }
754

755
                $startNums     = [];
1,760 ✔
756
                $endNums       = [];
1,760 ✔
757
                $currentNumber = '';
1,760 ✔
758
                $beforeHyphen  = true;
1,760 ✔
759
                $useHints      = $useHints && !!$chapterEnd;
1,760 ✔
760

761
                // adding the extra character allows the last digit to actually get parsed.
762
                foreach (str_split($string . ' ') as $char) {
1,760 ✔
763
                        if (is_numeric($char)) {
1,760 ✔
764
                                // still finding the full number
765
                                $currentNumber .= $char;
1,716 ✔
766
                        } else {
767
                                // End of number.  Int-ify and assign to appropriate half.
768
                                $currentNumber = (int)$currentNumber;
1,760 ✔
769
                                if ($currentNumber === 0) {
1,760 ✔
770
                                        continue;
44 ✔
771
                                }
772
                                if ($beforeHyphen) {
1,716 ✔
773
                                        $startNums[] = $currentNumber;
1,716 ✔
774
                                } else {
775
                                        $endNums[] = $currentNumber;
1,001 ✔
776
                                }
777
                                $currentNumber = ''; // reset for next number.
1,716 ✔
778

779
                                if ($char == '-') {
1,716 ✔
780
                                        $beforeHyphen = false;
1,131 ✔
781
                                }
782
                        }
783
                }
784

785
                $numInx = count($startNums) * 10 + count($endNums) + ($useHints ? 100 : 0);
1,760 ✔
786
                switch ($numInx) {
160 ✔
787
                        case 0: // whole book
1,760 ✔
788
                                $chapterStart = null;
44 ✔
789
                                $chapterEnd = null;
44 ✔
790
                                $verseStart = null;
44 ✔
791
                                $verseEnd = null;
44 ✔
792
                                break;
44 ✔
793
                        case 10: // one full chapter
1,716 ✔
794
                                $chapterStart = $startNums[0];
968 ✔
795
                                $chapterEnd   = $chapterStart;
968 ✔
796
                                break;
968 ✔
797
                        case 110: // One verse, same chapter as previous (Probably?)
1,364 ✔
798
                                if (!$verseStart && !$verseEnd) { // Previous indicator was chapters only.  This should be, too.
44 ✔
799
                                        $chapterStart = $startNums[0];
33 ✔
800
                                        $chapterEnd = $chapterStart;
33 ✔
801
                                } else { // Previous indicator had verses; this should be verses, too.
802
                                        $chapterStart = $chapterEnd;
11 ✔
803
                                        $verseStart   = $startNums[0];
11 ✔
804
                                        $verseEnd     = $verseStart;
11 ✔
805
                                }
806
                                break;
44 ✔
807
                        case 11: // multiple full chapters
1,342 ✔
808
                                $chapterStart = $startNums[0];
627 ✔
809
                                $chapterEnd   = $endNums[0];
627 ✔
810
                                break;
627 ✔
811
                        case 111: // multiple verses from previous chapter (probably?)
737 ✔
812
                                if (!$verseStart && !$verseEnd) { // Previous indicator was chapters only.  This should be, too.
33 ✔
813
                                        $chapterStart = $startNums[0];
11 ✔
814
                                        $chapterEnd = $endNums[0];
11 ✔
815
                                        $verseStart = null;
11 ✔
816
                                        $verseEnd = null;
11 ✔
817
                                } else { // Previous indicator had verses; this should be verses, too.
818
                                        $chapterStart = $chapterEnd;
22 ✔
819
                                        $verseStart   = $startNums[0];
22 ✔
820
                                        $verseEnd     = $endNums[0];
22 ✔
821
                                }
822
                                break;
33 ✔
823
                        case 12: // full chapter to part of chapter
726 ✔
824
                        case 112:
704 ✔
825
                                $chapterStart = $startNums[0];
22 ✔
826
                                $verseStart   = null;
22 ✔
827
                                $chapterEnd   = $endNums[0];
22 ✔
828
                                $verseEnd     = $endNums[1];
22 ✔
829
                                break;
22 ✔
830
                        case 20: // one verse.  This is the weird case.
704 ✔
831
                        case 120:
352 ✔
832
                                $chapterStart = $startNums[0];
374 ✔
833
                                $verseStart   = $startNums[1];
374 ✔
834
                                $chapterEnd   = $chapterStart;
374 ✔
835
                                $verseEnd     = $verseStart;
374 ✔
836
                                break;
374 ✔
837
                        case 21: // multiple verses from one chapter.
352 ✔
838
                        case 121:
110 ✔
839
                                $chapterStart = $startNums[0];
242 ✔
840
                                $chapterEnd   = $chapterStart;
242 ✔
841
                                $verseStart   = $startNums[1];
242 ✔
842
                                $verseEnd     = $endNums[0];
242 ✔
843
                                break;
242 ✔
844
                        case 22: // multiple verses from across chapters
110 ✔
845
                        case 122:
22 ✔
846
                                $chapterStart = $startNums[0];
99 ✔
847
                                $verseStart   = $startNums[1];
99 ✔
848
                                $chapterEnd   = $endNums[0];
99 ✔
849
                                $verseEnd     = $endNums[1];
99 ✔
850
                                break;
99 ✔
851
                        default:
852
                                throw new ScripturNumException("Badly formed numerical reference.");
11 ✔
853
                }
854
        }
477 ✔
855

856
        /**
857
         * Converts a ScripturNum int into reference numbers.
858
         *
859
         * @param int $int The ScripturNum integer
860
         * @param int $book The book number
861
         * @param int $chapterStart The first Chapter
862
         * @param int $verseStart The first Verse
863
         * @param int $chapterEnd The last Chapter
864
         * @param int $verseEnd The last Verse
865
         *
866
         * @throws ScripturNumException If the reference is unintelligible.
867
         */
868
        protected static function intToRefNums(int $int, &$book, &$chapterStart, &$verseStart, &$chapterEnd, &$verseEnd)
869
        {
870
                $book = $int >> 24;
1,892 ✔
871
                $int  -= ($book << 24);
1,892 ✔
872

873
                $refAIndex = $int >> 12;
1,892 ✔
874
                $int       -= ($refAIndex << 12);
1,892 ✔
875

876
                $refBIndex = &$int;
1,892 ✔
877

878
                if ($refBIndex < $refAIndex) {
1,892 ✔
879
                        throw new ScripturNumException('Unintelligible Reference');
11 ✔
880
                }
881

882
                self::bkIndexToSingleRef($book, $refAIndex, $chapterStart, $verseStart);
1,881 ✔
883
                self::bkIndexToSingleRef($book, $refBIndex, $chapterEnd, $verseEnd);
1,870 ✔
884

885
                $book++;
1,859 ✔
886
        }
507 ✔
887

888

889
        /**
890
         * Convert a ScrupturNum int into a concatenated number.  (Concatenated numbers are often used for text libraries.)
891
         *
892
         * @param int        $int The int representing the full passage
893
         * @param string|int $concatStart The concatenated "number" possibly larger than an int representing the start of
894
         *     the passage.
895
         * @param string|int $concatEnd The concatenated "number" possibly larger than an int representing the end of the
896
         *     passage.
897
         *
898
         * @throws ScripturNumException If the reference is unintelligible.
899
         */
900
        public static function intToConcats(int $int, &$concatStart, &$concatEnd)
901
        {
902
                $p = [0, 0, 0, 0, 0];
11 ✔
903
                self::intToRefNums($int, $p[0], $p[1], $p[2], $p[3], $p[4]);
11 ✔
904
                $concatStart = $p[2] + ($p[1] * 1000) + ($p[0] * 1000000);
11 ✔
905
                $concatEnd   = $p[4] + ($p[3] * 1000) + ($p[0] * 1000000);
11 ✔
906
        }
3 ✔
907

908
        /**
909
         * Parse a book index number into a chapter and verse.
910
         *
911
         * @param int $book Book number
912
         * @param int $index Verse Index Number
913
         * @param int $chapter Chapter
914
         * @param int $verse Verse
915
         *
916
         * @throws ScripturNumException
917
         */
918
        protected static function bkIndexToSingleRef($book, $index, &$chapter, &$verse)
919
        {
920
                $index++;
1,892 ✔
921
                $v       = Bible::getVerseCounts();
1,892 ✔
922
                $chapter = 0;
1,892 ✔
923
                if ( ! isset($v[$book])) {
1,892 ✔
924
                        throw new ScripturNumException("There are not that many books in the Bible.");
11 ✔
925
                }
926
                while ($index > $v[$book][$chapter]) {
1,881 ✔
927
                        $index -= $v[$book][$chapter];
1,661 ✔
928
                        if ( ! isset($v[$book][++$chapter])) {
1,661 ✔
929
                                throw new ScripturNumException("There are not that many verses in this book.");
11 ✔
930
                        }
931
                }
932
                $chapter++;
1,881 ✔
933
                $verse = $index;
1,881 ✔
934
        }
513 ✔
935

936
        /**
937
         * Given a string with any kind of text content, this method will search for any human-readable scripture references
938
         * and try to parse them into discrete passages.  Returns a ScripturNumArray.
939
         *
940
         * @since 2.0.0
941
         *
942
         * @param string $string
943
         * @param bool   $excludeAllBookOnlyRefs
944
         * @param null   $exceptions
945
         *
946
         * @return ScripturNumArray
947
         */
948
        public static function extractFromString(string $string, bool $excludeAllBookOnlyRefs = false, &$exceptions = null): ScripturNumArray
949
        {
950
                $results = new ScripturNumArray();
176 ✔
951

952
                $allBookNames = self::getBookNames();
176 ✔
953
                $allBookNames = array_merge(...$allBookNames);
176 ✔
954

955
                $cj = static::getConjunctions();
176 ✔
956

957
                $string = str_replace([$cj['and'], $cj['through'], $cj['chapter']], [',', '-', ''], $string);
176 ✔
958

959
                if ($excludeAllBookOnlyRefs) {
176 ✔
960
                        $regExSets = [
8 ✔
961
                                [
8 ✔
962
                                        'bs' => $allBookNames,
11 ✔
963
                                        'ps' => '+'
11 ✔
964
                                ]
8 ✔
965
                        ];
8 ✔
966
                } else {
967
                        $b2 = self::getCommonTerms();
165 ✔
968
                        $b1 = array_diff($allBookNames, $b2);
165 ✔
969
                        $regExSets = [
120 ✔
970
                                [
120 ✔
971
                                        'bs' => $b1,
165 ✔
972
                                        'ps' => '*'
165 ✔
973
                                ],
120 ✔
974
                                [
120 ✔
975
                                        'bs' => $b2,
165 ✔
976
                                        'ps' => '+'
165 ✔
977
                                ]
120 ✔
978
                        ];
120 ✔
979
                        unset($b1, $b2);
165 ✔
980
                }
981
                unset($allBookNames);
176 ✔
982

983
        $combinedMatches = [];
176 ✔
984
                foreach ($regExSets as $re) {
176 ✔
985
            $b = implode("|", $re['bs']);
176 ✔
986
            $plusOrStar = $re['ps'];
176 ✔
987
            /** @noinspection RegExpUnnecessaryNonCapturingGroup -- They really are necessary. */
988
            $pattern = "/\b(?:$b)\.?(?:[-\s,;&]*1?\d{1,2}:?(?:1?\d{1,2})?)$plusOrStar\b/i";
176 ✔
989

990
            preg_match_all($pattern, $string, $matches, PREG_OFFSET_CAPTURE);
176 ✔
991

992
            $combinedMatches = array_merge($combinedMatches, $matches[0]);
176 ✔
993
        }
994

995
        // Sort matches by position in string
996
        usort($combinedMatches, function($a, $b) {
128 ✔
997
            return $a[1] - $b[1];
66 ✔
998
        });
176 ✔
999

1000
        $lastBook = -1;
176 ✔
1001
        $lastEnd = -1;
176 ✔
1002
        foreach ($combinedMatches as $m) {
176 ✔
1003
            try {
1004
                $ints = static::stringToInts($m[0], $exceptions);
154 ✔
1005
            } catch (ScripturNumException $e) {
11 ✔
1006
                continue;
11 ✔
1007
            }
1008
            foreach($ints as $i) {
154 ✔
1009
                try {
1010
                    $sn = new static($i);
154 ✔
NEW
1011
                } catch (ScripturNumException $e) {
×
NEW
1012
                    continue;
×
1013
                }
1014
                if ($lastBook != $sn->book && $lastEnd > $m[1]) {
154 ✔
1015
                    // See issue #14
1016
                    continue;
11 ✔
1017
                }
1018
                $results[] = $sn;
154 ✔
1019
                $lastBook = $sn->book;
154 ✔
1020
            }
1021
            $lastEnd = $m[1] + strlen($m[0]);
154 ✔
1022
        }
1023

1024
                return $results;
176 ✔
1025
        }
1026

1027
        /**
1028
         * Test whether a given passage is within a given larger passage.  Will also return true if they are the same.
1029
         *
1030
         * @since 2.0.0
1031
         *
1032
         * @param int $largerPassage
1033
         *
1034
         * @return bool
1035
         */
1036
        public function isWithinInt(int $largerPassage): bool
1037
        {
1038
                if (($this->int & self::BOOK_MASK) != ($largerPassage & self::BOOK_MASK))
154 ✔
1039
                        return false;
22 ✔
1040

1041
                if (($this->int & self::START_MASK) < ($largerPassage & self::START_MASK))
132 ✔
1042
                        return false;
22 ✔
1043

1044
                if (($this->int & self::END_MASK) > ($largerPassage & self::END_MASK))
110 ✔
1045
                        return false;
22 ✔
1046

1047
                return true;
88 ✔
1048
        }
1049

1050
        /**
1051
         * Test whether a given passage is within a given larger passage.  Will also return true if they are the same.
1052
         *
1053
         * @since 2.0.0
1054
         *
1055
         * @param ScripturNum $largerPassage
1056
         *
1057
         * @return bool
1058
         */
1059
        public function isWithin(ScripturNum $largerPassage): bool
1060
        {
1061
                return $this->isWithinInt($largerPassage->getInt());
77 ✔
1062
        }
1063

1064
        /**
1065
         * Test whether a given passage has any commonality with another passage.
1066
         *
1067
         * @since 2.0.0
1068
         *
1069
         * @param int $otherPassage
1070
         *
1071
         * @return bool
1072
         */
1073
        public function overlapsWithInt(int $otherPassage): bool
1074
        {
1075
                if (($this->int & self::BOOK_MASK) != ($otherPassage & self::BOOK_MASK))
165 ✔
1076
                        return false;
33 ✔
1077

1078
                if (($this->int & self::START_MASK) > (($otherPassage & self::END_MASK) << 12))
132 ✔
1079
                        return false;
22 ✔
1080

1081
                if (($this->int & self::END_MASK) < (($otherPassage & self::START_MASK) >> 12))
110 ✔
1082
                        return false;
22 ✔
1083

1084
                return true;
88 ✔
1085
        }
1086

1087
        /**
1088
         * Test whether a given passage has any commonality with another passage.
1089
         *
1090
         * @since 2.0.0
1091
         *
1092
         * @param ScripturNum $otherPassage
1093
         *
1094
         * @return bool
1095
         */
1096
        public function overlapsWith(ScripturNum $otherPassage): bool
1097
        {
1098
                return $this->overlapsWithInt($otherPassage->getInt());
88 ✔
1099
        }
1100

1101
        /**
1102
         * Test whether a given passage has any commonality with another passage, or is adjacent to it.
1103
         *
1104
         * @since 2.0.0
1105
         *
1106
         * @param int $otherPassage
1107
         *
1108
         * @return bool
1109
         */
1110
        public function overlapsOrAdjacentInt(int $otherPassage): bool
1111
        {
1112
                if (($this->int & self::BOOK_MASK) != ($otherPassage & self::BOOK_MASK))
473 ✔
1113
                        return false;
132 ✔
1114

1115
                if (($this->int & self::START_MASK) - (1 << 12) > (($otherPassage & self::END_MASK) << 12))
374 ✔
1116
                        return false;
44 ✔
1117

1118
                if ((($this->int & self::END_MASK) + 1) < (($otherPassage & self::START_MASK) >> 12))
330 ✔
1119
                        return false;
154 ✔
1120

1121
                return true;
198 ✔
1122
        }
1123

1124
        /**
1125
         * Test whether a given passage has any commonality with another passage, or is adjacent to it.
1126
         *
1127
         * @since 2.0.0
1128
         *
1129
         * @param ScripturNum $otherPassage
1130
         *
1131
         * @return bool
1132
         */
1133
        public function overlapsOrAdjacent(ScripturNum $otherPassage): bool
1134
        {
1135
                return $this->overlapsOrAdjacentInt($otherPassage->getInt());
297 ✔
1136
        }
1137

1138
        /**
1139
         * Combines two adjacent or overlapping passages into one int.
1140
         *
1141
         * @since 2.0.0
1142
         *
1143
         * @param int $otherPassage
1144
         *
1145
         * @return int
1146
         * @throws ScripturNumException
1147
         *@since 2.0.0
1148
         */
1149
        public function combineWithInt(int $otherPassage): int
1150
        {
1151
                if (!$this->overlapsOrAdjacentInt($otherPassage)) {
110 ✔
1152
                        throw new ScripturNumException("Cannot combine passages that aren't overlapping or adjacent.");
22 ✔
1153
                }
1154

1155
                $newInt = $this->int & self::BOOK_MASK;
88 ✔
1156
                $newInt += min($this->int & self::START_MASK, $otherPassage & self::START_MASK);
88 ✔
1157
                $newInt += max($this->int & self::END_MASK, $otherPassage & self::END_MASK);
88 ✔
1158

1159
                return $newInt;
88 ✔
1160
        }
1161

1162
        /**
1163
         * Combines two adjacent or overlapping passages into one ScripturNum.
1164
         *
1165
         * @since 2.0.0
1166
         *
1167
         * @param ScripturNum $otherPassage
1168
         *
1169
         * @return ScripturNum
1170
         * @throws ScripturNumException
1171
         */
1172
        public function combineWith(ScripturNum $otherPassage): ScripturNum
1173
        {
1174
                $int = $this->combineWithInt($otherPassage->getInt());
77 ✔
1175
                return new ScripturNum($int);
66 ✔
1176
        }
1177

1178
        /**
1179
         * Test whether a given passage contains a given smaller passage.  Will also return true if they are the same.
1180
         *
1181
         * @param int $smallerPassage
1182
         *
1183
         * @return bool
1184
         */
1185
        public function containsInt(int $smallerPassage): bool
1186
        {
1187
                if (($this->int & self::BOOK_MASK) != ($smallerPassage & self::BOOK_MASK))
132 ✔
1188
                        return false;
22 ✔
1189

1190
                if (($this->int & self::START_MASK) > ($smallerPassage & self::START_MASK))
110 ✔
1191
                        return false;
66 ✔
1192

1193
                if (($this->int & self::END_MASK) < ($smallerPassage & self::END_MASK))
44 ✔
1194
                        return false;
22 ✔
1195

1196
                return true;
22 ✔
1197
        }
1198

1199
        /**
1200
         * Test whether a given passage contains a given smaller passage.  Will also return true if they are the same.
1201
         *
1202
         * @param ScripturNum $smallerPassage
1203
         *
1204
         * @return bool
1205
         */
1206
        public function contains(ScripturNum $smallerPassage): bool
1207
        {
1208
                return $this->containsInt($smallerPassage->getInt());
66 ✔
1209
        }
1210

1211
        /**
1212
         * Generate a query statement that can be used to search an int column in generic SQL for a passage that is
1213
         * entirely contained within the given ScripturNum.
1214
         *
1215
         * @param string $columnRef  The name of the column or value to use in the query.
1216
         *
1217
         * @return string
1218
         */
1219
        public function toSqlExclusive(string $columnRef): string
1220
        {
1221
                if ($this->isSingleVerse()) {
99 ✔
1222
                        $i = $this->getInt();
22 ✔
1223
                        return "$columnRef = $i";
22 ✔
1224
                }
1225

1226
                $r = [];
77 ✔
1227

1228
                $mask = self::BOOK_MASK;
77 ✔
1229
                $value = $this->int & self::BOOK_MASK;
77 ✔
1230
                $r[] = "($columnRef & $mask) = $value";
77 ✔
1231

1232
                $mask = self::START_MASK;
77 ✔
1233
                $value = ($this->int & self::START_MASK);
77 ✔
1234
                $r[] = "($columnRef & $mask) >= $value";
77 ✔
1235

1236
                $mask = self::END_MASK;
77 ✔
1237
                $value = ($this->int & self::END_MASK);
77 ✔
1238
                $r[] = "($columnRef & $mask) <= $value";
77 ✔
1239

1240
                return "( " . implode(" AND ", $r) . " )";
77 ✔
1241
        }
1242

1243
        /**
1244
         * Generate a query statement that can be used to search an int column in generic SQL for a passage that overlaps
1245
         * with the given ScripturNum.
1246
         *
1247
         * @param string $columnRef  The name of the column or value to use in the query.
1248
         *
1249
         * @return string
1250
         */
1251
        public function toSqlInclusive(string $columnRef): string
1252
        {
1253
                $r = [];
99 ✔
1254

1255
                $mask = self::BOOK_MASK;
99 ✔
1256
                $value = $this->int & self::BOOK_MASK;
99 ✔
1257
                $r[] = "($columnRef & $mask) = $value";
99 ✔
1258

1259
                $mask = self::START_MASK;
99 ✔
1260
                $value = ($this->int & self::END_MASK) << 12;
99 ✔
1261
                $r[] = "($columnRef & $mask) <= $value";
99 ✔
1262

1263
                $mask = self::END_MASK;
99 ✔
1264
                $value = ($this->int & self::START_MASK) >> 12;
99 ✔
1265
                $r[] = "($columnRef & $mask) >= $value";
99 ✔
1266

1267
                return "( " . implode(" AND ", $r) . " )";
99 ✔
1268
        }
1269
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc