Completed
Branch master (1f48b5)
by P.R.
09:12 queued 05:47
created

BaseCommand::setRewriteConfigFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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