FileStorage.php 5.47 KB
Newer Older
1 2
<?php

3 4 5 6 7
/**
 * @file
 * Definition of Drupal\Core\Config\FileStorage.
 */

8 9
namespace Drupal\Core\Config;

10 11
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
12
use Drupal\Component\Utility\String;
13

14
/**
15
 * Defines the file storage.
16
 */
17
class FileStorage implements StorageInterface {
18 19

  /**
20
   * The filesystem path for configuration objects.
21
   *
22
   * @var string
23
   */
24
  protected $directory = '';
25 26

  /**
27
   * Constructs a new FileStorage.
28 29 30
   *
   * @param string $directory
   *   A directory path to use for reading and writing of configuration files.
31
   */
32 33
  public function __construct($directory) {
    $this->directory = $directory;
34 35 36
  }

  /**
37
   * Returns the path to the configuration file.
38
   *
39 40
   * @return string
   *   The path to the configuration file.
41
   */
42
  public function getFilePath($name) {
43
    return $this->directory . '/' . $name . '.' . static::getFileExtension();
44 45 46 47 48 49 50 51 52 53
  }

  /**
   * Returns the file extension used by the file storage for all configuration files.
   *
   * @return string
   *   The file extension.
   */
  public static function getFileExtension() {
    return 'yml';
54 55
  }

56 57 58 59 60 61 62 63 64 65 66 67
  /**
   * Check if the directory exists and create it if not.
   */
  protected function ensureStorage() {
    $success = file_prepare_directory($this->directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
    $success = $success && file_save_htaccess($this->directory, TRUE, TRUE);
    if (!$success) {
      throw new StorageException("Failed to create config directory {$this->directory}");
    }
    return $this;
  }

68
  /**
69
   * Implements Drupal\Core\Config\StorageInterface::exists().
70
   */
71 72
  public function exists($name) {
    return file_exists($this->getFilePath($name));
73 74 75
  }

  /**
76
   * Implements Drupal\Core\Config\StorageInterface::read().
77
   *
78
   * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
79
   */
80 81
  public function read($name) {
    if (!$this->exists($name)) {
82
      return FALSE;
83 84
    }
    $data = file_get_contents($this->getFilePath($name));
85 86 87 88 89 90 91 92 93
    try {
      $data = $this->decode($data);
    }
    catch (InvalidDataTypeException $e) {
      throw new UnsupportedDataTypeConfigException(String::format('Invalid data type in config @name: !message', array(
        '@name' => $name,
        '!message' => $e->getMessage(),
      )));
    }
94
    return $data;
95 96
  }

97 98 99 100 101 102 103 104 105 106 107 108 109
  /**
   * {@inheritdoc}
   */
  public function readMultiple(array $names) {
    $list = array();
    foreach ($names as $name) {
      if ($data = $this->read($name)) {
        $list[$name] = $data;
      }
    }
    return $list;
  }

110
  /**
111
   * {@inheritdoc}
112
   */
113
  public function write($name, array $data) {
114 115 116
    try {
      $data = $this->encode($data);
    }
117 118 119 120 121
    catch (InvalidDataTypeException $e) {
      throw new StorageException(String::format('Invalid data type in config @name: !message', array(
        '@name' => $name,
        '!message' => $e->getMessage(),
      )));
122 123
    }

124 125
    $target = $this->getFilePath($name);
    $status = @file_put_contents($target, $data);
126 127 128 129 130
    if ($status === FALSE) {
      // Try to make sure the directory exists and try witing again.
      $this->ensureStorage();
      $status = @file_put_contents($target, $data);
    }
131 132
    if ($status === FALSE) {
      throw new StorageException('Failed to write configuration file: ' . $this->getFilePath($name));
133
    }
134 135 136
    else {
      drupal_chmod($target);
    }
137
    return TRUE;
138 139 140
  }

  /**
141
   * Implements Drupal\Core\Config\StorageInterface::delete().
142
   */
143 144
  public function delete($name) {
    if (!$this->exists($name)) {
145 146
      if (!file_exists($this->directory)) {
        throw new StorageException($this->directory . '/ not found.');
147 148 149 150
      }
      return FALSE;
    }
    return drupal_unlink($this->getFilePath($name));
151
  }
152

153
  /**
154
   * Implements Drupal\Core\Config\StorageInterface::rename().
155 156 157 158 159 160 161 162 163
   */
  public function rename($name, $new_name) {
    $status = @rename($this->getFilePath($name), $this->getFilePath($new_name));
    if ($status === FALSE) {
      throw new StorageException('Failed to rename configuration file from: ' . $this->getFilePath($name) . ' to: ' . $this->getFilePath($new_name));
    }
    return TRUE;
  }

164
  /**
165
   * Implements Drupal\Core\Config\StorageInterface::encode().
166
   */
167
  public function encode($data) {
168
    return Yaml::encode($data);
169 170 171
  }

  /**
172
   * Implements Drupal\Core\Config\StorageInterface::decode().
173
   */
174
  public function decode($raw) {
175
    $data = Yaml::decode($raw);
176 177 178
    // A simple string is valid YAML for any reason.
    if (!is_array($data)) {
      return FALSE;
179
    }
180
    return $data;
181 182 183
  }

  /**
184
   * Implements Drupal\Core\Config\StorageInterface::listAll().
185
   */
186 187 188
  public function listAll($prefix = '') {
    // glob() silently ignores the error of a non-existing search directory,
    // even with the GLOB_ERR flag.
189
    if (!file_exists($this->directory)) {
190
      return array();
191
    }
192
    $extension = '.' . static::getFileExtension();
193 194
    // \GlobIterator on Windows requires an absolute path.
    $files = new \GlobIterator(realpath($this->directory) . '/' . $prefix . '*' . $extension);
195 196 197 198 199 200 201

    $names = array();
    foreach ($files as $file) {
      $names[] = $file->getBasename($extension);
    }

    return $names;
202
  }
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217

  /**
   * Implements Drupal\Core\Config\StorageInterface::deleteAll().
   */
  public function deleteAll($prefix = '') {
    $success = TRUE;
    $files = $this->listAll($prefix);
    foreach ($files as $name) {
      if (!$this->delete($name) && $success) {
        $success = FALSE;
      }
    }

    return $success;
  }
218
}