, (*1)
koinos
A PHP library and composer package for working with:, (*2)
- Biblical references in texts and databases, and
- Classical and Koine Greek text in Unicode.
Κοινός means 'common' in Ancient Greek. This package contains a number of
reusable services and utilities from the Hexapla project., (*3)
Installation
You will need PHP >=5.4 and phpunit >=4.0., (*4)
In composer.json:, (*5)
"require": {
"eukras/koinos": "~1.1"
}
Tests
The tests are the best documentation for developers., (*6)
composer dump-autoload # updates /vendor for autoloading; see .gitignore
phpunit -c tests
Library data files
./src/Resources/library/lxx/books.csv
./src/Resources/library/nt/books.csv
In each file the CSV lines specify id, library-name, name, short-name, handle,
reference-depth, aliases, total-chapters., (*7)
- Handles are the short names used in URIs, e.g.
Romans 1 has handle rom+1.
- Normal books like 1 Corinthians are reference depth 2 (chapter:verse), but
single-chapter books like Philemon are identified by verse only, so have a
reference depth of 1.
...
107,NT,1 Corinthians,1 Cor,1cor,2,1co,16
...
118,NT,Philemon,Phm,phm,1,phl/philem,1
...
Service\ReferenceManager and Utility\Reference
The ReferenceManager is use to construct, manipulate and format References., (*8)
./src/Utility/Reference.php
./src/Service/ReferenceManager.php
./tests/Utility/Reference.php
./tests/Service/ReferenceManager.php
Examples
Initialise the ReferenceManager as follows:, (*9)
use Koinos\Service\ReferenceManager;
$rm = new ReferenceManager($libraries=['nt', 'lxx']);
Libraries are easy to create: just add Resources/library/$library/books.csv
(see above), and pass $library as a constructor argument., (*10)
The ReferenceManager handles most Reference operations., (*11)
$mattId = $rm->matchBookName('matt'); // returns (int)101, say.
$matt28 = $rm->createReferenceFromBookAndChapter($mattId, 28);
$matt28 is now a Reference object., (*12)
$mark1 = $rm->getNextChapterReference($matt28);
echo $rm->getShortTitle($matt28); // Matt 28
echo $rm->getHandle($matt28); // matt+28
You will normally want to work with complex human-readable reference strings:, (*13)
$ref1 = $rm->createReferenceFromQuery('1 Cor 16:1-5,8,10-12,13-14');
$ref2 = $rm->createReferenceFromQuery('1cor+16.1-4,5,8,10-14');
In this example $ref1 generates identically to $ref2. Adjacent verse ranges
are combined together into a standard reference. Koinos does not know or care
how many verses are in a given chapter, though; internally, full-chapter links
are treated as Book 1:1-999., (*14)
echo $rm->getTitle($ref1); // 1 Corinthians 16:1-5,8,10-14
echo $rm->getTitle($ref2); // 1 Corinthians 16:1-5,8,10-14
$ref1->equals($ref2); // true
$ref1->contains($ref2); // true
All this together allows HTML links to be generated easily, though in a
framework you would most likely plug $rm->getHandle($ref) into a route., (*15)
echo $rm->getLink($matt28, $uriPrefix='/r/');
Being able to generate links allows them to be auto-replaced in text:, (*16)
$linkedHtml = $rm->linkReferencesInHtml($textContainingReferences, '/r/');
The scanner for matching links in text will identify if they exist in
parallels, that is, if references are separated by a pipe character, e.g. "The
Psalms Ps 14 | Ps 53 are the same." See the test suite for examples., (*17)
Utility\Reference Internals
Internal working is done with quadruples of [book, section, chapter, verse];
In $ref1, 1cor is book #107, and has two-level referencing (c:v), so the
section number is always 1. Every quadruple becomes an UNSIGNED INT(12) for
SQL (see below)., (*18)
| Query |
Quadruple |
Index |
| 1 Cor 16:1 |
[107, 1, 16, 1] |
107001016001 |
This makes it easy to efficiently index and match references and ranges:, (*19)
echo $ref1->getSqlClause($columnName='reference');
// (reference BETWEEN 107001016001 AND 107001016005) OR
// (reference BETWEEN 107001016008 AND 107001016008) OR
// (reference BETWEEN 107001016010 AND 107001016014)
echo $ref1->getSqlRangeClause($startColumn='rangeBegins', $endColumn='rangeEnds');
// (rangeEnds >= 107001016001 AND rangeBegins <= 107001016005) OR
// (rangeEnds >= 107001016008 AND rangeBegins <= 107001016008) OR
// (rangeEnds >= 107001016010 AND rangeBegins <= 107001016014)
The only gotcha is that, in PHP, these numbers must be treated as a strings, or
cast to (double): 12 digits exceed PHP's integer length. References are
usually manipulated as quadruples though, which are regular integers 1-999., (*20)
Utility\Greek
The Greek utility performs simple manipulation and scanning of Greek text:, (*21)
./src/Utility/Greek.php
./tests/Utility/Greek.php
Initialise without arguments:, (*22)
use Koinos\Utility\Greek; // see composer.json
$g = new Greek;
This performs romanization, and offers some Unicode convenience wrappers., (*23)
echo $g->romanize('Ῥύγχος'); // Rhunchos
echo $g->romanize('Ἡσυχάζω'); // Hēsuchazō
echo $g->romanize('Αὑτοῖσι'); // Hautoisi
echo $g->length('ᾁ'); // 1
echo $g->lowercase('Α'); // α
echo $g->unicodeChr('1f0c'); // Ἄ
Here's how it would be used to scan the structure of Psalm 116 (LXX) -- in
Hexapla this is used to save texts word-by-word into a database., (*24)
$ps116 = "1 αλληλουια.
αἰνεῖτε τὸν κύριον πάντα τὰ ἔθνη. ἐπαινέσατε αὐτόν πάντες οἱ λαοί.
2 ὅτι ἐκραταιώθη τὸ ἔλεος αὐτοῦ [ἐφ’ ἡμᾶς] καὶ ἡ ἀλήθεια τοῦ κυρίου
μένει εἰς τὸν αἰῶνα.
Τί εἰς τέλος;";
For each word, let's grab its book/chapter/verse numbers (BCV), then
paragraph/sentence/word numbers (PSW), and then any leading or trailing
punctuation. (Psalms are book 227.), (*25)
$structure = $g->getStructure($ps116, 227, 116);
// b c v p s w prefix word suffix
[ [ 227, 116, 1, 1, 1, 1, '', 'αλληλουια', '.' ],
[ 227, 116, 1, 2, 1, 1, '', 'αἰνεῖτε', '' ],
[ 227, 116, 1, 2, 1, 2, '', 'τὸν', '' ],
[ 227, 116, 1, 2, 1, 3, '', 'κύριον', '' ],
[ 227, 116, 1, 2, 1, 4, '', 'πάντα', '' ],
[ 227, 116, 1, 2, 1, 5, '', 'τὰ', '' ],
[ 227, 116, 1, 2, 1, 6, '', 'ἔθνη', '.' ],
[ 227, 116, 1, 2, 2, 1, '', 'ἐπαινέσατε', '' ],
[ 227, 116, 1, 2, 2, 2, '', 'αὐτόν', '' ],
[ 227, 116, 1, 2, 2, 3, '', 'πάντες', '' ],
[ 227, 116, 1, 2, 2, 4, '', 'οἱ', '' ],
[ 227, 116, 1, 2, 2, 5, '', 'λαοί', '.' ],
[ 227, 116, 2, 3, 1, 1, '', 'ὅτι', '' ],
[ 227, 116, 2, 3, 1, 2, '', 'ἐκραταιώθη', '' ],
[ 227, 116, 2, 3, 1, 3, '', 'τὸ', '' ],
[ 227, 116, 2, 3, 1, 4, '', 'ἔλεος', '' ],
[ 227, 116, 2, 3, 1, 5, '', 'αὐτοῦ', '' ],
[ 227, 116, 2, 3, 1, 6, '[','ἐφ’', '' ],
[ 227, 116, 2, 3, 1, 7, '', 'ἡμᾶς', ']' ],
[ 227, 116, 2, 3, 1, 8, '', 'καὶ', '' ],
[ 227, 116, 2, 3, 1, 9, '', 'ἡ', '' ],
[ 227, 116, 2, 3, 1, 10, '', 'ἀλήθεια', '' ],
[ 227, 116, 2, 3, 1, 11, '', 'τοῦ', '' ],
[ 227, 116, 2, 3, 1, 12, '', 'κυρίου', '' ],
[ 227, 116, 2, 3, 1, 13, '', 'μένει', '' ],
[ 227, 116, 2, 3, 1, 14, '', 'εἰς', '' ],
[ 227, 116, 2, 3, 1, 15, '', 'τὸν', '' ],
[ 227, 116, 2, 3, 1, 16, '', 'αἰῶνα', '.' ],
[ 227, 116, 2, 4, 1, 1, '', 'Τί', '' ],
[ 227, 116, 2, 4, 1, 2, '', 'εἰς', '' ],
[ 227, 116, 2, 4, 1, 3, '', 'τέλος', ';' ] ];
See the class and Test suites for more., (*26)