Completed
Push — master ( c95337...5e0152 )
by P.R.
04:34
created

BaseCommand::readMetadata()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.0218

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 17
ccs 8
cts 9
cp 0.8889
rs 9.2
cc 4
eloc 8
nc 4
nop 0
crap 4.0218
1
<?php
2
//----------------------------------------------------------------------------------------------------------------------
3
namespace SetBased\Audit\Command;
4
5
use SetBased\Exception\RuntimeException;
6
use SetBased\Stratum\Style\StratumStyle;
7
use Symfony\Component\Console\Command\Command;
8
use Symfony\Component\Console\Formatter\OutputFormatter;
9
10
//----------------------------------------------------------------------------------------------------------------------
11
/**
12
 * Base command for other commands of AuditApplication.
13
 */
14
class BaseCommand extends Command
15
{
16
  //--------------------------------------------------------------------------------------------------------------------
17
  /**
18
   * All config file as array.
19
   *
20
   * @var array
21
   */
22
  protected $config = [];
23
24
  /**
25
   * The name of the configuration file.
26
   *
27
   * @var string
28
   */
29
  protected $configFileName = '';
30
31
  /**
32
   * Table metadata from config file.
33
   *
34
   * @var array
35
   */
36
  protected $configMetadata = [];
37
38
  /**
39
   * The Output decorator.
40
   *
41
   * @var StratumStyle
42
   */
43
  protected $io;
44
45
  /**
46
   * If set (the default) the config file must be rewritten. Set to false for testing only.
47
   *
48
   * @var bool
49
   */
50
  protected $rewriteConfigFile = true;
51
52
  //--------------------------------------------------------------------------------------------------------------------
53
  /**
54
   * Returns the value of a setting.
55
   *
56
   * @param array  $settings    The settings as returned by parse_ini_file.
57
   * @param bool   $mandatory   If set and setting $settingName is not found in section $sectionName an exception
58
   *                            will be thrown.
59
   * @param string $sectionName The name of the section of the requested setting.
60
   * @param string $settingName The name of the setting of the requested setting.
61
   *
62
   * @return null|string
63
   *
64
   * @throws RuntimeException
65
   */
66 17
  protected static function getSetting($settings, $mandatory, $sectionName, $settingName)
67
  {
68
    // Test if the section exists.
69 17
    if (!array_key_exists($sectionName, $settings))
70
    {
71
      if ($mandatory)
72
      {
73
        throw new RuntimeException("Section '%s' not found in configuration file.", $sectionName);
74
      }
75
      else
76
      {
77
        return null;
78
      }
79
    }
80
81
    // Test if the setting in the section exists.
82 17
    if (!array_key_exists($settingName, $settings[$sectionName]))
83
    {
84
      if ($mandatory)
85
      {
86
        throw new RuntimeException("Setting '%s' not found in section '%s' configuration file.",
87
                                   $settingName,
88
                                   $sectionName);
89
      }
90
      else
91
      {
92
        return null;
93
      }
94
    }
95
96 17
    return $settings[$sectionName][$settingName];
97
  }
98
99
  //--------------------------------------------------------------------------------------------------------------------
100
  /**
101
   * Reads configuration parameters from the configuration file.
102
   */
103 17
  public function readConfigFile()
104
  {
105 17
    $content = file_get_contents($this->configFileName);
106
107 17
    $this->config = (array)json_decode($content, true);
108 17
    if (json_last_error()!=JSON_ERROR_NONE)
109
    {
110
      throw new RuntimeException("Error decoding JSON: '%s'.", json_last_error_msg());
111
    }
112
113 17
    if (!isset($this->config['audit_columns']))
114
    {
115 1
      $this->config['audit_columns'] = [];
116
    }
117
118 17
    if (!isset($this->config['additional_sql']))
119
    {
120 2
      $this->config['additional_sql'] = [];
121
    }
122
123 17
    if (!isset($this->config['tables']))
124
    {
125 1
      $this->config['tables'] = [];
126
    }
127
128 17
    $this->readMetadata();
129 17
  }
130
131
  //--------------------------------------------------------------------------------------------------------------------
132
  /**
133
   * Use for testing only.
134
   *
135
   * @param bool $rewriteConfigFile If true the config file must be rewritten. Otherwise the config must not be
136
   *                                rewritten.
137
   */
138 17
  public function setRewriteConfigFile($rewriteConfigFile)
139
  {
140 17
    $this->rewriteConfigFile = $rewriteConfigFile;
141 17
  }
142
143
  //--------------------------------------------------------------------------------------------------------------------
144
  /**
145
   * Rewrites the config file with updated data.
146
   */
147 17
  protected function rewriteConfig()
148
  {
149
    // Return immediately when the config file must not be rewritten.
150 17
    if (!$this->rewriteConfigFile) return;
151
152 4
    ksort($this->config['tables']);
153 4
    $this->writeTwoPhases($this->configFileName, json_encode($this->config, JSON_PRETTY_PRINT));
154
155 4
    $filename = $this->getTableMetadataPath();
156 4
    if ($filename!==null)
157
    {
158 4
      ksort($this->configMetadata);
159 4
      $this->writeTwoPhases($filename, json_encode($this->configMetadata, JSON_PRETTY_PRINT));
160
    }
161 4
  }
162
163
  //--------------------------------------------------------------------------------------------------------------------
164
  /**
165
   * Writes a file in two phase to the filesystem.
166
   *
167
   * First write the data to a temporary file (in the same directory) and than renames the temporary file. If the file
168
   * already exists and its content is equal to the data that must be written no action  is taken. This has the
169
   * following advantages:
170
   * * In case of some write error (e.g. disk full) the original file is kept in tact and no file with partially data
171
   * is written.
172
   * * Renaming a file is atomic. So, running processes will never read a partially written data.
173
   *
174
   * @param string $filename The name of the file were the data must be stored.
175
   * @param string $data     The data that must be written.
176
   */
177 4
  protected function writeTwoPhases($filename, $data)
178
  {
179 4
    $write_flag = true;
180 4
    if (file_exists($filename))
181
    {
182 4
      $old_data = file_get_contents($filename);
183 4
      if ($data==$old_data) $write_flag = false;
184
    }
185
186 4
    if ($write_flag)
187
    {
188
      $tmp_filename = $filename.'.tmp';
189
      file_put_contents($tmp_filename, $data);
190
      rename($tmp_filename, $filename);
191
192
      $this->io->text(sprintf('Wrote <fso>%s</fso>', OutputFormatter::escape($filename)));
193
    }
194
    else
195
    {
196 4
      $this->io->text(sprintf('File <fso>%s</fso> is up to date', OutputFormatter::escape($filename)));
197
    }
198 4
  }
199
200
  //--------------------------------------------------------------------------------------------------------------------
201
  /**
202
   * Returns the path of the table metadata file.
203
   *
204
   * @return string|null
205
   */
206 17
  private function getTableMetadataPath()
207
  {
208 17
    if (isset($this->config['metadata']))
209
    {
210 17
      return dirname($this->configFileName).'/'.$this->config['metadata'];
211
    }
212
213
    return null;
214
  }
215
216
  //--------------------------------------------------------------------------------------------------------------------
217
  /**
218
   * Reads table metadata from the table metadata config file (if any).
219
   */
220 17
  private function readMetadata()
221
  {
222 17
    $filename = $this->getTableMetadataPath();
223 17
    if ($filename!==null)
224
    {
225 17
      if (file_exists($filename))
226
      {
227 17
        $content = file_get_contents($filename);
228
229 17
        $this->configMetadata = (array)json_decode($content, true);
230 17
        if (json_last_error()!=JSON_ERROR_NONE)
231
        {
232
          throw new RuntimeException("Error decoding JSON: '%s'.", json_last_error_msg());
233
        }
234
      }
235
    }
236 17
  }
237
238
  //--------------------------------------------------------------------------------------------------------------------
239
}
240
241
//----------------------------------------------------------------------------------------------------------------------
242