Passed
Push — master ( e3afe0...6ec87a )
by George
03:01
created

PrimaryKey::createHash()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
namespace JsonTable\Analyse;
3
4
/**
5
 * Perform primary key analysis.
6
 *
7
 * @package JsonTable
8
 */
9
class PrimaryKey extends Analyse implements AnalyseInterface
10
{
11
    /**
12
     * @var string The description for fields with duplicated primary keys.
13
     */
14
    const ERROR_DUPLICATE_PRIMARY_KEY = 'There are <strong>%d</strong> rows that have duplicated primary keys:';
15
16
    /**
17
     * @var array   The current CSV row being analysed.
18
     */
19
    private $currentCsvRow;
20
21
    /**
22
     * @var int The position of the current CSV row row in the CSV file.
23
     */
24
    private $rowNumber;
25
26
    /**
27
     * @var array   The primary keys for every row in the file.
28
     */
29
    private $fileKeys;
30
31
    /**
32
     * @var array   The primary key parts for the current row.
33
     */
34
    private $rowKeyParts;
35
36
    /**
37
     * @var array   The primary key fields.
38
     */
39
    private $primaryKeyFields;
40
41
    /**
42
     * @var string  The name of the primary key field currently being analysed.
43
     */
44
    private $primaryKeyFieldName;
45
46
    /**
47
     * @var string  The hash of the data taken from the primary key fields in the current CSV row.
48
     */
49
    private $hash;
50
51
52
    /**
53
     * Validate that any specified primary key constraints have been met.
54
     *
55
     * @return  boolean Does the data meet the primary key constraints.
56
     *
57
     *
58
     */
59 28
    public function validate()
60
    {
61 28
        if (false === property_exists(parent::$schemaJson, 'primaryKey')) {
62
            return true;
63
        }
64
65 28
        $this->setPrimaryKeyFields();
66 28
        $this->fileKeys = [];
67
68 28
        self::rewindFilePointerToFirstData();
69
70 28
        $this->rowNumber= 1;
71
72 28
        while ($currentCsvRow = parent::loopThroughFileRows()) {
1 ignored issue
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (loopThroughFileRows() instead of validate()). Are you sure this is correct? If so, you might want to change this to $this->loopThroughFileRows().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
73 28
            $this->currentCsvRow = $currentCsvRow;
1 ignored issue
show
Documentation Bug introduced by
It seems like $currentCsvRow can also be of type boolean. However, the property $currentCsvRow is declared as type array. 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...
74 28
            $this->getPrimaryKeyDataForRow();
75 28
            $this->createHash();
76
77 28
            if ($existingKey = $this->isHashUnique()) {
78
                $this->handleDuplicateHash($existingKey);
79
80
                if ($this->stopIfInvalid) {
81
                    return false;
82
                }
83
            }
84
85 28
            $this->fileKeys[$this->rowNumber] = $this->hash;
86 28
            $this->rowNumber++;
87 28
        }
88
89 28
        return true;
90
    }
91
92
93
    /**
94
     * Set the primary key fields.
95
     *
96
     * @return  void
97
     */
98 28
    private function setPrimaryKeyFields()
99
    {
100 28
        $this->primaryKeyFields = (array) parent::$schemaJson->primaryKey;
101 28
    }
102
103
104
    /**
105
     * Check that there is a column in the JSON table schema file for the current primary key field.
106
     *
107
     * @return  void
108
     *
109
     * @throws  \Exception if the primary key was not in the schema file.
110
     */
111 28
    private function checkColumnExistsInSchema()
112
    {
113 28
        if (false === $this->getSchemaKeyFromName($this->primaryKeyFieldName)) {
114
            throw new \Exception("The primary key &quot;$this->primaryKeyFieldName&quot; was not in the file.
115
                    Primary key columns should be set as required.");
116
        }
117 28
    }
118
119
120
    /**
121
     * Get the data in the CSV column for the current primary key column.
122
     *
123
     * @return  string  The data in the column.
124
     */
125 28
    private function csvDataForPrimaryKeyColumn()
126
    {
127 28
        $csvPosition = $this->getCsvPositionFromName($this->primaryKeyFieldName);
128 28
        return $this->currentCsvRow[$csvPosition];
129
    }
130
131
132
    /**
133
     * Get the data in the primary key columns for the current CSV row.
134
     *
135
     * @return  void
136
     */
137 28
    private function getPrimaryKeyDataForRow()
138
    {
139 28
        $this->rowKeyParts = [];
140
141 28
        foreach ($this->primaryKeyFields as $fieldName) {
142 28
            $this->primaryKeyFieldName = strtolower($fieldName);
143 28
            $this->checkColumnExistsInSchema();
144 28
            $this->rowKeyParts[] = $this->csvDataForPrimaryKeyColumn();
145 28
        }
146 28
    }
147
148
149
    /**
150
     * Create a hash of the data taken from the primary key fields in the current CSV row.
151
     *
152
     * @return  void
153
     */
154 28
    private function createHash()
155
    {
156 28
        $this->hash = implode(', ', $this->rowKeyParts);
157 28
    }
158
159
160
    /**
161
     * Check whether the current hash has already been created for this file.
162
     *
163
     * @return  boolean|int False if this row's primary key hash is unique
164
     *                      or the number of the row with the same hash if it's not.
165
     */
166 28
    private function isHashUnique()
167
    {
168 28
        return array_search($this->hash, $this->fileKeys);
169
    }
170
171
172
    /**
173
     * Handle the current hash not being unique.
174
     *
175
     * @param   int $existingKey    The number of the row with the same hash.
176
     *
177
     * @return  void
178
     */
179
    private function handleDuplicateHash($existingKey)
180
    {
181
        $primaryKeyColumns = implode(', ', $this->primaryKeyFields);
182
        $errorMessage = "The data in columns &quot;$primaryKeyColumns&quot; should be unique,
183
                but rows $existingKey &amp; $this->rowNumber have the same values of &quot;$this->hash&quot;";
184
185
        $this->error->setError(self::ERROR_DUPLICATE_PRIMARY_KEY, $errorMessage);
186
        $this->statistics->setErrorRow($this->rowNumber);
187
    }
188
}