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