Completed
Push — master ( b5c840...3d5461 )
by Marcus
04:46
created

MysqlVariablesLoader::getVariableScope()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Loaders\MySqlVariablesLoader
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2019 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Loaders;
22
23
use TechDivision\Import\Connection\ConnectionInterface;
24
use TechDivision\Import\Configuration\SubjectConfigurationInterface;
25
26
/**
27
 * Loader for attribute sets.
28
 *
29
 * @author    Tim Wagner <[email protected]>
30
 * @copyright 2019 TechDivision GmbH <[email protected]>
31
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
32
 * @link      https://github.com/techdivision/import
33
 * @link      http://www.techdivision.com
34
 */
35
class MysqlVariablesLoader implements LoaderInterface
36
{
37
38
    /**
39
     * The key for the variable name.
40
     *
41
     * @var string
42
     */
43
    const VARIABLE_NAME = 'Variable_name';
44
45
    /**
46
     * The key for the variable value.
47
     *
48
     * @var string
49
     */
50
    const VALUE = 'Value';
51
52
    /**
53
     * The connection instance.
54
     *
55
     * @var \TechDivision\Import\Connection\ConnectionInterface
56
     */
57
    protected $connection;
58
59
    /**
60
     * The possible variable scopes.
61
     *
62
     * @var array
63
     */
64
    protected $variableScopes = array('GLOBAL', 'SESSION');
65
66
    /**
67
     * The MySQL variable scope (one of GLOBAL or SESSION).
68
     *
69
     * @var string
70
     */
71
    protected $variableScope;
72
73
    /**
74
     * The MySQL variable name.
75
     *
76
     * @var string
77
     */
78
    protected $variableName;
79
80
    /**
81
     * The recommendations for critically MySQL variables.
82
     *
83
     * @var array
84
     */
85
    protected $recommendations = array(
86
        'innodb_flush_log_at_trx_commit' => array(
87
            'severity' => array(0 => 0, 1 => 2, 2 => 0),
88
            'description' => 'Your setting for may result in a significantly slower performance. Consider to switch this value to 0 or 2 to improve performance. Read more about that topic on the MySQL website https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit. '
89
        )
90
    );
91
92
    /**
93
     * The available severity mappings.
94
     *
95
     * @var array
96
     */
97
    protected $severityMappings = array(
98
        0 => 'white',
99
        1 => 'yellow',
100
        2 => 'red'
101
    );
102
103
    /**
104
     * Initializes the event.
105
     *
106
     * @param \TechDivision\Import\Connection\ConnectionInterface $connection    The connection instance
107
     * @param string                                              $variableScope The MySQL variable scope
108
     * @param string                                              $variableName  The MySQL variable name
109
     */
110
    public function __construct(ConnectionInterface $connection, $variableScope = 'GLOBAL', $variableName = 'innodb%')
111
    {
112
113
        // set the connection and the variable name
114
        $this->connection = $connection;
115
116
        // set the variable name and scope
117
        $this->setVariableName($variableName);
118
        $this->setVariableScope($variableScope);
119
    }
120
121
    /**
122
     * Loads and returns data the custom validation data.
123
     *
124
     * @param \TechDivision\Import\Configuration\ParamsConfigurationInterface $configuration The configuration instance to load the validations from
125
     *
126
     * @return \ArrayAccess The array with the data
127
     */
128
    public function load(SubjectConfigurationInterface $configuration = null)
129
    {
130
131
        // initialize the array for the rows
132
        $rows = array();
133
134
        // prepare the statement
135
        $statement = sprintf("SHOW %s VARIABLES LIKE %s", $this->getVariableScope(), $this->getVariableName());
136
137
        // load and assemble the row
138
        foreach ($this->getConnection()->query($statement) as $value) {
0 ignored issues
show
Bug introduced by
The expression $this->getConnection()->query($statement) of type object<PDOStatement>|boolean 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...
139
            if ($row = $this->prepareRow($value)) {
140
                $rows[] = $row;
141
            }
142
        }
143
144
        // return the rows
145
        return $rows;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $rows; (array) is incompatible with the return type declared by the interface TechDivision\Import\Loaders\LoaderInterface::load of type ArrayAccess.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
146
    }
147
148
    /**
149
     * Initialize the arary with the table row.
150
     *
151
     * @param array $value The status variable loaded from the DB
152
     *
153
     * @return string[] The array with the row ready to be rendered
154
     */
155
    protected function prepareRow(array $value)
156
    {
157
158
        // query whether or not a recommendation for the variable is available
159
        if (isset($this->recommendations[$variableName = $value[self::VARIABLE_NAME]])) {
160
            // if yes, load the recommendation
161
            $recommendation = $this->recommendations[$variableName];
162
            // load the severity of the recommendation
163
            $severity = $recommendation['severity'][$val = $value[self::VALUE]];
164
            // if severity is at least on a warning level
165
            if ($severity >= 1) {
166
                // format the value
167
                $mappedValue = sprintf('<bg=%s>%s</>', $this->severityMappings[$severity], $val);
168
                // return the row with the formatted value
169
                return array(
170
                    'Variable'    => $variableName,
171
                    'Value'       => $mappedValue,
172
                    'Description' => $recommendation['description']
173
                );
174
            }
175
        }
176
    }
177
178
    /**
179
     * Set's the MySQL variable name.
180
     *
181
     * @param string $variableName The MySQL variable name
182
     *
183
     * @return void
184
     */
185
    protected function setVariableName($variableName)
186
    {
187
        $this->variableName = $this->getConnection()->quote($variableName);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getConnection()->quote($variableName) can also be of type boolean. However, the property $variableName is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
188
    }
189
190
    /**
191
     * Return's the quoted MySQL variable name.
192
     *
193
     * @return string The MySQL variable name
194
     */
195
    protected function getVariableName()
196
    {
197
        return $this->variableName;
198
    }
199
200
    /**
201
     * Set's the the MySQL variable scope.
202
     *
203
     * @param string $variableScope The MySQL variable scope to use
204
     *
205
     * @return void
206
     * @throws \InvalidArgumentException Is thrown if the passed variable is invalid
207
     */
208
    protected function setVariableScope($variableScope)
209
    {
210
        if (in_array($variableScope, $this->variableScopes)) {
211
            $this->variableScope = $variableScope;
212
        } else {
213
            throw new \InvalidArgumentException(
214
                sprintf(
215
                    'Invalid argument %s passed (required one of %s',
216
                    $variableScope,
217
                    implode(', ', $this->variableScopes)
218
                )
219
            );
220
        }
221
    }
222
223
    /**
224
     * Return's the MySQL variable scope.
225
     *
226
     * @return string The MySQL variable scope
227
     */
228
    protected function getVariableScope()
229
    {
230
        return $this->variableScope;
231
    }
232
233
    /**
234
     * Returns the coonection instance.
235
     *
236
     * @return \TechDivision\Import\Connection\ConnectionInterface The connection instance
237
     */
238
    protected function getConnection()
239
    {
240
        return $this->connection;
241
    }
242
}
243