Page MenuHomePhabricator

D561.diff
No OneTemporary

D561.diff

diff --git a/SpitFire.php b/SpitFire.php
--- a/SpitFire.php
+++ b/SpitFire.php
@@ -5,6 +5,7 @@
use spitfire\core\app\AppNotFoundException;
use spitfire\core\app\AppAssetsInterface;
use spitfire\core\app\RecursiveAppAssetLocator;
+use spitfire\core\config\Configuration;
use spitfire\core\Context;
use spitfire\core\Environment;
use spitfire\core\Request;
@@ -36,6 +37,12 @@
*/
private $provider;
+ /**
+ *
+ * @var core\config\Configuration
+ */
+ private $config;
+
/**
* Provides logging capabilities for the applications running within Spitfire.
* You can select a logging mechanism by adding a PSR\log compatible logger
@@ -113,6 +120,12 @@
}
+ public function config(Configuration $set = null) : Configuration
+ {
+ if ($set !== null) { $this->config = $set; }
+ return $this->config;
+ }
+
/**
* Set / Get applications from Spitfire. The software you write can use this
* to communicate with the applications.
diff --git a/_init/InitScriptInterface.php b/_init/InitScriptInterface.php
new file mode 100644
--- /dev/null
+++ b/_init/InitScriptInterface.php
@@ -0,0 +1,31 @@
+<?php namespace spitfire\_init;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2021 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+interface InitScriptInterface
+{
+
+ public function exec() : void;
+}
diff --git a/_init/LoadConfiguration.php b/_init/LoadConfiguration.php
new file mode 100644
--- /dev/null
+++ b/_init/LoadConfiguration.php
@@ -0,0 +1,73 @@
+<?php namespace spitfire\_init;
+
+use spitfire\core\config\Configuration;
+use spitfire\core\Environment;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2021 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+class LoadConfiguration implements InitScriptInterface
+{
+
+ public function exec(): void
+ {
+ $config = new Configuration();
+ env(new Environment(spitfire()->locations()->root('.env')));
+
+ /*
+ * This function walks the directory for the config and loads the appropriate
+ * data.
+ */
+ $walk = function ($dir, $namespace) use (&$walk, $config) {
+ /*
+ * We're only interested in PHP files, since these contain the models.
+ * The system does not just import all the models and use reflection to
+ * locate them, instead, it does some magic with the filenames.
+ *
+ * There's a certain level of risk we assume whenever blindly looping
+ * over a set of files in PHP and including them. But other than manually
+ * parsing them - there's not much we can do to look for class declarations
+ * in them. The models folder should be for models only.
+ */
+ $scripts = glob($dir . '*.php');
+
+ foreach ($scripts as $file) {
+ $config->import($namespace . basename($file), include($file));
+ }
+
+ /*
+ * We iterate into folders to locate deeper seated models.
+ */
+ $folders = glob($dir . '*', GLOB_ONLYDIR);
+
+ foreach ($folders as $folder) {
+ $walk($dir . basename($folder) . DIRECTORY_SEPARATOR, $namespace . basename($folder) . '.');
+ }
+ };
+
+ $walk(spitfire()->locations()->config(), '');
+ spitfire()->config($config);
+ }
+
+}
diff --git a/core/config/Configuration.php b/core/config/Configuration.php
new file mode 100644
--- /dev/null
+++ b/core/config/Configuration.php
@@ -0,0 +1,114 @@
+<?php namespace spitfire\core\config;
+
+use spitfire\support\arrays\DotNotationAccessor;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2021 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Configuration contains an array of data parsed from a configuration file (or
+ * multiple, in the event of the configuration referring to a directory that contains
+ * configuration files).
+ *
+ * Due to the fact that configuration is cached, the system generates configuration
+ * by invoking a static method that will recursively walk over all the files and
+ * import the data, assembling a tree of arrays.
+ *
+ * When caching the configuration, the loaded environments are also cached and
+ * therefore your application's cache will need to be rebuilt in order to load
+ * new environments.
+ *
+ * Configuration files are automatically flattened, so that information can be
+ * read with dot notation easily.
+ *
+ * NOTE: Configuration does not support arrays (this is why they are flattened). I seem
+ * to get tripped up by this concept myself a lot, and this is why I'm adding this
+ * note. If you need to configure something in an array style fashion you're probably
+ * better off using service providers.
+ */
+class Configuration
+{
+
+ /**
+ * Contains the configuration array. This array contains the flattened config
+ * from the configuration files (even if these contain arrays to set up the stuff)
+ *
+ * @var string[]
+ */
+ private $data = [];
+
+ /**
+ * Using a dot notation accessor for this class removes the complexity from this
+ * class and allows us to work on caching the data here.
+ *
+ * @var DotNotationAccessor
+ */
+ private $interface;
+
+ public function __construct()
+ {
+ $this->interface = new DotNotationAccessor($this->data);
+ }
+
+ /**
+ * Retrieve a configuration from the repository. You may not retrieve a config
+ * as an array.
+ *
+ * @param string $key
+ * @param mixed $fallback
+ */
+ public function get(string $key, $fallback = null)
+ {
+ return $this->interface->has($key)? $this->interface->get($key) : $fallback;
+ }
+
+ /**
+ * Set a configuration. Please note that all the code that went before will not have
+ * received the configuration. Also, you should consider replacing calls to this with
+ * writing to the configuration directly whenever possible.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return Configuration
+ */
+ public function set(string $key, $value = null)
+ {
+ $this->interface->set($key, $value);
+ return $this;
+ }
+
+ /**
+ * Import the configuration from a file. These files can contain executable code, or
+ * environment calls, but whenever caching is enabled the configuration will be computed
+ * once and not be regenerated until you request it.
+ *
+ * @param string $namespace
+ * @param mixed[] $values
+ */
+ public function import(string $namespace, $values)
+ {
+ $this->interface->set($namespace, $values);
+ return $this;
+ }
+}
diff --git a/core/functions.php b/core/functions.php
--- a/core/functions.php
+++ b/core/functions.php
@@ -477,3 +477,21 @@
])
);
}
+
+/**
+ * Returns an application configuration. Note that configuration in Spitfire is tiered,
+ * configuration and environments.
+ *
+ * You as the developer should establish the configuration so the components you add to
+ * your application work as expected, and should expose the environment to the person
+ * deploying the application so they can customize the behavior.
+ *
+ *
+ * @param string $key The key in the configuration
+ * @param mixed $fallback The value to return if the configuration does not contain the key
+ * @return mixed The data for this configuration entry
+ */
+function config($key, $fallback = null)
+{
+ return spitfire()->config()->get($key, $fallback);
+}
diff --git a/support/arrays/DotNotationAccessor.php b/support/arrays/DotNotationAccessor.php
new file mode 100644
--- /dev/null
+++ b/support/arrays/DotNotationAccessor.php
@@ -0,0 +1,115 @@
+<?php namespace spitfire\support\arrays;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2021 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+class DotNotationAccessor
+{
+
+ /**
+ * Determines whether the accessor should return an array for the given data or
+ * whether the accessor should return null if the data did not hit a leaf.
+ */
+ const ALLOW_ARRAY_RETURN = 0x0001;
+
+ private $data;
+
+ public function __construct(&$data) {
+ $this->data = &$data;
+ }
+
+ public function has(string $key) : bool
+ {
+ $steps = explode('.', $key);
+ $current = &$this->data;
+
+ while ($steps) {
+ $key = array_shift($steps);
+
+ /*
+ * If the key does not exist, we cannot continue further down into the
+ * array. We need to stop here.
+ */
+ if (!array_key_exists($key, $current)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function get(string $key, int $flags = 0)
+ {
+ $steps = explode('.', $key);
+ $current = &$this->data;
+
+ while ($steps) {
+ $key = array_shift($steps);
+
+ /*
+ * If the key does not exist, we cannot continue further down into the
+ * array. We need to stop here.
+ */
+ if (!array_key_exists($key, $current)) {
+ return null;
+ }
+
+ $current = &$current[$key];
+ }
+
+ /**
+ * If we're not supposed to return arrays, but the data at hand is an array,
+ * we will return a null value.
+ */
+ if (is_array($current) && !($flags && self::ALLOW_ARRAY_RETURN)) {
+ return null;
+ }
+
+ return $current;
+ }
+
+ public function set(string $key, $data)
+ {
+ $steps = explode('.', $key);
+ $current = &$this->data;
+
+ while (isset($steps[1])) {
+ $key = array_shift($steps);
+
+ /*
+ * If the key does not exist, we cannot continue further down into the
+ * array. We need to stop here.
+ */
+ if (!array_key_exists($key, $current)) {
+ $current[$key] = [];
+ }
+
+ $current = &$current[$key];
+ }
+
+ $current[$steps[0]] = $data;
+ return $this;
+ }
+}
diff --git a/tests/core/config/ConfigurationTest.php b/tests/core/config/ConfigurationTest.php
new file mode 100644
--- /dev/null
+++ b/tests/core/config/ConfigurationTest.php
@@ -0,0 +1,66 @@
+<?php namespace tests\spitfire\core\config;
+
+use PHPUnit\Framework\TestCase;
+use spitfire\core\config\Configuration;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2019 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+class ConfigurationTest extends TestCase
+{
+
+ public function testBasicWrite()
+ {
+ $config = new Configuration();
+ $config->set('test', 'hello');
+ $config->set('test.this', 'hello world');
+ $config->set('test.that', false);
+ $config->set('test.everything', null);
+
+ $this->assertEquals('hello', $config->get('test'));
+ $this->assertEquals('hello world', $config->get('test.this'));
+ $this->assertEquals(null, $config->get('test.everything'));
+ $this->assertEquals(false, $config->get('test.that'));
+
+ /**
+ * This key does not exist, so we would return the fallback value
+ */
+ $this->assertEquals(null, $config->get('test.th'));
+ }
+
+ public function testImport()
+ {
+ $config = new Configuration();
+ $config->import('hello', [
+ 'world' => 'hello world',
+ 'everyone' => 'hello everyone'
+ ]);
+
+
+ $this->assertEquals(null, $config->get('hello'));
+ $this->assertEquals('hello world', $config->get('hello.world'));
+ }
+
+}
\ No newline at end of file
diff --git a/tests/support/arrays/DotNotationAccessorTest.php b/tests/support/arrays/DotNotationAccessorTest.php
new file mode 100644
--- /dev/null
+++ b/tests/support/arrays/DotNotationAccessorTest.php
@@ -0,0 +1,117 @@
+<?php namespace tests\spitfire\support\arrays;
+
+use PHPUnit\Framework\TestCase;
+use spitfire\support\arrays\DotNotationAccessor;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2021 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+class DotNotationAccessorTest extends TestCase
+{
+
+ public function testRead()
+ {
+ $array = [
+ 'a' => 'b',
+ 'foo' => [
+ 'bar' => 'baz'
+ ],
+ 2 => [3]
+ ];
+
+ $interface = new DotNotationAccessor($array);
+
+ $this->assertEquals('b', $interface->get('a'));
+ $this->assertEquals('baz', $interface->get('foo.bar'));
+ }
+
+ /**
+ * Test that changes made to the array are listened to by the accessor.
+ */
+ public function testExternalChanges()
+ {
+ $array = [
+ 'a' => 'b',
+ 'foo' => [
+ 'bar' => 'baz'
+ ],
+ 2 => [3]
+ ];
+
+ $interface = new DotNotationAccessor($array);
+
+ $this->assertEquals('b', $interface->get('a'));
+ $this->assertEquals('baz', $interface->get('foo.bar'));
+
+ $array['foo']['bar'] = 'Hello world';
+ $this->assertEquals('Hello world', $interface->get('foo.bar'));
+
+ $array['foo'] = ['bar' => 'Bye world'];
+ $this->assertEquals('Bye world', $interface->get('foo.bar'));
+
+ }
+
+ /**
+ * Test that changes that were made inside the accessor class actually are
+ * broadcast to the array it's monitoring
+ */
+ public function testInternalChanges()
+ {
+ $array = [
+ 'a' => 'b',
+ 'foo' => [
+ 'bar' => 'baz'
+ ],
+ 2 => [3]
+ ];
+
+ $interface = new DotNotationAccessor($array);
+
+ $this->assertEquals('b', $interface->get('a'));
+ $this->assertEquals('baz', $interface->get('foo.bar'));
+
+ $interface->set('foo.bar', 'Hello world');
+ $this->assertEquals('Hello world', $array['foo']['bar']);
+
+ $interface->set('foo', ['bar' => 'Bye world']);
+ $this->assertEquals('Bye world', $array['foo']['bar']);
+
+ }
+
+ public function testFlags()
+ {
+ $array = [
+ 'a' => 'b',
+ 'foo' => [
+ 'bar' => 'baz'
+ ],
+ 2 => [3]
+ ];
+
+ $interface = new DotNotationAccessor($array);
+ $this->assertEquals(null, $interface->get('foo'));
+ $this->assertArrayHasKey('bar', $interface->get('foo', DotNotationAccessor::ALLOW_ARRAY_RETURN));
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Apr 13, 3:39 PM (4 w, 54 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
6077
Default Alt Text
D561.diff (19 KB)

Event Timeline