123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- <?php
- declare(strict_types=1);
-
- /**
- * ENV
- *
- * The PHP singleton is considered bad design for nebulous reasons,
- * but for securely loading a site config it does exactly what we need:
- *
- * - Ensure that only one instance of itself can ever exist
- * - Load the instance everywhere we need to do $ENV->VALUE
- * - No memory penalty because of multiple $ENV instances
- * - Static values in classes/config.php are immutable
- * - Site configs don't exist in the constants table
- * - Separate public and private config values
- *
- * @see https://stackoverflow.com/a/3724689
- * @see https://phpenthusiast.com/blog/the-singleton-design-pattern-in-php
- */
-
- class ENV
- {
- # Disinstantiates itself
- private static $ENV = null;
-
- # Config options receptacles
- private static $Priv = []; # Passwords, app keys, database, etc.
- private static $Pub = []; # Site meta, options, resources, etc.
-
-
- /**
- * __functions
- */
-
- # Prevents outside construction
- private function __construct()
- {
- # Would be expensive, e.g.,
- # $ENV = new ENV();
- return;
- }
-
- # Prevents multiple instances
- public function __clone()
- {
- return error('clone() not allowed.');
- }
-
- # Prevents unserializing
- public function __wakeup()
- {
- return error('wakeup() not allowed.');
- }
-
- # $this->key returns public->key
- public function __get($key)
- {
- return isset(self::$Pub[$key])
- ? self::$Pub[$key]
- : false;
- }
-
- # isset()
- public function __isset($key)
- {
- return isset(self::$Pub[$key]);
- }
-
-
- /**
- * Gets n' Sets
- */
-
- # Calls its self's creation or returns itself
- public static function go()
- {
- return (self::$ENV === null)
- ? self::$ENV = new ENV()
- : self::$ENV;
- }
-
- # get
- public static function getPriv($key)
- {
- return isset(self::$Priv[$key])
- ? self::$Priv[$key]
- : false;
- }
-
- public static function getPub($key)
- {
- return isset(self::$Pub[$key])
- ? self::$Pub[$key]
- : false;
- }
-
- # set
- public static function setPriv($key, $value)
- {
- return self::$Priv[$key] = $value;
- }
-
- public static function setPub($key, $value)
- {
- return self::$Pub[$key] = $value;
- }
-
-
- /**
- * convert
- *
- * Take a mixed input and returns a RecursiveArrayObject.
- * This function is the sausage grinder, so to speak.
- */
- public function convert($obj)
- {
- switch (gettype($obj)) {
- case 'string':
- $out = json_decode($obj, true);
- return (json_last_error() === JSON_ERROR_NONE)
- ? new RecursiveArrayObject($out)
- : error('json_last_error_msg(): ' . json_last_error_msg());
- break;
-
- case 'array':
- case 'object':
- return new RecursiveArrayObject($obj);
-
- default:
- return error('$ENV->convert() expects a JSON string, array, or object.');
- break;
- }
- }
-
-
- /**
- * toArray
- *
- * Takes an object and returns an array.
- * @param object|string $obj Thing to turn into an array
- * @return $new New recursive array with $obj contents
- * @see https://ben.lobaugh.net/blog/567/php-recursively-convert-an-object-to-an-array
- */
- public function toArray($obj)
- {
- if (is_object($obj)) {
- $obj = (array) $obj;
- }
-
- if (is_array($obj)) {
- $new = array();
-
- foreach ($obj as $key => $value) {
- $new[$key] = $this->toArray($value);
- }
- } else {
- $new = $obj;
- }
- return $new;
- }
-
-
- /**
- * dedupe
- *
- * Takes a collection (usually an array) of various jumbled $ENV slices.
- * Returns a once-deduplicated RecursiveArrayObject with original nesting intact.
- * Simple and handy if you need to populate a form with arbitrary collections of metadata.
- */
- public function dedupe($obj)
- {
- if (is_object($obj)) {
- $obj = (array) $obj;
- }
-
- return new RecursiveArrayObject(
- array_unique($this->toArray($obj))
- );
- }
-
-
- /**
- * flatten
- *
- * Takes an $ENV node or array of them
- * and flattens out the multi-dimensionality.
- * It returns a flat array with keys intact.
- */
- public function flatten($arr, int $lvl = null)
- {
- if (!is_array($arr) && !is_object($arr)) {
- return error('$ENV->flatten() expects an array or object, got ' . gettype($arr));
- }
-
- $new = array();
-
- foreach ($arr as $k => $v) {
- /*
- if (is_object($v)) {
- $v = $this->toArray($v);
- }
- */
-
- if (is_array($v)) {
- $new = array_merge($new, $this->flatten($v));
- } else {
- $new[$k] = $v;
- }
- }
-
- return $new;
- }
-
-
- /**
- * map
- *
- * Simple array_map() object wrapper.
- * Maps a callback (or default) to an object.
- *
- * Example output:
- * $Hashes = $ENV->map('md5', $ENV->CATS->{6});
- *
- * var_dump($Hashes);
- * object(RecursiveArrayObject)#324 (1) {
- * ["storage":"ArrayObject":private]=>
- * array(6) {
- * ["ID"]=>
- * string(32) "28c8edde3d61a0411511d3b1866f0636"
- * ["Name"]=>
- * string(32) "fe83ccb5dc96dbc0658b3c4672c7d5fe"
- * ["Icon"]=>
- * string(32) "52963afccc006d2bce3c890ad9e8f73a"
- * ["Platforms"]=>
- * string(32) "d41d8cd98f00b204e9800998ecf8427e"
- * ["Formats"]=>
- * string(32) "d41d8cd98f00b204e9800998ecf8427e"
- * ["Description"]=>
- * string(32) "ca6628e8c13411c800d1d9d0eaccd849"
- * }
- * }
- *
- * var_dump($Hashes->Icon);
- * string(32) "52963afccc006d2bce3c890ad9e8f73a"
- *
- * @param string $fn Callback function
- * @param object|string $obj Object or property to operate on
- * @return object $RAO Mapped RecursiveArrayObject
- */
- public function map(string $fn = '', $obj = null)
- {
- # Set a default function if desired
- if (empty($fn) && !is_object($fn)) {
- $fn = 'array_filter';
- }
-
- # Quick sanity check
- if ($fn === 'array_map') {
- error("map() can't invoke the function it wraps.");
- }
-
- /**
- * $fn not a closure
- *
- * var_dump(
- * gettype(
- * (function() { return; })
- * ));
- * string(6) "object"
- */
- if (is_string($fn) && !is_object($fn)) {
- $fn = trim(strtok($fn, ' '));
- }
-
- # Map the sanitized function name
- # to a mapped array conversion
- return new RecursiveArrayObject(
- array_map(
- $fn,
- array_map(
- $fn,
- $this->toArray($obj)
- )
- )
- );
- }
- }
-
-
- /**
- * @author: etconsilium@github
- * @license: BSDLv2
- * @see https://github.com/etconsilium/php-recursive-array-object
- */
-
- class RecursiveArrayObject extends \ArrayObject
- {
- /**
- * __construct
- */
- public function __construct($input = null, $flags = self::ARRAY_AS_PROPS, $iterator_class = "ArrayIterator")
- {
- foreach ($input as $key => $value) {
- $this->__set($key, $value);
- }
- return $this;
- }
-
-
- /**
- * __set
- */
- public function __set($name, $value)
- {
- if (is_array($value) || is_object($value)) {
- $this->offsetSet($name, (new self($value)));
- } else {
- $this->offsetSet($name, $value);
- }
- }
-
-
- /**
- * __get
- */
- public function __get($name)
- {
- if ($this->offsetExists($name)) {
- return $this->offsetGet($name);
- } elseif (array_key_exists($name, $this)) {
- return $this[$name];
- } else {
- throw new \InvalidArgumentException(sprintf('$this have not prop `%s`', $name));
- }
- }
-
-
- /**
- * __isset
- */
- public function __isset($name)
- {
- return array_key_exists($name, $this);
- }
-
-
- /**
- * __unset
- */
- public function __unset($name)
- {
- unset($this[$name]);
- }
- }
|