diff --git a/README.adoc b/README.adoc index 704e25c..1e2cda3 100644 --- a/README.adoc +++ b/README.adoc @@ -86,6 +86,27 @@ My recommendation is to simply pick an instantiation method and use that for an == Features +=== Storing Data + +This is the primary feature of Brief. + +Brief expects data in an array, but whether that array is keyed or numeric is up to you. +(Or, use a language where arrays aren't as confusing.) + +[source,php] +---- +$keyed = Brief::make([ + 'key' => 'value1', + 'key2' => 'value2', +]); + +$numeric = Brief::make([ + 'number1', + 'number2', +]); +---- + + === Aliasing You may find yourself in a situation where you want to have multiple keys that point to the same data. diff --git a/src/Brief.php b/src/Brief.php index e7d1aef..08aa7b3 100644 --- a/src/Brief.php +++ b/src/Brief.php @@ -224,7 +224,25 @@ protected function collapseAliasChain(string $alias, $chain = []) */ public function getAliasedKey(string $alias) { - return $this->collapseAliasChain($alias); + // Check alias for allowable keys + if (false === $this::isKeyAllowed($alias)) { + $this->log('ProtectedKey', 'This key is protected and cannot be used.', + ['key' => $alias, 'protected_keys' => self::$protected]); + + return false; + } + + $authoritative = $this->collapseAliasChain($alias); + + // Check resolved value for allowable keys + if (false === $this::isKeyAllowed($authoritative)) { + $this->log('ProtectedKey', 'This key is protected and cannot be used.', + ['key' => $authoritative, 'protected_keys' => self::$protected]); + + return false; + } + + return $authoritative; } /** @@ -240,6 +258,10 @@ public function getAliasedKey(string $alias) */ protected function storeSingle($value, string $key = null, int $order = null): self { + if (isset($this->aliases[$key])) { + $key = $this->getAuthoritativeName($key) ?? $key; + } + if (false === $this::isKeyAllowed($key)) { $this->log('ProtectedKey', 'This key is protected and cannot be used.', ['key' => $key, 'protected_keys' => self::$protected]); @@ -247,10 +269,6 @@ protected function storeSingle($value, string $key = null, int $order = null): s return $this; } - if (isset($this->aliases[$key])) { - $key = $this->getAuthoritativeName($key) ?? $key; - } - $this->store[$key] = [ 'value' => $value, 'order' => $order @@ -281,7 +299,6 @@ protected function store(array $values, int $order_start = 0) return $this; } - /** * Checks an individual key to see if it is allowed. * @@ -294,6 +311,38 @@ public static function isKeyAllowed($key) return ! in_array($key, self::$protected); } + protected function getIntFromUnderscoreProp($key) + { + if (0 === strpos($key, '_') && is_numeric(substr($key, 1))) { + return intval(substr($key, 1)); + } + + return null; + } + /** + * TODO: Make a `maybeSetByOrder` that allows us to try and set an ordered + * item by numeric key using an underscore prop. Should first text to see if + * there is an existing prop using the underscore string. + */ + + protected function maybeGetByOrder($key) + { + /** + * Integers are not allowed as object properties, so here we make a + * a way to easily access ordered data; by prefixing the order number + * with an underscore, i.e. `$brief->_1`. + */ + $order = $this->getIntFromUnderscoreProp($key); + if (null !== $order) { + $value = $this->getByOrder($order); + if (null !== $value) { + return $value; + } + } + + return false; + } + /** * True if key has been set; false otherwise. * @@ -303,7 +352,7 @@ public static function isKeyAllowed($key) */ public function __isset($name) { - return in_array($name, array_keys($this->store)); + return $this->__get($name) ?? false; } /** @@ -315,7 +364,7 @@ public function __isset($name) */ public function __get($name) { - return $this->getArgument($this->getAuthoritativeName($name)); + return $this->maybeGetByOrder($name) ?: $this->get($name); } /** @@ -330,7 +379,7 @@ public function __get($name) */ public function __set(string $name, $value) { - $this->storeSingle($value, $this->getAuthoritativeName($name) ?? $name, $this->getIncrementedOrder()); + $this->set($name, $value); } /** @@ -340,13 +389,57 @@ public function __set(string $name, $value) * * @return mixed */ - protected function getArgument($name) + protected function getByKey($name) { return isset($this->store[$name]) ? $this->getValue($this->store[$name]) : null; } + /** + * Get a value from the Brief. + * + * If $key is an integer, it will get the data base on order, on + * the assumption that this is a numeric array. If $key is a string, + * they it will get the data based on the key. + * + * @param string|int $key + * @return mixed|null + */ + public function get($key) + { + if (is_int($key)) { + return $this->getByOrder($key); + } elseif (is_string($key)) { + return $this->getByKey($this->getAuthoritativeName($key)); + } + + return null; + } + + /** + * Store a value. + * + * If $key is an integer, then the value will be stored with that as + * the order and the key name. If $key is a string, then the value will + * be stored under that key, and its order will be the next in order. + * + * @param string|int $key + * @param mixed $value + */ + public function set($key, $value) + { + if (is_int($key)) { + $this->storeSingle($value, (string)$key, $key); + } elseif (is_string($key)) { + $this->storeSingle( + $value, + $this->getAuthoritativeName($key), + $this->getIncrementedOrder() + ); + } + } + /** * This will get the authoritative name (either the key, or the key that * the passed alias points to). @@ -482,6 +575,18 @@ public function getOrdered($fill = null) return array_column($this->getFilledOrdered($fill), 'value', 'order'); } + /** + * Get a value based on its order. + * + * @param integer $int + * @return mixed + */ + public function getByOrder(int $int) + { + $ordered = $this->getOrdered(); + return $ordered[$int] ?? null; + } + /** * Get all data in this Brief as a keyed array. * @@ -557,7 +662,7 @@ public function find($keys) { // Be friendly if (is_string($keys)) { - return $this->getArgument($keys); + return $this->getByKey($keys); } // ...Otherwise, it has to be an array @@ -572,7 +677,7 @@ public function find($keys) $get = array_shift($keys); - return $this->getArgument($get) ?: $this->find($keys); + return $this->getByKey($get) ?: $this->find($keys); } /** diff --git a/tests/BriefTest.php b/tests/BriefTest.php index e682b46..57b4ded 100644 --- a/tests/BriefTest.php +++ b/tests/BriefTest.php @@ -65,7 +65,9 @@ public function testAttemptingToUseProtectedKeyToDynamicallySetValueThrowsCorrec { $this->expectOutputRegex('/ERR: ProtectedKey :: This key is protected and cannot be used. ::.*/ms'); - Brief::make([], ['logger' => true])->protected = 'value'; + $Brief = Brief::make([], ['logger' => true]); + $Brief->protected = 'value'; + var_dump($Brief); } public function testAttemptingToPassNonViableInputLogsCorrectData(): void