GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 107e1c...3ac338 )
by Vadim
08:13
created

Migration::executeInPerconaTool()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 8.6737
c 0
b 0
f 0
cc 5
eloc 17
nc 3
nop 2
1
<?php
2
3
namespace ConsoleTools\Model;
4
5
use Zend\Db\Adapter\Adapter;
6
use Zend\Db\Sql\Expression;
7
use Zend\Db\Sql\Sql;
8
use Zend\Db\Sql\Ddl;
9
use Zend\Console\Prompt\Confirm;
10
use Zend\ServiceManager\ServiceLocatorAwareTrait;
11
use Zend\ServiceManager\ServiceLocatorInterface;
12
13
/**
14
 * Generate class and methods for new migration
15
 *
16
 * @author     V.Leontiev <[email protected]>
17
 * @license    http://opensource.org/licenses/MIT MIT
18
 * @since      php 5.3 or higher
19
 * @see        https://github.com/newage/console-tools
20
 */
21
class Migration
22
{
23
    use ServiceLocatorAwareTrait;
24
25
    /**
26
     * Current db adapter
27
     *
28
     * @var Adapter
29
     */
30
    protected $adapter = null;
31
32
    /**
33
     * @var bool
34
     */
35
    protected $percona = false;
36
37
    /**
38
     * Migration table name of database
39
     *
40
     * @var string
41
     */
42
    const TABLE = 'migrations';
43
44
    /**
45
     * Constructor
46
     * Create migration table
47
     * Set current db adapter
48
     *
49
     * @param \Zend\Db\Adapter\Adapter $adapter
50
     */
51
    public function __construct($adapter = null, ServiceLocatorInterface $serviceLocator, $percona)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
52
    {
53
        $this->adapter = $adapter;
54
        $this->percona = $percona;
55
        $this->createTable();
56
        $this->setServiceLocator($serviceLocator);
57
    }
58
59
    /**
60
     * Create a migration table
61
     *
62
     * @return bool
63
     */
64
    public function createTable()
65
    {
66
        $sql = new Sql($this->adapter);
67
68
        try {
69
            $select = $sql->select(self::TABLE);
70
            $queryString = $sql->getSqlStringForSqlObject($select);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
71
            $this->adapter->query($queryString, Adapter::QUERY_MODE_EXECUTE);
72
        } catch (\Exception $err) {
73
            $table = new Ddl\CreateTable(self::TABLE);
74
            $table->addColumn(new Ddl\Column\Integer('id'));
75
            $table->addColumn(new Ddl\Column\Char('migration', 255));
76
            $table->addColumn(new Ddl\Column\Text('up'));
77
            $table->addColumn(new Ddl\Column\Text('down'));
78
            $table->addColumn(new Ddl\Column\Integer('ignored', false, 0, array('length' => 1)));
79
80
            $table->addConstraint(new Ddl\Constraint\PrimaryKey('id'));
81
            $table->addConstraint(new Ddl\Constraint\UniqueKey(['migration'], 'unique_key'));
82
83
            $queryString = $sql->getSqlStringForSqlObject($table);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
84
            $this->adapter->query($queryString, Adapter::QUERY_MODE_EXECUTE);
85
        }
86
    }
87
88
    /**
89
     * Get a last migration
90
     *
91
     * @return string
92
     */
93
    public function last()
94
    {
95
        $sql = new Sql($this->adapter);
96
        $select = $sql->select(self::TABLE);
97
        $select->columns(array(
98
            'last' => new Expression('MAX(id)'),
99
            'up',
100
            'down',
101
            'ignored'
102
        ));
103
104
        $selectString = $sql->getSqlStringForSqlObject($select);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
105
        $results = $this->adapter->query($selectString, Adapter::QUERY_MODE_EXECUTE);
106
107
        return $results->current();
0 ignored issues
show
Bug introduced by
The method current does only exist in Zend\Db\Adapter\Driver\ResultInterface, but not in Zend\Db\Adapter\Driver\S...tSet\ResultSetInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
108
    }
109
    /**
110
     * Get a last migration
111
     *
112
     * @param bool $isShow Show sql queries
0 ignored issues
show
Bug introduced by
There is no parameter named $isShow. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
113
     * @return string
114
     */
115
    public function get(array $where = [])
116
    {
117
        $sql = new Sql($this->adapter);
118
        $select = $sql->select(self::TABLE);
119
        $select->columns(array('*'));
120
        $select->where($where);
121
122
        $selectString = $sql->getSqlStringForSqlObject($select);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
123
        $results = $this->adapter->query($selectString, Adapter::QUERY_MODE_EXECUTE);
124
125
        return $results->current();
0 ignored issues
show
Bug introduced by
The method current does only exist in Zend\Db\Adapter\Driver\ResultInterface, but not in Zend\Db\Adapter\Driver\S...tSet\ResultSetInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
126
    }
127
128
    /**
129
     * Get applied a migrations name
130
     *
131
     * @return array
132
     */
133
    public function applied()
134
    {
135
        $migrationFiles = array();
136
        $sql = new Sql($this->adapter);
137
        $select = $sql->select();
138
        $select->from(self::TABLE);
139
140
        $selectString = $sql->getSqlStringForSqlObject($select);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
141
        $results = $this->adapter->query($selectString, Adapter::QUERY_MODE_EXECUTE);
142
143
        if ($results->count() > 0) {
0 ignored issues
show
Bug introduced by
The method count does only exist in Zend\Db\Adapter\Driver\R...tSet\ResultSetInterface, but not in Zend\Db\Adapter\Driver\StatementInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
144
            foreach ($results as $migration) {
0 ignored issues
show
Bug introduced by
The expression $results of type object<Zend\Db\Adapter\D...Driver\ResultInterface> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
145
                $migrationFiles[] = $migration->migration;
146
            }
147
        }
148
149
        return $migrationFiles;
150
    }
151
152
    /**
153
     * Apply up on the migration
154
     * And insert migration name to base
155
     *
156
     * @param string $migrationName
157
     * @param array $migrationArray
158
     * @param bool $ignoreMigration
159
     * @param bool $doNotSaveAsExecuted
160
     * @throws \Exception
161
     * @internal param bool $ig
162
     */
163
    public function upgrade($migrationName, array $migrationArray, $ignoreMigration = false, $doNotSaveAsExecuted = false)
164
    {
165
        $connection = $this->adapter->getDriver()->getConnection();
166
        $connection->beginTransaction();
167
168
        try {
169
            if (!$ignoreMigration) {
170
                $this->executeQueriesOneByOne($migrationArray['up']);
171
            }
172
            if (!$doNotSaveAsExecuted) {
173
                $sql = new Sql($this->adapter);
174
                $insert = $sql->insert(self::TABLE);
175
                $insert->values(array(
176
                    'migration' => $migrationName,
177
                    'up' => $migrationArray['up'],
178
                    'down' => $migrationArray['down'],
179
                    'ignore' => (int)$ignoreMigration,
180
                ));
181
                $queryString = $sql->getSqlStringForSqlObject($insert);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
182
                $this->adapter->query($queryString, Adapter::QUERY_MODE_EXECUTE);
183
            }
184
185
            $connection->commit();
186
        } catch (\Exception $exception) {
187
            $connection->rollback();
188
            throw new \Exception($exception->getMessage());
189
        }
190
    }
191
192
    /**
193
     * Apply down on the migration
194
     * And remove migration name from base
195
     *
196
     * @param string $migrationName
197
     * @param array $migrationArray
198
     * @param bool $ignore
199
     * @throws \Exception
200
     */
201
    public function downgrade($migrationName, array $migrationArray, $ignore = false)
202
    {
203
        $connection = $this->adapter->getDriver()->getConnection();
204
205
        try {
206
            $connection->beginTransaction();
207
208
            if (!$ignore) {
209
                $this->executeQueriesOneByOne($migrationArray['down']);
210
            }
211
212
            $sql = new Sql($this->adapter);
213
            $delete = $sql->delete(self::TABLE);
214
            $delete->where(array('migration' => $migrationName));
215
216
            $queryString = $sql->getSqlStringForSqlObject($delete);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
217
            $this->adapter->query($queryString, Adapter::QUERY_MODE_EXECUTE);
218
219
            $connection->commit();
220
        } catch(\Exception $exception) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after CATCH keyword; 0 found
Loading history...
221
            $connection->rollback();
222
            throw new \Exception($exception->getMessage());
223
        }
224
    }
225
226
    /**
227
     * @param string $migration
228
     */
229
    protected function executeQueriesOneByOne($migration = '')
230
    {
231
        $config = $this->getServiceLocator()->get('config');
232
        if (!isset($config['db'])) {
233
            throw new \RuntimeException('Does nto exist `db` config!');
234
        }
235
        $dbConfig = $config['db'];
236
237
        $queries = explode(';' . PHP_EOL, $migration);
238
        foreach ($queries as $query) {
239
            $query = trim($query);
240
            if (!empty($query)) {
241
                if (Confirm::prompt($query . PHP_EOL . 'Run this query? [y/n]', 'y', 'n')) {
242
                    if ($this->executeInPerconaTool($query, $dbConfig) === false) {
243
                        $this->adapter->query($query, Adapter::QUERY_MODE_EXECUTE);
244
                    }
245
                } elseif (Confirm::prompt('Break execution and ROLLBACK? [y/n]', 'y', 'n')) {
246
                    $connection = $this->adapter->getDriver()->getConnection();
247
                    $connection->rollback();
248
                    exit;
249
                }
250
            }
251
        }
252
    }
253
254
    protected function executeInPerconaTool($query, $dbConfig): bool
255
    {
256
        if (stristr($query, 'ALTER TABLE') && $this->percona) {
257
            $cleanQuery = str_replace(['`', '\''], '', $query);
258
            preg_match('~ALTER TABLE\s([\w\d\_]+)\s(.*);?~smi', $cleanQuery, $matches);
259
            if (!isset($matches[1]) || !isset($matches[2])) {
260
                return false;
261
            }
262
            $perconaString = sprintf(
263
                'pt-online-schema-change --execute --alter-foreign-keys-method=auto --password=%1$s --user=%2$s --alter "%6$s" D=%3$s,t=%5$s,h=%4$s',
264
                $dbConfig['password'],
265
                $dbConfig['username'],
266
                $dbConfig['database'],
267
                $dbConfig['hostname'],
268
                $matches[1],
269
                $matches[2]
270
            );
271
            $result = exec($perconaString);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
272
            return true;
273
        }
274
        return false;
275
    }
276
}
277