JIN is data format which uses the structure of an INI file with JSON-like values.
It is a simple and informal language which is most suitable for confgiruation data and settings. It works best as a metaconfiguration or schema configuration of sorts, but can also just give rich type and object support to something as simple as a .env file.
In some ways it is similar to TOML (https://github.com/toml-lang/toml), but does not have a formal specification. Parsing rules can best be described as follows:
- File structure is that of an INI file
- key = value
- [section]
- ; comment
- Values are JSON-like with the following differences
- Escaped character, e.g. \n, \b, \t are not supported
- A single \ does not need to be escaped
$jin_parser = newDotink\Jin\Parser()
$jin_string = file_get_contents('config.jin');
$config_data = $jin_parser->parse($jin_string)->get();Calling get() on the collection will return the full parsed data as an associative array.
If you'd rather work directly with the collection you can leave off the get(). You can see more documentation about the collection at (https://github.com/adbario/php-dot-notation):
$config = $jin_parser->parse($jin_string);Using the collection directly will allow you to use the "dot notation" to reference and retrieve specific values, as well as to supply defaults if they don't exist:
$config->get('database.connections.default', [
'name' => 'website',
'host' => 'localhost',
'user' => 'web',
'pass' => '3ch0th3w4lRUS'
]);You can, additionally, preserve stdClass objects in parsed JSON by passing FALSE to the second parameter:
$config_data = $jin_parser->parse($jin_string, FALSE)->get();field=value;INIstylestringStrings don't have to be quoted, but can be:
field="value";JSONstylestringIntegers are converted to the proper type automatically:
integerValue=1Floats too...
floatValue=1.03Booleans and NULL values are case insensitive:
boolValue=falseboolValue=TRUEnullValue=NULLmulti=JINsupportsmulti-linevaluesuntilthenewlineresemblesanINIdatabasestructure.So,forexample,thislinewouldbeparsedwithnewlinespreserveduntil`foo=bar`or`[section]`or`\n\n`.Comments are allowed anywhere in a value, so it is important to keep in mind that anything after an ; character is going to be cut off.
field="This probably does not do what you expect; this is stripped"Arrays are defined literally:
favoriteFoods=["Tacos","Sushi","Curry"]Objects are also defined literally too:
favorites={"food": "Indian","music": "Classic Rock"}Both can span multiple lines and contain comments:
multiFoods=["Tacos","Sushi",;Mostketofriendly"Curry"]multiFavorites={;;Thebasics;"food": "Tacos","music": "Classic Rock";Notactuallymyfavorite}Although values can be JSON-like, they are not, strictly speaking, JSON. The major difference is that they do not support JSON's built in escaped characters, so you cannot use \n or \t. On the bright side, you do not need to escape a backslash:
middlewares=["App\Middleware\ResponseHandler"]Sections provide an alternative to JSON object structures. Note, sections are never parsed as stdClass objects, but will always return associative arrays.
[category]fieldOne=valueOnefieldTwo=valueTwofieldThree=1fieldFour=[0,7,9,13]fieldFive={"foo": "bar"}You can add sub-sections by separating the previous category name by a dot. This is extremely useful for keyed configuration values with repeating data, for example, imagine a database config with multiple aliases connections:
[database][database.connections.default]driver=pgsqldbname=websitehost=localhost[database.connections.forums]driver=mysqldbname=forumshost=localhostuser=webpass=3ch0th3w4lRUSYou can reference a parent section for shorter section names.
[database][&.connections.default]driver=pgsqldbname=websitehost=localhostReferences can be stacked to refer to sub-sub-sections. Reference stacking always begins from the last section defined without a reference:
[database][&.connections];;Thissectioncontainsallofourdatabaseconnections;[&&.default]driver=pgsqldbname=websitehost=localhostYou can get values from the environment.
envField=env(DEBUGGING)And provide defaults when they are not set:
envField=env(DEBUGGING,TRUE)You can use native language functions (in this implementation, PHP):
runField=run(md5('hash this thing'))You can add context to the parser for access to variables as well:
$jin_parser = newDotink\Jin\Parser([
'app' => $app
]);Then access/use them as you'd expect:
cacheDirectory=run($app->getDirectory('storage/cache',TRUE))You can add custom functions by passing an array of callables keyed by the function name as the second parameter to the parser:
$jin_parser = newDotink\Jin\Parser([], [
'hello' => function($name) {
return'Hello ' . $name;
}
]);Then use them as you'd expect.
hello=hello(Matt)Note, you can overload the env() and even the run() function, however, overloading structures such as map(), def(), inc() will not work.
Templates provide a powerful way to duplciate complex data structures with different values:
[database]settings=def(type,name,host,user,pass){{"type": $type,"name": $name,"host": $host,"auth": {"user": $user,"pass": $pass}}}[&.connections]default=inc(database.settings){pgsqlmy_databaselocalhost;;Donotbeafraidtouseanyvalidvaluewherevaluesare;specified;env(DB_USER,web)env(DB_PASS,NULL)}Templates can also be used to create arrays of non-keyed objects:
[routing]route=def(methods,pattern,target){{"methods": $methods,"pattern": $pattern,"target": $target}};;Themapfunctiontakesatabseparatedlistofvalues.Multipletabs;arereducedtoonebeforeparsing.;routes=map(routing.route){["GET"]/ViewHome["GET"]/articlesListArticles["POST"]/articlesCreateArticle["GET"]/articles/{id}ViewArticle["POST"]/articles/{id}EditArticle}php vendor/bin/phpunit --bootstrap vendor/autoload.php test/routines/
JIN was originally written as a way to configure a data mapper ORM. It is a very flexible and intuitive language, but it may not make sense in all cases. It is strongly recommended that if you are using it for frequently accessed configurations (like during runtime) that you serialize and cache the resulting collection rather than parsing it on every load.
There is a hobbled together grammar file for Atom which can be found here:
https://github.com/dotink/atom-language-jin
Because of its similarity to TOML, TOML syntax highlighting also tends to look well. You can alternatively try JS/JSON syntax highlighting, but your mileage may vary depending on syntax highlighting implementations.