|
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
|
|
|
* 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
|
|
|
private $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
|
|
|
protected static function getSetting($settings, $mandatory, $sectionName, $settingName) |
|
60
|
|
|
{ |
|
61
|
|
|
// Test if the section exists. |
|
62
|
|
View Code Duplication |
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
|
|
View Code Duplication |
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
|
|
|
return $settings[$sectionName][$settingName]; |
|
90
|
|
|
} |
|
91
|
|
|
|
|
92
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
93
|
|
|
/** |
|
94
|
|
|
* Reads configuration parameters from the configuration file. |
|
95
|
|
|
*/ |
|
96
|
|
|
public function readConfigFile() |
|
97
|
|
|
{ |
|
98
|
|
|
$content = file_get_contents($this->configFileName); |
|
99
|
|
|
|
|
100
|
|
|
$this->config = (array)json_decode($content, true); |
|
101
|
|
|
if (json_last_error()!=JSON_ERROR_NONE) |
|
102
|
|
|
{ |
|
103
|
|
|
throw new RuntimeException("Error decoding JSON: '%s'.", json_last_error_msg()); |
|
104
|
|
|
} |
|
105
|
|
|
|
|
106
|
|
|
if (!is_array($this->config['audit_columns'])) |
|
107
|
|
|
{ |
|
108
|
|
|
$this->config['audit_columns'] = []; |
|
109
|
|
|
} |
|
110
|
|
|
|
|
111
|
|
|
if (!is_array($this->config['additional_sql'])) |
|
112
|
|
|
{ |
|
113
|
|
|
$this->config['additional_sql'] = []; |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
if (!is_array($this->config['tables'])) |
|
117
|
|
|
{ |
|
118
|
|
|
$this->config['tables'] = []; |
|
119
|
|
|
} |
|
120
|
|
|
|
|
121
|
|
|
foreach ($this->config['tables'] as $table_name => $params) |
|
122
|
|
|
{ |
|
123
|
|
|
$this->config['tables'][$table_name]['audit'] = filter_var($params['audit'], FILTER_VALIDATE_BOOLEAN); |
|
124
|
|
|
} |
|
125
|
|
|
} |
|
126
|
|
|
|
|
127
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
128
|
|
|
/** |
|
129
|
|
|
* Use for testing only. |
|
130
|
|
|
* |
|
131
|
|
|
* @param bool $rewriteConfigFile If true the config file must be rewritten. Otherwise the config must not be |
|
132
|
|
|
* rewritten. |
|
133
|
|
|
*/ |
|
134
|
|
|
public function setRewriteConfigFile($rewriteConfigFile) |
|
135
|
|
|
{ |
|
136
|
|
|
$this->rewriteConfigFile = $rewriteConfigFile; |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
140
|
|
|
/** |
|
141
|
|
|
* Rewrites the config file with updated data. |
|
142
|
|
|
*/ |
|
143
|
|
|
protected function rewriteConfig() |
|
144
|
|
|
{ |
|
145
|
|
|
// Return immediately when the config file must not be rewritten. |
|
146
|
|
|
if (!$this->rewriteConfigFile) return; |
|
147
|
|
|
|
|
148
|
|
|
$this->writeTwoPhases($this->configFileName, json_encode($this->config, JSON_PRETTY_PRINT)); |
|
149
|
|
|
} |
|
150
|
|
|
|
|
151
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
152
|
|
|
/** |
|
153
|
|
|
* Writes a file in two phase to the filesystem. |
|
154
|
|
|
* |
|
155
|
|
|
* First write the data to a temporary file (in the same directory) and than renames the temporary file. If the file |
|
156
|
|
|
* already exists and its content is equal to the data that must be written no action is taken. This has the |
|
157
|
|
|
* following advantages: |
|
158
|
|
|
* * In case of some write error (e.g. disk full) the original file is kept in tact and no file with partially data |
|
159
|
|
|
* is written. |
|
160
|
|
|
* * Renaming a file is atomic. So, running processes will never read a partially written data. |
|
161
|
|
|
* |
|
162
|
|
|
* @param string $filename The name of the file were the data must be stored. |
|
163
|
|
|
* @param string $data The data that must be written. |
|
164
|
|
|
*/ |
|
165
|
|
|
protected function writeTwoPhases($filename, $data) |
|
166
|
|
|
{ |
|
167
|
|
|
$write_flag = true; |
|
168
|
|
|
if (file_exists($filename)) |
|
169
|
|
|
{ |
|
170
|
|
|
$old_data = file_get_contents($filename); |
|
171
|
|
|
if ($data==$old_data) $write_flag = false; |
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
|
|
if ($write_flag) |
|
175
|
|
|
{ |
|
176
|
|
|
$tmp_filename = $filename.'.tmp'; |
|
177
|
|
|
file_put_contents($tmp_filename, $data); |
|
178
|
|
|
rename($tmp_filename, $filename); |
|
179
|
|
|
|
|
180
|
|
|
$this->io->text(sprintf('Wrote <fso>%s</fso>', OutputFormatter::escape($filename))); |
|
181
|
|
|
} |
|
182
|
|
|
else |
|
183
|
|
|
{ |
|
184
|
|
|
$this->io->text(sprintf('File <fso>%s</fso> is up to date', OutputFormatter::escape($filename))); |
|
185
|
|
|
} |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
189
|
|
|
} |
|
190
|
|
|
|
|
191
|
|
|
//---------------------------------------------------------------------------------------------------------------------- |
|
192
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.