BaseCommand   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 160
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 47
c 2
b 0
f 0
dl 0
loc 160
ccs 48
cts 48
cp 1
rs 10
wmc 15

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setRewriteConfigFile() 0 3 1
A connect() 0 9 1
A readConfigFile() 0 20 5
A rewriteConfig() 0 24 4
A writeTwoPhases() 0 23 4
1
<?php
2
declare(strict_types=1);
3
4
namespace SetBased\Audit\Command;
5
6
use Noodlehaus\Config;
7
use SetBased\Audit\MySql\AuditDataLayer;
8
use SetBased\Audit\Style\AuditStyle;
9
use SetBased\Config\TypedConfig;
10
use SetBased\Stratum\MySql\MySqlDefaultConnector;
11
use Symfony\Component\Console\Command\Command;
12
use Symfony\Component\Console\Formatter\OutputFormatter;
13
14
/**
15
 * Base command for other commands of AuditApplication.
16
 */
17
class BaseCommand extends Command
18
{
19
  //--------------------------------------------------------------------------------------------------------------------
20
  /**
21
   * The sections in the configurations file.
22
   *
23
   * @var array
24
   */
25
  private static array $sections = ['database', 'audit_columns', 'additional_sql', 'tables'];
26
27
  /**
28
   * The strong typed configuration reader and writer.
29
   *
30
   * @var TypedConfig
31
   */
32
  protected TypedConfig $config;
33
34
  /**
35
   * The name of the configuration file.
36
   *
37
   * @var string
38
   */
39
  protected string $configFileName = '';
40
41
  /**
42
   * The Output decorator.
43
   *
44
   * @var AuditStyle
45
   */
46
  protected AuditStyle $io;
47
48
  /**
49
   * If set (the default) the config file must be rewritten. Set to false for testing only.
50
   *
51
   * @var bool
52
   */
53
  protected bool $rewriteConfigFile = true;
54
55
  //--------------------------------------------------------------------------------------------------------------------
56
  /**
57
   * Reads configuration parameters from the configuration file.
58
   */
59 31
  public function readConfigFile(): void
60
  {
61 31
    $this->config = new TypedConfig(new Config($this->configFileName));
62 31
    $config       = $this->config->getConfig();
63
64 31
    foreach (self::$sections as $key)
65
    {
66 31
      if (!isset($config[$key]))
67
      {
68 13
        $config[$key] = [];
69
      }
70
    }
71
72 31
    $credentials = $this->config->getOptString('database.credentials');
73 31
    if ($credentials!==null)
74
    {
75 3
      $tmp = new TypedConfig(new Config(dirname($this->configFileName).'/'.$credentials));
76 3
      foreach ($tmp->getManArray('database') as $key => $value)
77
      {
78 3
        $config->set('database.'.$key, $value);
79
      }
80
    }
81 31
  }
82
83
  //--------------------------------------------------------------------------------------------------------------------
84
  /**
85
   * Use for testing only.
86
   *
87
   * @param bool $rewriteConfigFile If true the config file must be rewritten. Otherwise the config must not be
88
   *                                rewritten.
89
   */
90 31
  public function setRewriteConfigFile(bool $rewriteConfigFile)
91
  {
92 31
    $this->rewriteConfigFile = $rewriteConfigFile;
93 31
  }
94
95
  //--------------------------------------------------------------------------------------------------------------------
96
  /**
97
   * Connects to a MySQL instance.
98
   */
99 31
  protected function connect(): void
100
  {
101 31
    $connector = new MySqlDefaultConnector($this->config->getManString('database.host'),
102 31
                                           $this->config->getManString('database.user'),
103 31
                                           $this->config->getManString('database.password'),
104 31
                                           $this->config->getManString('database.data_schema'),
105 31
                                           $this->config->getManInt('database.port', 3306));
106 31
    $dl        = new AuditDataLayer($connector, $this->io);
107 31
    $dl->connect();
108 31
  }
109
110
  //--------------------------------------------------------------------------------------------------------------------
111
  /**
112
   * Rewrites the config file with updated data.
113
   */
114 31
  protected function rewriteConfig(): void
115
  {
116
    // Return immediately when the config file must not be rewritten.
117 31
    if (!$this->rewriteConfigFile)
118
    {
119 12
      return;
120 12
    }
121
122 12
    $tables = $this->config->getManArray('tables');
123 12
    ksort($tables);
124
125 12
    $config           = new Config($this->configFileName);
126 12
    $config['tables'] = $tables;
127
128 12
    $data = [];
129
    foreach (self::$sections as $key)
130 12
    {
131
      if (!empty($config->get($key)))
132
      {
133
        $data[$key] = $config->get($key);
134 12
      }
135 12
    }
136
137
    $this->writeTwoPhases($this->configFileName, json_encode($data, JSON_PRETTY_PRINT));
138
  }
139
140
  //--------------------------------------------------------------------------------------------------------------------
141
  /**
142
   * Writes a file in two phase to the filesystem.
143
   *
144
   * First write the data to a temporary file (in the same directory) and than renames the temporary file. If the file
145
   * already exists and its content is equal to the data that must be written no action  is taken. This has the
146
   * following advantages:
147
   * * In case of some write error (e.g. disk full) the original file is kept in tact and no file with partially data
148
   * is written.
149
   * * Renaming a file is atomic. So, running processes will never read a partially written data.
150
   *
151 12
   * @param string $filename The name of the file were the data must be stored.
152
   * @param string $data     The data that must be written.
153 12
   */
154 12
  protected function writeTwoPhases(string $filename, string $data): void
155
  {
156 12
    $write_flag = true;
157 12
    if (file_exists($filename))
158
    {
159
      $old_data = file_get_contents($filename);
160 12
      if ($data===$old_data)
161
      {
162 11
        $write_flag = false;
163 11
      }
164 11
    }
165
166 11
    if ($write_flag)
167
    {
168
      $tmp_filename = $filename.'.tmp';
169
      file_put_contents($tmp_filename, $data);
170 10
      rename($tmp_filename, $filename);
171
172 12
      $this->io->text(sprintf('Wrote <fso>%s</fso>', OutputFormatter::escape($filename)));
173
    }
174
    else
175
    {
176
      $this->io->text(sprintf('File <fso>%s</fso> is up to date', OutputFormatter::escape($filename)));
177
    }
178
  }
179
180
  //--------------------------------------------------------------------------------------------------------------------
181
}
182
183
//----------------------------------------------------------------------------------------------------------------------
184