summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Pugh2017-11-27 19:42:47 (GMT)
committerJon Pugh2017-11-27 19:42:47 (GMT)
commit8e8b813b95fd347dcc5bd1ebba31a52ed92bccca (patch)
tree69a3236aed05c5a3533ea5520cca019a3c548f09
parent1c0f6e2639834c5fbe0dd7062674421b5dee6154 (diff)
Fix major loading time problems. When there are more than 3 contexts, loading time just to get the list was huge:
- Move YML file search and loading code to new method loadAllContexts() method that runs on Provision::__construct(). - Ensure dependent contexts can be loaded (using loadContext() method). - Refactor objects to always be "ProvisionAware". - Remove static functions for public and private. - Fix ContextNodeDefinition to use ProvisionAwareTrait.
-rw-r--r--src/Command.php4
-rw-r--r--src/Command/SaveCommand.php2
-rw-r--r--src/Command/StatusCommand.php1
-rw-r--r--src/ConfigDefinition/ContextNodeDefinition.php14
-rw-r--r--src/Context.php17
-rw-r--r--src/Context/SiteContext.php7
-rw-r--r--src/ContextSubscriber.php1
-rw-r--r--src/Provision.php112
-rw-r--r--src/ServiceSubscription.php5
9 files changed, 110 insertions, 53 deletions
diff --git a/src/Command.php b/src/Command.php
index 4a572b6..2c35f30 100644
--- a/src/Command.php
+++ b/src/Command.php
@@ -74,7 +74,7 @@ abstract class Command extends BaseCommand
try {
// Load context from context_name argument.
$this->context_name = $this->input->getArgument('context_name');
- $this->context = Provision::getContext($this->context_name, $this->getProvision());
+ $this->context = $this->getProvision()->getContext($this->context_name);
}
catch (\Exception $e) {
@@ -96,7 +96,7 @@ abstract class Command extends BaseCommand
$this->input->setArgument('context_name', $this->context_name);
try {
- $this->context = Provision::getContext($this->context_name, $this->getProvision());
+ $this->context = $this->getProvision()->getContext($this->context_name);
}
catch (\Exception $e) {
$this->context = NULL;
diff --git a/src/Command/SaveCommand.php b/src/Command/SaveCommand.php
index 68faf28..ee83c13 100644
--- a/src/Command/SaveCommand.php
+++ b/src/Command/SaveCommand.php
@@ -352,7 +352,7 @@ class SaveCommand extends Command
$contexts[$property] = $this->input->getOption($property);
try {
- $context = Provision::getContext($contexts[$property]);
+ $context = $this->getProvision()->getContext($contexts[$property]);
}
catch (\Exception $e) {
throw new \Exception("Context set by option --{$property} does not exist.");
diff --git a/src/Command/StatusCommand.php b/src/Command/StatusCommand.php
index c6e8039..ff75deb 100644
--- a/src/Command/StatusCommand.php
+++ b/src/Command/StatusCommand.php
@@ -43,6 +43,7 @@ class StatusCommand extends Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
+ $this->getProvision();
if ($input->getArgument('context_name')) {
$rows = [['Configuration File', $this->context->config_path]];
diff --git a/src/ConfigDefinition/ContextNodeDefinition.php b/src/ConfigDefinition/ContextNodeDefinition.php
index 2be0aeb..12c707c 100644
--- a/src/ConfigDefinition/ContextNodeDefinition.php
+++ b/src/ConfigDefinition/ContextNodeDefinition.php
@@ -2,8 +2,7 @@
namespace Aegir\Provision\ConfigDefinition;
-use Aegir\Provision\Application;
-use Aegir\Provision\Provision;
+use Aegir\Provision\Common\ProvisionAwareTrait;
use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
@@ -29,6 +28,8 @@ use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
*/
class ContextNodeDefinition extends ScalarNodeDefinition
{
+ use ProvisionAwareTrait;
+
protected function createNode()
{
/**
@@ -51,12 +52,13 @@ class ContextNodeDefinition extends ScalarNodeDefinition
*/
public function validateContext($value)
{
+ $this->setProvision($this->getNode()->getAttribute('provision'));
+
// No need to do anything else.
// If there is no context named $value, getContext() throws an exception for us.
- Provision::getContext($value);
// If context_type is specified, Validate that the desired context is the right type.
- if ($this->getNode()->getAttribute('context_type') && Provision::getContext($value)->type != $this->getNode()->getAttribute('context_type')) {
+ if ($this->getNode()->getAttribute('context_type') && $this->getProvision()->getContext($value)->type != $this->getNode()->getAttribute('context_type')) {
throw new InvalidConfigurationException(strtr('The context specified for !name must be type !type.', [
'!name' => $this->name,
'!type' => $this->getNode()->getAttribute('context_type'),
@@ -70,8 +72,8 @@ class ContextNodeDefinition extends ScalarNodeDefinition
$this->getNode()->getAttribute('service_requirement'):
$path[2]
;
-
- Provision::getContext($value)->getService($service);
+
+ $this->getProvision()->getContext($value)->getService($service);
}
return $value;
}
diff --git a/src/Context.php b/src/Context.php
index 1dfcb17..c3c3a2c 100644
--- a/src/Context.php
+++ b/src/Context.php
@@ -14,6 +14,7 @@ use Robo\Common\BuilderAwareTrait;
use Robo\Contract\BuilderAwareInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\Processor;
+use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Yaml\Dumper;
@@ -74,17 +75,16 @@ class Context implements BuilderAwareInterface
*/
function __construct(
$name,
- Provision $provision = NULL,
+ Provision $provision,
$options = [])
{
$this->name = $name;
+
+ $this->setProvision($provision);
+ $this->setBuilder($this->getProvision()->getBuilder());
+
$this->loadContextConfig($options);
$this->prepareServices();
-
- if ($provision) {
- $this->setProvision($provision);
- $this->setBuilder($this->getProvision()->getBuilder());
- }
}
/**
@@ -126,7 +126,7 @@ class Context implements BuilderAwareInterface
$this->config = $processor->processConfiguration($this, $configs);
} catch (\Exception $e) {
- throw new \Exception(
+ throw new InvalidOptionException(
strtr("There is an error with the configuration for !type '!name'. Check the file !file and try again. \n \nError: !message", [
'!type' => $this->type,
'!name' => $this->name,
@@ -302,6 +302,7 @@ class Context implements BuilderAwareInterface
->node($property, 'context')
->isRequired()
->attribute('context_type', $type)
+ ->attribute('provision', $this->getProvision())
->end()
->end();
}
@@ -332,7 +333,7 @@ class Context implements BuilderAwareInterface
// If type is empty, it's because it's in the ServerContext
if (empty($info['type'])) {
- $server = Provision::getContext($info['server']);
+ $server = $this->getProvision()->getContext($info['server']);
$service_type = ucfirst($server->getService($service)->type);
}
else {
diff --git a/src/Context/SiteContext.php b/src/Context/SiteContext.php
index 445e5a5..a324cfb 100644
--- a/src/Context/SiteContext.php
+++ b/src/Context/SiteContext.php
@@ -3,6 +3,7 @@
namespace Aegir\Provision\Context;
use Aegir\Provision\Application;
+use Aegir\Provision\Context;
use Aegir\Provision\ContextSubscriber;
use Aegir\Provision\Provision;
use Symfony\Component\Config\Definition\ConfigurationInterface;
@@ -46,9 +47,9 @@ class SiteContext extends ContextSubscriber implements ConfigurationInterface
// There is no need to check if the property exists because the config system does that.
// $this->db_server = $application->getContext($this->properties['db_server']);
- $this->platform = Provision::getContext($this->properties['platform'], $provision);
-
-
+ // Load platform context... @TODO: Automatically do this for required contexts?
+ $this->platform = $this->getProvision()->getContext($this->properties['platform']);
+
// Add platform http service subscription.
$this->serviceSubscriptions['http'] = $this->platform->getSubscription('http');
$this->serviceSubscriptions['http']->context = $this;
diff --git a/src/ContextSubscriber.php b/src/ContextSubscriber.php
index a2a68f3..32da760 100644
--- a/src/ContextSubscriber.php
+++ b/src/ContextSubscriber.php
@@ -93,6 +93,7 @@ class ContextSubscriber extends Context
->node('server', 'context')
->isRequired()
->attribute('context_type', 'server')
+ ->attribute('provision', $this->getProvision())
->end()
->append($this->addServiceProperties('service_subscriptions'))
->end()
diff --git a/src/Provision.php b/src/Provision.php
index 64d6866..3dd0e38 100644
--- a/src/Provision.php
+++ b/src/Provision.php
@@ -63,6 +63,16 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
private $commands = [];
/**
+ * @var \Aegir\Provision\Context[]
+ */
+ private $contexts = [];
+
+ /**
+ * @var array[]
+ */
+ private $context_files = [];
+
+ /**
* Provision constructor.
*
* @param \Aegir\Provision\Console\Config $config
@@ -104,6 +114,46 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
$this->setBuilder($container->get('builder'));
$this->setLogger($container->get('logger'));
+
+ $this->loadAllContexts();
+ }
+
+ /**
+ * Lookup all context yml files and load into Context classes.
+ */
+ private function loadAllContexts()
+ {
+ $folder = $this->getConfig()->get('config_path') . '/provision';
+ $finder = new \Symfony\Component\Finder\Finder();
+ $finder->files()->name("*.yml")->in($folder);
+ foreach ($finder as $file) {
+ $context_type = substr($file->getFilename(), 0, strpos($file->getFilename(), '.'));
+ $context_name = substr($file->getFilename(), strpos($file->getFilename(), '.') + 1, strlen($file->getFilename()) - strlen($context_type) - 5);
+
+ $this->context_files[$context_name] = [
+ 'name' => $context_name,
+ 'type' => $context_type,
+ 'file' => $file,
+ ];
+ }
+
+ // Load Context classes from files metadata.
+ foreach ($this->context_files as $context) {
+ $class = Context::getClassName($context['type']);
+ $this->contexts[$context['name']] = new $class($context['name'], $this);
+ }
+ }
+
+ /**
+ * Loads a single context from file into $this->contexts[$name].
+ *
+ * Used to load dependant contexts that might not be instantiated yet.
+ *
+ * @param $name
+ */
+ public function loadContext($name) {
+ $class = Context::getClassName($this->context_files[$name]['type']);
+ $this->contexts[$this->context_files[$name]['name']] = new $class($this->context_files[$name]['name'], $this);
}
public function run(InputInterface $input, OutputInterface $output) {
@@ -186,41 +236,19 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
}
/**
- * Load all contexts into Context objects.
+ * Return all available contexts.
*
* @return array
*/
- static function getAllContexts($name = '', Provision $provision = NULL) {
- $contexts = [];
- $config = new Config();
-
- $context_files = [];
- $finder = new \Symfony\Component\Finder\Finder();
- $finder->files()->name('*' . $name . '.yml')->in($config->get('config_path') . '/provision');
- foreach ($finder as $file) {
- $context_type = substr($file->getFilename(), 0, strpos($file->getFilename(), '.'));
- $context_name = substr($file->getFilename(), strpos($file->getFilename(), '.') + 1, strlen($file->getFilename()) - strlen($context_type) - 5);
-
- $context_files[$context_name] = [
- 'name' => $context_name,
- 'type' => $context_type,
- 'file' => $file,
- ];
- }
-
- foreach ($context_files as $context) {
- $class = Context::getClassName($context['type']);
- $contexts[$context['name']] = new $class($context['name'], $provision);
- }
-
- if ($name && isset($contexts[$name])) {
- return $contexts[$name];
+ public function getAllContexts($name = '') {
+ if ($name && isset($this->contexts[$name])) {
+ return $this->contexts[$name];
}
- elseif ($name && !isset($contexts[$name])) {
+ elseif ($name && !isset($this->contexts[$name])) {
return NULL;
}
else {
- return $contexts;
+ return $this->contexts;
}
}
@@ -272,14 +300,36 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
* @return array|\Aegir\Provision\Context
* @throws \Exception
*/
- static public function getContext($name, Provision $provision = NULL) {
+ public function getContext($name) {
+ // Check if $name is empty.
+ if (empty($name)) {
+ throw new \Exception('Context name must not be empty.');
+ }
+
+ // If context exists but hasn't been loaded, load it.
+ if (empty($this->contexts[$name]) && !empty($this->context_files[$name])) {
+ $this->loadContext($name);
+ }
+
+ // Check if context still isn't found.
+ if (empty($this->contexts[$name])) {
+ throw new \Exception('Context not found with name: ' . $name);
+ }
+ return $this->contexts[$name];
+ }
+
+ /**
+ * Look for a context file being present. This is available before Context
+ * objects are bootstrapped.
+ */
+ public function getContextFile($name) {
if (empty($name)) {
throw new \Exception('Context name must not be empty.');
}
- if (empty(Provision::getAllContexts($name, $provision))) {
+ if (empty($this->context_files[$name])) {
throw new \Exception('Context not found with name: ' . $name);
}
- return Provision::getAllContexts($name, $provision);
+ return$this->context_files[$name];
}
/**
diff --git a/src/ServiceSubscription.php b/src/ServiceSubscription.php
index b7d9932..fb0c42a 100644
--- a/src/ServiceSubscription.php
+++ b/src/ServiceSubscription.php
@@ -27,7 +27,9 @@ class ServiceSubscription {
$service_name
) {
$this->context = $context;
- $this->server = Provision::getContext($server, $context->getProvision());
+ $this->setProvision($context->getProvision());
+
+ $this->server = $this->getProvision()->getContext($server);
$this->service = $this->server->getService($service_name);
$this->type = $this->server->getService($service_name)->type;
@@ -35,7 +37,6 @@ class ServiceSubscription {
$this->properties = $context->config['service_subscriptions'][$service_name]['properties'];
}
- $this->setProvision($context->getProvision());
}
public function verify() {