Config.php 7.85 KB
Newer Older
1 2
<?php
/**
3
 * @file
4 5 6
 * Provision configuration generation classes.
 */

7
class Provision_Config {
drumm's avatar
drumm committed
8 9 10 11
  /**
   * Template file, a PHP file which will have access to $this and variables
   * as defined in $data.
   */
12
  public $template = NULL;
drumm's avatar
drumm committed
13 14 15 16

  /**
   * Associate array of variables to make available to the template.
   */
17
  public $data = array();
drumm's avatar
drumm committed
18 19

  /**
20
   * A Provision_Context object thie configuration relates to.
drumm's avatar
drumm committed
21
   *
22
   * @var Provision_Context
drumm's avatar
drumm committed
23
   */
24
  public $context = NULL;
drumm's avatar
drumm committed
25 26 27 28

  /**
   * If set, replaces file name in log messages.
   */
29
  public $description = NULL;
drumm's avatar
drumm committed
30 31 32 33

  /**
   * Octal Unix mode for permissons of the created file.
   */
34
  protected $mode = NULL;
drumm's avatar
drumm committed
35 36 37 38

  /**
   * Unix group name for the created file.
   */
39
  protected $group = NULL;
40

41 42 43 44 45 46 47 48
  /**
   * An optional data store class to instantiate for this config.
   */
  protected $data_store_class = NULL;

  /**
   * The data store.
   */
49
  public $store = NULL;
50

drumm's avatar
drumm committed
51
  /**
52
   * Forward $this->... to $this->context->...
drumm's avatar
drumm committed
53 54
   * object.
   */
55
  function __get($name) {
56 57
    if (isset($this->context)) {
      return $this->context->$name;
58 59
    }
  }
drumm's avatar
drumm committed
60 61 62 63

  /**
   * Constructor, overriding not recommended.
   *
64
   * @param $context
65
   *   An alias name for d(), the Provision_Context that this configuration
drumm's avatar
drumm committed
66 67
   *   is relevant to.
   * @param $data
mig5's avatar
mig5 committed
68
   *   An associative array to potentially manipulate in process() and make
drumm's avatar
drumm committed
69 70
   *   available as variables to the template.
   */
71
  function __construct($context, $data = array()) {
72
    if (is_null($this->template)) {
73
      throw new Exception(dt("No template specified for: %class", array('%class' => get_class($this))));
74
    }
75

76 77
    // Accept both a reference and an alias name for the context.
    $this->context = is_object($context) ? $context : d($context);
78

79
    if (sizeof($data)) {
80
      $this->data = $data;
81
    }
82

83 84
    if (!is_null($this->data_store_class) && class_exists($this->data_store_class)) {
      $class = $this->data_store_class;
85
      $this->store = new $class($context, $data);
86 87
    }

drumm's avatar
drumm committed
88 89
  }

90
  /**
drumm's avatar
drumm committed
91
   * Process and add to $data before writing the configuration.
92
   *
drumm's avatar
drumm committed
93
   * This is a stub to be implemented by subclasses.
94
   */
95
  function process() {
96 97 98
    if (is_object($this->store)) {
      $this->data['records'] = array_filter(array_merge($this->store->loaded_records, $this->store->records));
    }
99
    return TRUE;
100 101
  }

102
  /**
drumm's avatar
drumm committed
103
   * The filename where the configuration is written.
104
   *
drumm's avatar
drumm committed
105
   * This is a stub to be implemented by subclasses.
106
   */
107
  function filename() {
108
    return FALSE;
109 110
  }

drumm's avatar
drumm committed
111 112
  /**
   * Load template from filename().
113 114 115
   *
   * @see hook_provision_config_load_templates()
   * @see hook_provision_config_load_templates_alter()
drumm's avatar
drumm committed
116
   */
117
  private function load_template() {
118

119 120 121 122 123 124 125 126 127 128
    // Allow other Drush commands to change the template used first.
    $templates = drush_command_invoke_all('provision_config_load_templates', $this);
    // Ensure that templates is at least an array.
    if (!is_array($templates)) {
      $templates = array();
    }
    // Allow other Drush commands to alter the templates from other commands.
    drush_command_invoke_all_ref('provision_config_load_templates_alter', $templates, $this);
    if (!empty($templates) && is_array($templates)) {
      foreach ($templates as $file) {
129
        if (is_readable($file)) {
130
          drush_log(dt('Template loaded from hook(s): :file', array(
131
            ':file' => $file,
132
          )));
133 134 135 136 137 138 139
          return file_get_contents($file);
        }
      }
    }

    // If we've got this far, then try to find a template from this class or
    // one of its parents.
140
    if (isset($this->template)) {
141
      $class_name = get_class($this);
142 143 144
      while ($class_name) {
        // Iterate through the config file's parent classes until we
        // find the template file to use.
145
        $base_dir = provision_class_directory($class_name);
146 147
        $file = $base_dir . '/' . $this->template;

148
        if (is_readable($file)) {
149 150 151 152
          drush_log(dt('Template loaded from Provision Config class :class_name: :file', array(
            ':class_name' => $class_name,
            ':file' => $file,
          )));
153 154 155 156
          return file_get_contents($file);
        }

        $class_name = get_parent_class($class_name);
157
      }
158
    }
159

160
    // We've failed to find a template if we've reached this far.
161
    drush_log(dt('No template found for Provision Config class: ', array(':class' => get_class($this))), 'warning');
162
    return FALSE;
163 164
  }

drumm's avatar
drumm committed
165 166 167 168
  /**
   * Render template, making variables available from $variables associative
   * array.
   */
169
  private function render_template($template, $variables) {
helmo's avatar
helmo committed
170

171 172 173
    // Allow modules to alter the variables before writing to the template.
    // @see hook_provision_config_variables_alter()
    drush_command_invoke_all_ref('provision_config_variables_alter', $variables, $template, $this);
174
    
175 176 177
    drush_errors_off();
    extract($variables, EXTR_SKIP);  // Extract the variables to a local namespace
    ob_start();                      // Start output buffering
178
    eval('?>' . $template);                 // Generate content
179 180 181 182 183 184
    $contents = ob_get_contents();   // Get the contents of the buffer
    ob_end_clean();                  // End buffering and discard
    drush_errors_on();
    return $contents;                // Return the contents
  }

drumm's avatar
drumm committed
185 186 187 188 189 190 191 192 193 194
  /**
   * Write out this configuration.
   *
   * 1. Make sure parent directory exists and is writable.
   * 2. Load template with load_template().
   * 3. Process $data with process().
   * 4. Make existing file writable if necessary and possible.
   * 5. Render template with $this and $data and write out to filename().
   * 6. If $mode and/or $group are set, apply them for the new file.
   */
195 196
  function write() {
    $filename = $this->filename();
197
    // Make directory structure if it does not exist.
198
    if ($filename && !provision_file()->exists(dirname($filename))->status()) {
199
      provision_file()->mkdir(dirname($filename))
200 201 202 203
        ->succeed('Created directory @path.')
        ->fail('Could not create directory @path.');
    }

204
    $status = FALSE;
205 206 207 208 209
    if ($filename && is_writeable(dirname($filename))) {
      // manipulate data before passing to template.
      $this->process();

      if ($template = $this->load_template()) {
210
        // Make sure we can write to the file
211 212
        if (!is_null($this->mode) && !($this->mode & 0200) && provision_file()->exists($filename)->status()) {
          provision_file()->chmod($filename, $this->mode | 0200)
213 214 215 216
            ->succeed('Changed permissions of @path to @perm')
            ->fail('Could not change permissions of @path to @perm');
        }

217
        $status = provision_file()->file_put_contents($filename, $this->render_template($template, $this->data))
helmo's avatar
helmo committed
218 219
          ->succeed('Generated config in write(): ' . (empty($this->description) ? $filename : $this->description . ' (' . $filename. ')'), 'success')
          ->fail('Could not generate in write(): ' . (empty($this->description) ? $filename : $this->description . ' (' . $filename. ')'))->status();
220 221 222

        // Change the permissions of the file if needed
        if (!is_null($this->mode)) {
223
          provision_file()->chmod($filename, $this->mode)
224 225 226 227
            ->succeed('Changed permissions of @path to @perm')
            ->fail('Could not change permissions of @path to @perm');
        }
        if (!is_null($this->group)) {
228
          provision_file()->chgrp($filename, $this->group)
229 230
            ->succeed('Change group ownership of @path to @gid')
            ->fail('Could not change group ownership of @path to @gid');
231 232 233
        }
      }
    }
234
    return $status;
235 236
  }

237 238 239
  // allow overriding w.r.t locking
  function file_put_contents($filename, $text) {
    provision_file()->file_put_contents($filename, $text)
240
      ->succeed('Generated config in file_put_contents()' . (empty($this->description) ? $filename : $this->description), 'success');
241 242
  }

drumm's avatar
drumm committed
243 244 245
  /**
   * Remove configuration file as specified by filename().
   */
246
  function unlink() {
247
    return provision_file()->unlink($this->filename())->status();
248
  }
249

250
}