Passed
Push — master ( 163405...503637 )
by George
02:38
created

Base::setCsvHeaderColumns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

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 6
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
crap 1
1
<?php
2
namespace JsonTable;
3
4
/**
5
 * @package    JSON table
6
 */
7
abstract class Base
8
{
9
    /**
10
     * @access protected
11
     * @static
12
     *
13
     * @var string Schema JSON
14
     */
15
    protected static $schemaJson;
16
17
    /**
18
     * @access protected
19
     * @static
20
     *
21
     * @var string The path and name of the file to analyse.
22
     */
23
    protected static $fileName;
24
25
    /**
26
     * @access protected
27
     * @static
28
     *
29
     * @var array The columns found in the header.
30
     *                This is used to validate that each row has the correct number of columns
31
     *                and to get the column name from it's position.
32
     */
33
    protected static $headerColumns;
34
35
    /**
36
     * @access protected
37
     * @static
38
     *
39
     * @var object The SplFileObject of the CSV file.
40
     */
41
    protected static $file;
42
43
    /**
44
     * @access protected
45
     *
46
     * @var object The PDO object.
47
     */
48
    public static $pdoConnection;
49
50
51
    /**
52
     * Set the schema.
53
     *
54
     * @access public
55
     *
56
     * @param string $schemaJson The schema conforming to the JSON table schema specification.
57
     * @see http://dataprotocols.org/json-table-schema
58
     *
59
     * @return void
60
     *
61
     * @throws \Exception if the schema is not a valid JSON string.
62
     * @throws \Exception if the schema is an invalid data type.
63
     */
64 7
    public function setSchema($schemaJson)
65
    {
66 7
        if (is_string($schemaJson)) {
67 4
            if (is_null($schemaJson = json_decode($schemaJson))) {
68 1
                throw new \Exception('The schema is not a valid JSON string.');
69
            }
70 6
        } elseif (!is_object($schemaJson)) {
71 3
            throw new \Exception('Invalid schema data type.');
72
        }
73
74 3
        foreach ($schemaJson->fields as &$field) {
75 3
            $field->name = strtolower($field->name);
76 3
        }
77 3
        unset($field);
78
79 3
        self::$schemaJson = $schemaJson;
80 3
    }
81
82
83
    /**
84
     * Set the file.
85
     * This checks that the file exists.
86
     *
87
     * @access  public
88
     *
89
     * @param   string  $fileName    The path and name of the file to analyse.
90
     * @see http://dataprotocols.org/json-table-schema
91
     *
92
     * @return  boolean Whether the file was successfully set.
93
     */
94 9
    public function setFile($fileName)
95
    {
96 9
        if (file_exists($fileName)) {
97 4
            self::$fileName = (string) $fileName;
98 4
            return true;
99
        }
100
101 5
        return false;
102
    }
103
104
105
    /**
106
     * Set the database connection.
107
     *
108
     * @access protected
109
     * @static
110
     *
111
     * @param object $pdoConnection The PDO object.
112
     *
113
     * @return boolean Whether the connection was valid.
114
     */
115 1
    public function setPdoConnection($pdoConnection)
116
    {
117 1
        if ($pdoConnection instanceof \PDO) {
118 1
            self::$pdoConnection = $pdoConnection;
119 1
            return true;
120
        }
121
122
        return false;
123
    }
124
125
126
    /**
127
     * Open a handle to the file to be analysed.
128
     *
129
     * @access public
130
     * @static
131
     *
132
     * @return void
133
     *
134
     * @throws \Exception if a CSV file has not been set.
135
     */
136 3
    protected static function openFile()
137
    {
138 3
        if (empty(self::$fileName)) {
139
            throw new \Exception('CSV file not set.');
140
        }
141
142 3
        self::$file = new \SplFileObject(self::$fileName);
143 3
        self::$file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
144 3
    }
145
146
147
    /**
148
     * Set the CSV header columns from those in the file.
149
     * These are stored in lowercase as all column to schema checking is considered as case insensitive.
150
     *
151
     * @access protected
152
     * @static
153
     *
154
     * @return true on success.
155
     */
156 3
    protected static function setCsvHeaderColumns()
157
    {
158 3
        self::$file->rewind();
159 3
        self::$headerColumns = array_map('strtolower', self::$file->current());
160 3
        return true;
161
    }
162
163
164
    /**
165
     * Rewind the CSV file pointer to the first line of data.
166
     *
167
     * @access protected
168
     * @static
169
     *
170
     * @return void
171
     */
172 1
    protected static function rewindFilePointerToFirstData()
173
    {
174 1
        self::$file->seek(1);
175 1
    }
176
177
178
    /**
179
     * Get the data from the current CSV file row and move the pointer on to the next row.
180
     *
181
     * @access public
182
     * @static
183
     *
184
     * @return array boolean The CSV data or false if the end of the file has been reached.
185
     */
186 1
    protected static function loopThroughFileRows()
187
    {
188 1
        if (self::$file->eof()) {
189
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by JsonTable\Base::loopThroughFileRows of type array.

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...
190
        }
191
192 1
        $csvRow = self::$file->current();
193 1
        self::$file->next();
194
195 1
        return $csvRow;
196
    }
197
198
199
    /**
200
     * Get the key of the field with the specified name from the schema.
201
     * This can be used to validate that a column exists in the schema.
202
     *
203
     * @access protected
204
     *
205
     * @param string $fieldName The field name.
206
     *
207
     * @return int The key ID or false if the field is not found.
208
     */
209 1
    protected function getSchemaKeyFromName($fieldName)
210
    {
211 1
        foreach (self::$schemaJson->fields as $key => $field) {
212 1
            if ($field->name === $fieldName) {
213 1
                return $key;
214
            }
215 1
        }
216
217
        return false;
218
    }
219
220
221
    /**
222
     * Get the position of the field with the specified name from the CSV file.
223
     * This can be used to validate that a column exists in the CSV file.
224
     *
225
     * @access protected
226
     *
227
     * @param string $fieldName The field name.
228
     *
229
     * @return int The position or false if the field is not found.
230
     */
231 1
    protected function getCsvPositionFromName($fieldName)
232
    {
233 1
        return array_search($fieldName, self::$headerColumns);
234
    }
235
236
237
    /**
238
     * Get the schema object for a column, given the columns position in the CSV file.
239
     *
240
     * @access protected
241
     *
242
     * @param int $csvColumnPosition The position of the column in the CSV file.
243
     *
244
     * @return object The schema column.
245
     */
246 1
    protected function getSchemaColumnFromCsvColumnPosition($csvColumnPosition)
247
    {
248 1
        $csvColumnName = self::$headerColumns[$csvColumnPosition];
249 1
        $schemaKey = $this->getSchemaKeyFromName($csvColumnName);
250
251 1
        return self::$schemaJson->fields[$schemaKey];
252
    }
253
254
255
    /**
256
     * Get the type of the specified column.
257
     *
258
     * @access protected
259
     *
260
     * @param object $schemaColumn The schema column object to examine.
261
     *
262
     * @return string The type.
263
     */
264 1
    protected function getColumnType($schemaColumn)
265
    {
266 1
        return (property_exists($schemaColumn, 'type')) ? $schemaColumn->type : 'string';
267
    }
268
269
270
    /**
271
     * Get the format of the specified column.
272
     *
273
     * @access protected
274
     *
275
     * @param object $schemaColumn The schema column object to examine.
276
     *
277
     * @return string The format or null if no format is specified.
278
     */
279 1
    protected function getColumnFormat($schemaColumn)
280
    {
281 1
        return (property_exists($schemaColumn, 'format')) ? $schemaColumn->format : 'default';
282
    }
283
}
284