DatabaseOccurrences::execute()   B
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 42
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 22
nc 6
nop 2
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()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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.');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method warn() does only exist in the following implementations of said interface: Tequilarapido\Cli\EnhancedOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
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 ...');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method info() does only exist in the following implementations of said interface: Tequilarapido\Cli\EnhancedOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method title() does only exist in the following implementations of said interface: Tequilarapido\Cli\EnhancedOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method info() does only exist in the following implementations of said interface: Tequilarapido\Cli\EnhancedOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
136
                $total += $tableTotal;
137
            }
138
        }
139
140
        // No occurrence ?
141
        if ($total == 0)
142
        {
143
            $this->output->info('None.');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method info() does only exist in the following implementations of said interface: Tequilarapido\Cli\EnhancedOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
144
        }
145
146
        // Recap
147
        $this->output->title('Total ');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method title() does only exist in the following implementations of said interface: Tequilarapido\Cli\EnhancedOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
148
        $this->output->success('Total occurrences : ' . $total);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method success() does only exist in the following implementations of said interface: Tequilarapido\Cli\EnhancedOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
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
}