Completed
Push — master ( f70784...2818e7 )
by Emmanuel
04:01
created

DB::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
nc 1
cc 1
eloc 3
nop 1
crap 1
1
<?php
2
/**
3
 * Inet Data Anonymization
4
 *
5
 * PHP Version 5.3 -> 7.0
6
 *
7
 * @author Emmanuel Dyan
8
 * @author Rémi Sauvat
9
 * @copyright 2005-2015 iNet Process
10
 *
11
 * @package inetprocess/neuralyzer
12
 *
13
 * @license GNU General Public License v2.0
14
 *
15
 * @link http://www.inetprocess.com
16
 */
17
18
namespace Inet\Neuralyzer\Anonymizer;
19
20
use Inet\Neuralyzer\Exception\InetAnonException;
21
22
/**
23
 * DB Anonymizer
24
 */
25
class DB extends AbstractAnonymizer
26
{
27
    /**
28
     * The PDO connection
29
     *
30
     * @var \PDO
31
     */
32
    private $pdo;
33
34
    /**
35
     * Prepared statement is stored for the update, to make the queries faster
36
     *
37
     * @var \PDOStatement
38
     */
39
    private $preparedStmt;
40
41
42
    /**
43
     * Constructor
44
     *
45
     * @param \PDO $pdo
46
     */
47 15
    public function __construct(\PDO $pdo)
48
    {
49 15
        $this->pdo = $pdo;
50 15
        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
51 15
    }
52
53
    /**
54
     * Process an entity by reading / writing to the DB
55
     *
56
     * @param string        $table
57
     * @param callable|null $callback
58
     * @param bool          $pretend
59
     * @param bool          $returnResult
60
     *
61
     * @return void|array
62
     */
63 13
    public function processEntity($table, $callback = null, $pretend = true, $returnResult = false)
64
    {
65 13
        $queries = array();
66 13
        $actionsOnThatEntity = $this->whatToDoWithEntity($table);
67
68 11
        if ($actionsOnThatEntity & self::TRUNCATE_TABLE) {
69 4
            $where = $this->getWhereConditionInConfig($table);
70 4
            $query = $this->runDelete($table, $where, $pretend);
71 3
            ($returnResult === true ? array_push($queries, $query) : '');
72 3
        }
73
74 10
        if ($actionsOnThatEntity & self::UPDATE_TABLE) {
75
            // I need to read line by line if I have to update the table
76
            // to make sure I do update by update (slower but no other choice for now)
77 9
            $i = 0;
78 9
            $this->preparedStmt = null;
79
80 9
            $key = $this->getPrimaryKey($table);
81 7
            $res = $this->pdo->query("SELECT $key FROM $table");
82 7
            $res->setFetchMode(\PDO::FETCH_ASSOC);
83
84 7
            $this->pdo->beginTransaction();
85
86 7
            while ($row = $res->fetch()) {
87 7
                $val = $row['id'];
88 7
                $data = $this->generateFakeData($table);
89
90 7
                if ($pretend === false) {
91 4
                    $this->runUpdate($table, $data, $key, $val);
92 4
                }
93 7
                ($returnResult === true ? array_push($queries, $this->buildUpdateSQL($table, $data, "$key = '$val'")) : '');
94
95 7
                if (!is_null($callback)) {
96 3
                    $callback(++$i);
97 3
                }
98 7
            }
99
100
            // Commit, even if I am in pretend (that will do ... nothing)
101 7
            $this->pdo->commit();
102 7
        }
103
104 8
        return $queries;
105
    }
106
107
    /**
108
     * Identify the primary key for a table
109
     *
110
     * @param string $table
111
     *
112
     * @return string Field's name
113
     */
114 9
    protected function getPrimaryKey($table)
115
    {
116
        try {
117 9
            $res = $this->pdo->query("SHOW COLUMNS FROM $table WHERE `Key` = 'Pri'");
118 9
        } catch (\Exception $e) {
119 1
            throw new \PDOException('Query Error : ' . $e->getMessage());
120
        }
121
122 8
        $primary = $res->fetchAll(\PDO::FETCH_COLUMN);
123
        // Didn't find a primary key !
124 8
        if (empty($primary)) {
125 1
            throw new InetAnonException("Can't find a primary key for '$table'");
126
        }
127
128 7
        return $primary[0];
129
    }
130
131
    /**
132
     * Execute the Update with PDO
133
     *
134
     * @param string $table    Name of the table
135
     * @param array  $data     Array of fields => value to update the table
136
     * @param string $primaryKey
137
     * @param string $val      Primary Key's Value
138
     */
139 4
    private function runUpdate($table, array $data, $primaryKey, $val)
140
    {
141 4
        if (is_null($this->preparedStmt)) {
142 4
            $this->prepareStmt($table, $primaryKey, array_keys($data));
143 4
        }
144
145 4
        $values = array(":$primaryKey" => $val);
146 4
        foreach ($data as $field => $value) {
147 4
            $values[":$field"] = $value;
148 4
        }
149 4
        $this->preparedStmt->execute($values);
150 4
    }
151
152
    /**
153
     * Prepare the statement if asked
154
     * @param  string $table
155
     * @param  string $primaryKey
156
     * @param  array  $fields
0 ignored issues
show
Documentation introduced by
There is no parameter named $fields. Did you maybe mean $fieldsName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
157
     * @return \PDOStatement
158
     */
159 4
    private function prepareStmt($table, $primaryKey, array $fieldsName)
160
    {
161 4
        foreach ($fieldsName as $field) {
162 4
            $fields[] = "$field = IF($field IS NOT NULL, :$field, NULL)";
0 ignored issues
show
Coding Style Comprehensibility introduced by
$fields was never initialized. Although not strictly required by PHP, it is generally a good practice to add $fields = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
163 4
        }
164 4
        $sql = "UPDATE $table SET " . implode(', ', $fields) . " WHERE $primaryKey = :$primaryKey";
0 ignored issues
show
Bug introduced by
The variable $fields does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
165 4
        $this->preparedStmt = $this->pdo->prepare($sql);
166 4
    }
167
168
    /**
169
     * Execute the Delete with PDO
170
     *
171
     * @param string $table
172
     * @param string $where
173
     * @param bool   $pretend
174
     */
175 4
    private function runDelete($table, $where, $pretend)
176
    {
177 4
        $where = empty($where) ? '' : " WHERE $where";
178 4
        $sql = "DELETE FROM {$table}{$where}";
179
180 4
        if ($pretend === true) {
181 1
            return $sql;
182
        }
183
184
        try {
185 3
            $this->pdo->query($sql);
186 3
        } catch (\Exception $e) {
187 1
            throw new \PDOException('Query Error : ' . $e->getMessage());
188
        }
189
190 2
        return $sql;
191
    }
192
193
    /**
194
     * Build the SQL just for debug
195
     *
196
     * @param string $table
197
     * @param array  $data
198
     * @param string $where
199
     *
200
     * @return string
201
     */
202 6
    private function buildUpdateSQL($table, array $data, $where)
203
    {
204 6
        $fieldsVals = array();
205 6
        foreach ($data as $field => $value) {
206 6
            $fieldsVals[] = "$field = IF($field IS NOT NULL, '" . addslashes($value) . "', NULL)";
207 6
        }
208
209 6
        $sql = "UPDATE $table SET " . implode(', ', $fieldsVals) . " WHERE $where";
210
211 6
        return $sql;
212
    }
213
}
214