initial commit
This commit is contained in:
parent
92770f7a95
commit
8cff693387
16 changed files with 2357 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/vendor/
|
37
.gitlab-ci.yml
Normal file
37
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,37 @@
|
|||
# This file is a template, and might need editing before it works on your project.
|
||||
# Select image from https://hub.docker.com/_/php/
|
||||
image: php:7.1.1
|
||||
|
||||
# Select what we should cache between builds
|
||||
cache:
|
||||
paths:
|
||||
- vendor/
|
||||
|
||||
before_script:
|
||||
- apt-get update -yqq
|
||||
- apt-get install -yqq git libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev
|
||||
# Install PHP extensions
|
||||
- docker-php-ext-install mbstring mcrypt pdo_pgsql curl json intl gd xml zip bz2 opcache
|
||||
# Install & enable Xdebug for code coverage reports
|
||||
- pecl install xdebug
|
||||
- docker-php-ext-enable xdebug
|
||||
# Install and run Composer
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
- php composer.phar install
|
||||
|
||||
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||
# See http://docs.gitlab.com/ce/ci/services/README.html for examples.
|
||||
services:
|
||||
- mysql:5.7
|
||||
|
||||
# Set any variables we need
|
||||
variables:
|
||||
# Configure mysql environment variables (https://hub.docker.com/r/_/mysql/)
|
||||
MYSQL_DATABASE: mysql_database
|
||||
MYSQL_ROOT_PASSWORD: mysql_strong_password
|
||||
|
||||
# Run our tests
|
||||
# If Xdebug was installed you can generate a coverage report and see code coverage metrics.
|
||||
test:
|
||||
script:
|
||||
- vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never
|
31
composer.json
Normal file
31
composer.json
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "byjoby/flatarray",
|
||||
"description": "A library for working with multi-dimensional arrays through flattened keys",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [{
|
||||
"name": "Joby Elliott",
|
||||
"email": "joby@byjoby.com"
|
||||
}],
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FlatArray\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"FlatArray\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"phpunit"
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"symfony/yaml": "^3.4"
|
||||
}
|
||||
}
|
1541
composer.lock
generated
Normal file
1541
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
7
phpunit.xml
Normal file
7
phpunit.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<phpunit bootstrap="vendor/autoload.php">
|
||||
<testsuites>
|
||||
<testsuite name="All">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
74
src/Config/Config.php
Normal file
74
src/Config/Config.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
namespace FlatArray\Config;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use FlatArray\SelfReferencingFlatArray;
|
||||
use FlatArray\FlatArray;
|
||||
|
||||
class Config extends SelfReferencingFlatArray implements ConfigInterface
|
||||
{
|
||||
protected function parse(string $input, string $format) : array
|
||||
{
|
||||
$fn = 'parse_'.$format;
|
||||
if (!method_exists($this, $fn)) {
|
||||
throw new \Exception("Don't know how to parse the format \"$format\"");
|
||||
}
|
||||
if ($out = $this->$fn($input)) {
|
||||
return $out;
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function parse_yaml($input)
|
||||
{
|
||||
return Yaml::parse($input);
|
||||
}
|
||||
|
||||
public function json($raw = false) : string
|
||||
{
|
||||
return json_encode($this->get(null, $raw), JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function yaml($raw = false) : string
|
||||
{
|
||||
return Yaml::dump($this->get(null, $raw), 10);
|
||||
}
|
||||
|
||||
protected function read_ini($filename)
|
||||
{
|
||||
return parse_ini_file($filename, true);
|
||||
}
|
||||
|
||||
protected function read_json($filename)
|
||||
{
|
||||
return json_decode(file_get_contents($filename), true);
|
||||
}
|
||||
|
||||
protected function read_yaml($filename)
|
||||
{
|
||||
return Yaml::parse(file_get_contents($filename));
|
||||
}
|
||||
|
||||
protected function read_yml($filename)
|
||||
{
|
||||
return $this->read_yaml($filename);
|
||||
}
|
||||
|
||||
public function readFile($filename, string $name = null, bool $overwrite = false)
|
||||
{
|
||||
if (!is_file($filename) || !is_readable($filename)) {
|
||||
throw new \Exception("Couldn't read config file \"$filename\"");
|
||||
}
|
||||
$format = strtolower(preg_replace('/.+\./', '', $filename));
|
||||
$fn = 'read_'.$format;
|
||||
if (!method_exists($this, $fn)) {
|
||||
throw new \Exception("Don't know how to read the format \"$format\"");
|
||||
}
|
||||
$data = $this->$fn($filename);
|
||||
if (!$data) {
|
||||
throw new \Exception("Error reading \"".$filename."\"");
|
||||
}
|
||||
$this->merge($data, $name, $overwrite);
|
||||
}
|
||||
}
|
12
src/Config/ConfigInterface.php
Normal file
12
src/Config/ConfigInterface.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
namespace FlatArray\Config;
|
||||
|
||||
interface ConfigInterface extends \ArrayAccess
|
||||
{
|
||||
public function readFile($filename, string $name = null, bool $overwrite = false);
|
||||
public function json($raw = false) : string;
|
||||
public function yaml($raw = false) : string;
|
||||
public function get(string $name = null, bool $raw = false);
|
||||
public function merge($value, string $name = null, bool $overwrite = false);
|
||||
}
|
220
src/FlatArray.php
Normal file
220
src/FlatArray.php
Normal file
|
@ -0,0 +1,220 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
namespace FlatArray;
|
||||
|
||||
class FlatArray implements FlatArrayInterface
|
||||
{
|
||||
private $_arrayData = array();
|
||||
|
||||
public function __construct(array $data = null)
|
||||
{
|
||||
$this->merge($data);
|
||||
}
|
||||
|
||||
public function push(?string $name, $value)
|
||||
{
|
||||
$arr = $this->get($name);
|
||||
if ($arr !== null && !is_array($arr)) {
|
||||
return;
|
||||
}
|
||||
$arr[] = $value;
|
||||
$this->set($name, $arr);
|
||||
}
|
||||
|
||||
public function pop(?string $name)
|
||||
{
|
||||
$arr = $this->get($name);
|
||||
if ($arr !== null && !is_array($arr)) {
|
||||
return;
|
||||
}
|
||||
$out = array_pop($arr);
|
||||
$this->set($name, $arr);
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function shift(?string $name, $value)
|
||||
{
|
||||
$arr = $this->get($name);
|
||||
if ($arr !== null && !is_array($arr)) {
|
||||
return;
|
||||
}
|
||||
array_unshift($arr, $value);
|
||||
$this->set($name, $arr);
|
||||
}
|
||||
|
||||
public function unshift(?string $name)
|
||||
{
|
||||
$arr = $this->get($name);
|
||||
if ($arr !== null && !is_array($arr)) {
|
||||
return;
|
||||
}
|
||||
$out = array_shift($arr);
|
||||
$this->set($name, $arr);
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function set(?string $name, $value)
|
||||
{
|
||||
return $this->flattenSearch($name, $value);
|
||||
}
|
||||
|
||||
public function get(?string $name = null)
|
||||
{
|
||||
return $this->flattenSearch($name);
|
||||
}
|
||||
|
||||
public function unset(?string $name)
|
||||
{
|
||||
$this->flattenSearch($name, null, true);
|
||||
}
|
||||
|
||||
public function offsetSet($name, $value)
|
||||
{
|
||||
return $this->set($name, $value);
|
||||
}
|
||||
|
||||
public function offsetGet($name)
|
||||
{
|
||||
return $this->get($name);
|
||||
}
|
||||
|
||||
public function offsetExists($name)
|
||||
{
|
||||
return $this->flattenSearch($name) !== null;
|
||||
}
|
||||
|
||||
public function offsetUnset($name)
|
||||
{
|
||||
return $this->unset($name);
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
return reset($this->_arrayData);
|
||||
}
|
||||
|
||||
public function current()
|
||||
{
|
||||
return current($this->_arrayData);
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
return next($this->_arrayData);
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return key($this->_arrayData);
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return isset($this->_arrayData[$this->key()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively set a value, with control over whether existing values or new
|
||||
* values take precedence
|
||||
*/
|
||||
public function merge($value, string $name = null, bool $overwrite = false)
|
||||
{
|
||||
if (!isset($this[$name])) {
|
||||
//easiest possible outcome, old value doesn't exist, so we can just write the value
|
||||
$this->set($name, $value);
|
||||
return;
|
||||
} elseif (is_array($value) && is_array($this->get($name))) {
|
||||
//both new and old values are arrays
|
||||
foreach ($value as $k => $v) {
|
||||
if ($name) {
|
||||
$k = $name.'.'.$k;
|
||||
}
|
||||
$this->merge($v, $k, $overwrite);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
//old and new values exist, and one or both are not arrays, $overwrite rules the day
|
||||
if ($overwrite) {
|
||||
$this->set($name, $value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively builds a reference down into $this->config from a config key
|
||||
* string. It sets it if $value exists, otherwise it returns the value if it
|
||||
* exists.
|
||||
*/
|
||||
protected function flattenSearch(?string $name, $value = null, $unset = false)
|
||||
{
|
||||
//normalize key names in $value, and also $name
|
||||
$value = $this->normalizeValue($value);
|
||||
$name = preg_replace('/[^a-z0-9\-\_\.]/', '', strtolower($name));
|
||||
//check for home strings
|
||||
if ($name == '' || $name === null) {
|
||||
if ($unset) {
|
||||
$this->_arrayData = [];
|
||||
} elseif ($value !== null) {
|
||||
$this->_arrayData = $value;
|
||||
}
|
||||
return $this->_arrayData;
|
||||
}
|
||||
//validate
|
||||
if (substr($name, -1, 1) == '.' || substr($name, 0, 1) == '..' || strpos($name, '..') !== false) {
|
||||
return null;
|
||||
}
|
||||
//build a reference to where this name should be
|
||||
$parent = &$this->_arrayData;
|
||||
$name = explode('.', $name);
|
||||
$key = array_pop($name);
|
||||
foreach ($name as $part) {
|
||||
if (!isset($parent[$part])) {
|
||||
if ($value !== null) {
|
||||
$parent[$part] = array();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
$parent = &$parent[$part];
|
||||
}
|
||||
//now we have a ref, see if we can unset or set it
|
||||
if ($unset) {
|
||||
unset($parent[$key]);
|
||||
return null;
|
||||
} elseif ($value !== null) {
|
||||
if (is_array(@$parent[$key]) && is_array($value)) {
|
||||
//both value and destination are arrays, merge them
|
||||
$parent[$key] = array_replace_recursive($parent[$key], $value);
|
||||
} else {
|
||||
//set the hard way
|
||||
if (!is_array($parent)) {
|
||||
$parent = array();
|
||||
}
|
||||
$parent[$key] = $value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//return value
|
||||
if (!is_array($parent)) {
|
||||
return null;
|
||||
}
|
||||
return @$parent[$key];
|
||||
}
|
||||
|
||||
protected function normalizeValue($value)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
$norm = [];
|
||||
foreach ($value as $key => $value) {
|
||||
$nKey = preg_replace('/[^a-z0-9\-\_]/', '', strtolower($key));
|
||||
if ($nKey == '') {
|
||||
throw new \Exception("Key \"$key\" can't be used inside a FlatArray");
|
||||
}
|
||||
$norm[$nKey] = $this->normalizeValue($value);
|
||||
}
|
||||
return $norm;
|
||||
}
|
||||
}
|
17
src/FlatArrayInterface.php
Normal file
17
src/FlatArrayInterface.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
namespace FlatArray;
|
||||
|
||||
interface FlatArrayInterface extends \ArrayAccess, \Iterator
|
||||
{
|
||||
public function __construct(array $data = null);
|
||||
public function set(?string $name, $value);
|
||||
public function get(?string $name = null);
|
||||
public function unset(?string $name);
|
||||
public function merge($value, string $name = null, bool $overwrite = false);
|
||||
|
||||
public function push(?string $name, $value);
|
||||
public function pop(?string $name);
|
||||
public function shift(?string $name, $value);
|
||||
public function unshift(?string $name);
|
||||
}
|
90
src/SelfReferencingFlatArray.php
Normal file
90
src/SelfReferencingFlatArray.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
namespace FlatArray;
|
||||
|
||||
class SelfReferencingFlatArray extends FlatArray
|
||||
{
|
||||
public function get(string $name=null, bool $raw = false, $unescape = true)
|
||||
{
|
||||
$out = parent::get($name);
|
||||
if ($raw) {
|
||||
return $out;
|
||||
}
|
||||
$out = $this->filter($out);
|
||||
if ($unescape) {
|
||||
$out = $this->unescape($out);
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
protected function unescape($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
//search/replace on string values
|
||||
if (is_string($value)) {
|
||||
//unescape references
|
||||
$value = preg_replace(
|
||||
'/\$\\\?\{([^\}\\\]+)\\\?\}/',
|
||||
'\${$1}',
|
||||
$value
|
||||
);
|
||||
//return
|
||||
return $value;
|
||||
}
|
||||
//map this function onto array values
|
||||
if (is_array($value)) {
|
||||
return array_map(
|
||||
function ($i) {
|
||||
return $this->filter($i);
|
||||
},
|
||||
$value
|
||||
);
|
||||
}
|
||||
//fall back to just returning value, it's some other datatype
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively replace ${var/name} type strings in string values with
|
||||
*/
|
||||
protected function filter($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
//search/replace on string values
|
||||
if (is_string($value)) {
|
||||
//search for valid replacements
|
||||
$value = preg_replace_callback(
|
||||
//search for things like ${var/name}, escape with \ before last brace
|
||||
'/\$\{([^\}]*[^\.\\\])\}/',
|
||||
//replace match with value from $this if it exists
|
||||
function ($matches) {
|
||||
if (null !== $value = $this->get($matches[1], false, false)) {
|
||||
if (!is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return $matches[0];
|
||||
},
|
||||
//applied to $value
|
||||
$value
|
||||
);
|
||||
//return
|
||||
return $value;
|
||||
}
|
||||
//map this function onto array values
|
||||
if (is_array($value)) {
|
||||
return array_map(
|
||||
function ($i) {
|
||||
return $this->filter($i);
|
||||
},
|
||||
$value
|
||||
);
|
||||
}
|
||||
//fall back to just returning value, it's some other datatype
|
||||
return $value;
|
||||
}
|
||||
}
|
55
tests/Config/ConfigTest.php
Normal file
55
tests/Config/ConfigTest.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
declare(strict_types=1);
|
||||
namespace FlatArray\Config;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ConfigTest extends TestCase
|
||||
{
|
||||
public function testVariables()
|
||||
{
|
||||
//single level of variable
|
||||
$c = new Config();
|
||||
$c['a.b'] = 'a';
|
||||
$c['c'] = '${a.b}';
|
||||
$this->assertEquals('a', $c['c']);
|
||||
//variable referencing another variable
|
||||
$c['d'] = '${c}';
|
||||
$this->assertEquals('a', $c['d']);
|
||||
}
|
||||
|
||||
public function testReading()
|
||||
{
|
||||
$data = [
|
||||
'a' => 'b',
|
||||
'c' => [
|
||||
'd' => 'e'
|
||||
]
|
||||
];
|
||||
//json
|
||||
$a = new Config();
|
||||
$a->readFile(__DIR__.'/configtest.json');
|
||||
$this->assertEquals($data, $a->get());
|
||||
//yaml
|
||||
$a = new Config();
|
||||
$a->readFile(__DIR__.'/configtest.yaml');
|
||||
$this->assertEquals($data, $a->get());
|
||||
}
|
||||
|
||||
public function testSerializing()
|
||||
{
|
||||
$data = [
|
||||
'a' => 'b',
|
||||
'c' => [
|
||||
'd' => 'e'
|
||||
]
|
||||
];
|
||||
$c = new Config($data);
|
||||
//json
|
||||
$this->assertEquals($data, json_decode($c->json(), true));
|
||||
//yaml
|
||||
$this->assertEquals($data, Yaml::parse($c->yaml()));
|
||||
}
|
||||
}
|
6
tests/Config/configtest.json
Normal file
6
tests/Config/configtest.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"a": "b",
|
||||
"c": {
|
||||
"d": "e"
|
||||
}
|
||||
}
|
3
tests/Config/configtest.yaml
Normal file
3
tests/Config/configtest.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
a: b
|
||||
c:
|
||||
d: e
|
33
tests/FlatArrayPushPopTest.php
Normal file
33
tests/FlatArrayPushPopTest.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
declare(strict_types=1);
|
||||
namespace FlatArray;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class FlatArrayPushPopTest extends TestCase
|
||||
{
|
||||
public function testPushPop()
|
||||
{
|
||||
$f = new FlatArray();
|
||||
$f->push(null, 'foo');
|
||||
$f->push(null, 'bar');
|
||||
$this->assertEquals(['foo','bar'], $f->get());
|
||||
$this->assertEquals('bar', $f->pop(null));
|
||||
$this->assertEquals(['foo'], $f->get());
|
||||
$this->assertEquals('foo', $f->pop(null));
|
||||
$this->assertEquals([], $f->get());
|
||||
}
|
||||
|
||||
public function testShiftUnshift()
|
||||
{
|
||||
$f = new FlatArray();
|
||||
$f->shift(null, 'foo');
|
||||
$f->shift(null, 'bar');
|
||||
$this->assertEquals(['bar','foo'], $f->get());
|
||||
$this->assertEquals('bar', $f->unshift(null));
|
||||
$this->assertEquals(['foo'], $f->get());
|
||||
$this->assertEquals('foo', $f->unshift(null));
|
||||
$this->assertEquals([], $f->get());
|
||||
}
|
||||
}
|
177
tests/FlatArrayTest.php
Normal file
177
tests/FlatArrayTest.php
Normal file
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
declare(strict_types=1);
|
||||
namespace FlatArray;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class FlatArrayTest extends TestCase
|
||||
{
|
||||
public function testGetting()
|
||||
{
|
||||
$data = [
|
||||
'a' => 'A',
|
||||
'b' => ['c'=>'C']
|
||||
];
|
||||
$a = new FlatArray($data);
|
||||
//first level
|
||||
$this->assertEquals('A', $a['a']);
|
||||
$this->assertEquals('A', $a->get('a'));
|
||||
//nested
|
||||
$this->assertEquals('C', $a['b.c']);
|
||||
$this->assertEquals('C', $a->get('b.c'));
|
||||
//returning array
|
||||
$this->assertEquals(['c'=>'C'], $a['b']);
|
||||
$this->assertEquals(['c'=>'C'], $a->get('b'));
|
||||
//returning entire array by requesting null or empty string
|
||||
$this->assertEquals($data, $a[null]);
|
||||
$this->assertEquals($data, $a->get());
|
||||
$this->assertEquals($data, $a['']);
|
||||
$this->assertEquals($data, $a->get(''));
|
||||
//requesting invalid keys should return null
|
||||
$this->assertNull($a->get('nonexistent'));
|
||||
$this->assertNull($a->get('b.nonexistent'));
|
||||
$this->assertNull($a->get('..'));
|
||||
$this->assertNull($a->get('.'));
|
||||
//double dots
|
||||
$this->assertNull($a->get('..a'));
|
||||
$this->assertNull($a->get('a..'));
|
||||
$this->assertNull($a->get('..a..'));
|
||||
$this->assertNull($a->get('..a..'));
|
||||
$this->assertNull($a->get('b..c'));
|
||||
$this->assertNull($a->get('b..c..'));
|
||||
$this->assertNull($a->get('..b..c'));
|
||||
$this->assertNull($a->get('..b..c..'));
|
||||
$this->assertNull($a->get('b.c..'));
|
||||
$this->assertNull($a->get('..b.c'));
|
||||
$this->assertNull($a->get('..b.c..'));
|
||||
//single dots
|
||||
$this->assertNull($a->get('.a'));
|
||||
$this->assertNull($a->get('a.'));
|
||||
$this->assertNull($a->get('.a.'));
|
||||
$this->assertNull($a->get('.a.'));
|
||||
$this->assertNull($a->get('b.c.'));
|
||||
$this->assertNull($a->get('.b.c'));
|
||||
$this->assertNull($a->get('.b.c.'));
|
||||
$this->assertNull($a->get('b.c.'));
|
||||
$this->assertNull($a->get('.b.c'));
|
||||
$this->assertNull($a->get('.b.c.'));
|
||||
}
|
||||
|
||||
public function testSetting()
|
||||
{
|
||||
$data = [
|
||||
'a' => 'A',
|
||||
'b' => ['c'=>'C']
|
||||
];
|
||||
$a = new FlatArray($data);
|
||||
//setting on first layer
|
||||
$a['a'] = 'B';
|
||||
$this->assertEquals('B', $a['a']);
|
||||
$a['new'] = 'NEW';
|
||||
$this->assertEquals('NEW', $a['new']);
|
||||
//setting nested
|
||||
$a['b.c'] = 'D';
|
||||
$this->assertEquals('D', $a['b.c']);
|
||||
$a['b.new'] = 'NEW';
|
||||
$this->assertEquals('NEW', $a['b.new']);
|
||||
//final state
|
||||
$this->assertEquals(
|
||||
[
|
||||
'a' => 'B',
|
||||
'b' => [
|
||||
'c' => 'D',
|
||||
'new' => 'NEW'
|
||||
],
|
||||
'new' => 'NEW'
|
||||
],
|
||||
$a->get()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCaseNormalizing()
|
||||
{
|
||||
$h = new FlatArray([
|
||||
'ABC'=>['ABC'=>'ABC']
|
||||
]);
|
||||
$this->assertEquals('ABC', $h['abc.abc']);
|
||||
$this->assertEquals('ABC', $h['Abc.aBC']);
|
||||
$h['Abc.aBC'] = 'abc';
|
||||
$this->assertEquals('abc', $h['abc.abc']);
|
||||
$this->assertEquals('abc', $h['Abc.aBC']);
|
||||
$h['ABC'] = ['ABC'=>'ABC'];
|
||||
$this->assertEquals('ABC', $h['abc.abc']);
|
||||
$this->assertEquals('ABC', $h['Abc.aBC']);
|
||||
}
|
||||
|
||||
public function testAccidentalSubstrings()
|
||||
{
|
||||
$h = new FlatArray(['foo'=>'bar']);
|
||||
$this->assertNull($h['foo.baz']);
|
||||
}
|
||||
|
||||
public function testMerge()
|
||||
{
|
||||
$data = [
|
||||
'a' => 'b',
|
||||
'c' => [
|
||||
'd' => 'e'
|
||||
]
|
||||
];
|
||||
//overwrite false, original values should be preserved
|
||||
$c = new FlatArray($data);
|
||||
$c->merge([
|
||||
'a' => 'B',
|
||||
'c' => [
|
||||
'd' => 'E',
|
||||
'f' => 'g'
|
||||
],
|
||||
'h' => 'i'
|
||||
]);
|
||||
$this->assertEquals('b', $c['a']);
|
||||
$this->assertEquals('e', $c['c.d']);
|
||||
$this->assertEquals('i', $c['h']);
|
||||
$this->assertEquals('g', $c['c.f']);
|
||||
//overwrite true, original values should be overwritten
|
||||
$c = new FlatArray($data);
|
||||
$c->merge([
|
||||
'a' => 'B',
|
||||
'c' => [
|
||||
'd' => 'E',
|
||||
'f' => 'g'
|
||||
],
|
||||
'h' => 'i'
|
||||
], null, true);
|
||||
$this->assertEquals('B', $c['a']);
|
||||
$this->assertEquals('E', $c['c.d']);
|
||||
$this->assertEquals('i', $c['h']);
|
||||
$this->assertEquals('g', $c['c.f']);
|
||||
//overwrite false with mismatched array-ness
|
||||
$c = new FlatArray($data);
|
||||
$c->merge([
|
||||
'a' => ['b'=>'c'],
|
||||
'c' => 'd'
|
||||
]);
|
||||
$this->assertEquals('b', $c['a']);
|
||||
$this->assertEquals('e', $c['c.d']);
|
||||
//overwrite true with mismatched array-ness
|
||||
$c = new FlatArray($data);
|
||||
$c->merge([
|
||||
'a' => ['b'=>'c'],
|
||||
'c' => 'd'
|
||||
], null, true);
|
||||
$this->assertEquals('c', $c['a.b']);
|
||||
$this->assertEquals('d', $c['c']);
|
||||
}
|
||||
|
||||
public function testConstructionUnflattening()
|
||||
{
|
||||
$arr = new FlatArray([
|
||||
'foo.bar' => 'baz'
|
||||
]);
|
||||
$this->assertEquals(
|
||||
['foo'=>['bar'=>'baz']],
|
||||
$arr->get()
|
||||
);
|
||||
}
|
||||
}
|
53
tests/SelfReferencingFlatArrayTest.php
Normal file
53
tests/SelfReferencingFlatArrayTest.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/* FlatArray | https://gitlab.com/byjoby/flatarray | MIT License */
|
||||
declare(strict_types=1);
|
||||
namespace FlatArray;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SelfReferencingFlatArrayTest extends TestCase
|
||||
{
|
||||
public function testVariables()
|
||||
{
|
||||
$f = new SelfReferencingFlatArray([
|
||||
'foo' => 'bar',
|
||||
'bar' => ['baz'=>'qux'],
|
||||
'test' => [
|
||||
'foo' => '${foo}',
|
||||
'bar' => '${bar.baz}',
|
||||
'none' => '${foo.none}',
|
||||
'escaped' => [
|
||||
'full' => '$\\{foo\\}',
|
||||
'left' => '$\\{foo}',
|
||||
'right' => '${foo\\}'
|
||||
],
|
||||
],
|
||||
'nested' => [
|
||||
'foo' => '${test.foo}',
|
||||
'bar' => '${test.bar}',
|
||||
'none' => '${test.none}',
|
||||
'escaped' => [
|
||||
'full' => '${test.escaped.full}',
|
||||
'left' => '${test.escaped.left}',
|
||||
'right' => '${test.escaped.right}'
|
||||
]
|
||||
]
|
||||
]);
|
||||
//basic references
|
||||
$this->assertEquals('bar', $f['test.foo']);
|
||||
$this->assertEquals('qux', $f['test.bar']);
|
||||
$this->assertEquals('${foo.none}', $f['test.none']);
|
||||
//nested references
|
||||
$this->assertEquals('bar', $f['nested.foo']);
|
||||
$this->assertEquals('qux', $f['nested.bar']);
|
||||
$this->assertEquals('${foo.none}', $f['nested.none']);
|
||||
//escaped
|
||||
$this->assertEquals('${foo}', $f['test.escaped.full']);
|
||||
$this->assertEquals('${foo}', $f['test.escaped.left']);
|
||||
$this->assertEquals('${foo}', $f['test.escaped.right']);
|
||||
//nested and escaped
|
||||
$this->assertEquals('${foo}', $f['nested.escaped.full']);
|
||||
$this->assertEquals('${foo}', $f['nested.escaped.left']);
|
||||
$this->assertEquals('${foo}', $f['nested.escaped.right']);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue