1
|
|
|
<?php namespace Tequilarapido\Cli\Commands; |
2
|
|
|
|
3
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
4
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
5
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
6
|
|
|
use Tequilarapido\Cli\Commands\Base\AbstractDatabaseCommand; |
7
|
|
|
use Tequilarapido\Database\Column; |
8
|
|
|
use Tequilarapido\Helpers\ShellHelper; |
9
|
|
|
use Tequilarapido\PHPSerialized\SearchReplace; |
10
|
|
|
|
11
|
|
|
class DatabaseOccurrences extends AbstractDatabaseCommand |
12
|
|
|
{ |
13
|
|
|
|
14
|
|
|
const ARGUMENT_SEARCH = 'search'; |
15
|
|
|
const ARGUMENT_SEARCH_SEPARATOR = '|'; |
16
|
|
|
|
17
|
|
|
protected $occurrences = array(); |
18
|
|
|
|
19
|
|
View Code Duplication |
protected function configure() |
|
|
|
|
20
|
|
|
{ |
21
|
|
|
parent::configure(); |
22
|
|
|
|
23
|
|
|
$description = ''; |
24
|
|
|
$description .= 'Search string in database (even in serialized objects). ' . PHP_EOL; |
25
|
|
|
$description .= ' '; |
26
|
|
|
|
27
|
|
|
$this |
28
|
|
|
->setName('db:occurrences') |
29
|
|
|
->setDescription($description) |
30
|
|
|
->addArgument( |
31
|
|
|
static::ARGUMENT_SEARCH, |
32
|
|
|
InputArgument::OPTIONAL, |
33
|
|
|
'What to search for ? if not specified, the search items are taken from |
34
|
|
|
config file like for db:replace command. If multiple items, separate them by |', |
35
|
|
|
null |
36
|
|
|
); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
40
|
|
|
{ |
41
|
|
|
parent::execute($input, $output); |
42
|
|
|
|
43
|
|
|
// Search from argument or config ? |
44
|
|
|
if ($search = $input->getArgument(static::ARGUMENT_SEARCH)) { |
45
|
|
|
$replacements = $this->getReplacementsFromSearch($search); |
46
|
|
|
} else { |
47
|
|
|
$replacements = $this->config->getReplacements(); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
if (is_null($replacements)) { |
51
|
|
|
$this->output->warn('There is nothing to search according to configuration.'); |
|
|
|
|
52
|
|
|
return; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
// Setup connection |
56
|
|
|
$this->setup(); |
57
|
|
|
|
58
|
|
|
// Text data columns |
59
|
|
|
$this->output->info('Analysing database : looking for text columns ...'); |
|
|
|
|
60
|
|
|
$column = new Column(); |
61
|
|
|
$excludeTables = $this->config->getExcludeTables(); |
62
|
|
|
$textColumns = $column->scanTextColumns($this->databaseName, $excludeTables); |
63
|
|
|
|
64
|
|
|
// Start progress |
65
|
|
|
$progress = $this->getHelperSet()->get('progress'); |
66
|
|
|
$progress->start($this->output, count($textColumns)); |
67
|
|
|
|
68
|
|
|
// Process |
69
|
|
|
foreach ($textColumns as $tableName => $tableInfos) { |
70
|
|
|
$progress->advance(); |
71
|
|
|
$this->output->writeln(" : Processing $tableName "); |
72
|
|
|
$this->occurrences[$tableName] = $this->getOccurrences($tableName, $tableInfos, $replacements); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
// End progress |
76
|
|
|
$progress->finish(); |
77
|
|
|
|
78
|
|
|
// Display |
79
|
|
|
$this->displayOccurrences(); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
protected function getOccurrences($tableName, $tableInfos, $replacements) |
83
|
|
|
{ |
84
|
|
|
$occurrences = array(); |
85
|
|
|
|
86
|
|
|
// Is there columns ? |
87
|
|
|
if (empty($tableInfos['columns'])) { |
88
|
|
|
return; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
// Iterates in each column and looks for the old string |
92
|
|
|
foreach ($tableInfos['columns'] as $field_name) { |
93
|
|
|
foreach ($replacements as $replacement) { |
94
|
|
|
$searchQuery = $this->searchQuery($tableName, $tableInfos, $field_name, $replacement); |
95
|
|
|
$search_results = $this->db->select($searchQuery); |
96
|
|
|
|
97
|
|
|
if (!isset($occurrences[$replacement->from])) { |
98
|
|
|
$occurrences[$replacement->from] = 0; |
99
|
|
|
} |
100
|
|
|
$occurrences[$replacement->from] += count($search_results); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
// End shell progress |
105
|
|
|
ShellHelper::progressEnd($this->output); |
106
|
|
|
|
107
|
|
|
return $occurrences; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
View Code Duplication |
protected function searchQuery($tableName, $tableInfos, $field_name, $replacement) |
|
|
|
|
111
|
|
|
{ |
112
|
|
|
// table with primary key ? |
113
|
|
|
if (empty($tableInfos['pk'])) { |
114
|
|
|
return sprintf('SELECT `%s` FROM %s WHERE `%s` LIKE "%%%s%%"', $field_name, $tableName, $field_name, $replacement->from); |
115
|
|
|
} else { |
116
|
|
|
return sprintf('SELECT `%s`, ' . $tableInfos['pk'] . ' AS _id FROM %s WHERE `%s` LIKE "%%%s%%"', $field_name, $tableName, $field_name, $replacement->from); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
protected function displayOccurrences() |
121
|
|
|
{ |
122
|
|
|
|
123
|
|
|
$this->output->title('Occurrences by table'); |
|
|
|
|
124
|
|
|
|
125
|
|
|
$total = 0; |
126
|
|
|
foreach ($this->occurrences as $tableName => $tableOccurrences) { |
127
|
|
|
|
128
|
|
|
// Table occurrence |
129
|
|
|
$tableTotal = 0; |
130
|
|
|
foreach ($tableOccurrences as $searchTotal) { |
131
|
|
|
$tableTotal += $searchTotal; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
if ($tableTotal > 0) { |
135
|
|
|
$this->output->info($tableName . ' : ' . $tableTotal); |
|
|
|
|
136
|
|
|
$total += $tableTotal; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
// No occurrence ? |
141
|
|
|
if ($total == 0) |
142
|
|
|
{ |
143
|
|
|
$this->output->info('None.'); |
|
|
|
|
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
// Recap |
147
|
|
|
$this->output->title('Total '); |
|
|
|
|
148
|
|
|
$this->output->success('Total occurrences : ' . $total); |
|
|
|
|
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
private function getReplacementsFromSearch($search) |
152
|
|
|
{ |
153
|
|
|
$items = explode(static::ARGUMENT_SEARCH_SEPARATOR, $search); |
154
|
|
|
$replacements = array(); |
155
|
|
|
|
156
|
|
|
foreach ($items as $item) { |
157
|
|
|
if (!empty($item)) { |
158
|
|
|
$replacement = new \stdClass; |
159
|
|
|
$replacement->from = trim($item); |
160
|
|
|
|
161
|
|
|
$replacements[] = $replacement; |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
return $replacements; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
} |
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.