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
Pull Request — master (#22)
by Bart
20:52 queued 14:21
created

ExportService::parseFields()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 25
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

Changes 13
Bugs 2 Features 5
Metric Value
c 13
b 2
f 5
dl 0
loc 25
ccs 12
cts 12
cp 1
rs 8.439
cc 6
eloc 9
nc 6
nop 2
crap 6
1
<?php
2
3
namespace Craft;
4
5
/**
6
 * Export service.
7
 *
8
 * Handles common export logics.
9
 *
10
 * @author    Bob Olde Hampsink <[email protected]>
11
 * @copyright Copyright (c) 2015, Bob Olde Hampsink
12
 * @license   http://buildwithcraft.com/license Craft License Agreement
13
 *
14
 * @link      http://github.com/boboldehampsink
15
 */
16
class ExportService extends BaseApplicationComponent
17
{
18
    /**
19
     * Contains the working export service's name.
20
     *
21
     * @var IExportElementType
22
     */
23
    private $_service;
24
25
    /**
26
     * Saves an export map to the database.
27
     *
28
     * @param array $settings
29
     * @param array $map
30
     */
31 2
    public function saveMap(array $settings, array $map)
32
    {
33
        // Unset non-map settings
34 2
        unset($settings['limit'], $settings['offset']);
35 2
        ksort($settings);
36
37
        // Set criteria
38 2
        $criteria = new \CDbCriteria();
39 2
        $criteria->condition = 'settings = :settings';
40 2
        $criteria->params = array(
41 2
            ':settings' => JsonHelper::encode($settings),
42
        );
43
44
        // Check if we have a map already
45 2
        $mapRecord = $this->findMap($criteria);
46
47 2
        if (!count($mapRecord) || $mapRecord->settings != $settings) {
48
49
            // Save settings and map to database
50 1
            $mapRecord = $this->getNewMap();
51 1
            $mapRecord->settings = $settings;
52 1
        }
53
54
        // Save new map to db
55 2
        $mapRecord->map = $map;
56 2
        $mapRecord->save(false);
57 2
    }
58
59
    /**
60
     * @codeCoverageIgnore
61
     * @param \CDbCriteria $criteria
62
     * @return Export_MapRecord|array|null
63
     */
64
    public function findMap(\CDbCriteria $criteria)
65
    {
66
        return Export_MapRecord::model()->find($criteria);
67
    }
68
69
    /**
70
     * @codeCoverageIgnore
71
     * @return Export_MapRecord
72
     */
73
    protected function getNewMap()
74
    {
75
        return new Export_MapRecord();
76
    }
77
78
    /**
79
     * Download the export csv.
80
     *
81
     * @param array $settings
82
     * @return string
83
     * @throws Exception
84
     */
85 7
    public function download(array $settings)
86
    {
87
        // Get max power
88 7
        craft()->config->maxPowerCaptain();
89
90
        // Check what service we're gonna need
91 7
        if (!($this->_service = $this->getService($settings['type']))) {
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getService($settings['type']) can also be of type boolean. However, the property $_service is declared as type object<Craft\IExportElementType>. 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...
92 1
            throw new Exception(Craft::t('Unknown Element Type Service called.'));
93
        }
94
95
        // Get delimiter
96 6
        $delimiter = craft()->plugins->callFirst('registerExportCsvDelimiter');
97 6
        $delimiter = is_null($delimiter) ? ',' : $delimiter;
98
99
        // Open output buffer
100 6
        ob_start();
101
102
        // Write to output stream
103 6
        $export = fopen('php://output', 'w');
104
105
        // Get data
106 6
        $data = $this->getData($settings);
107
108
        // If there is data, process
109 6
        if (count($data)) {
110
111
            // Put down columns
112 3
            fputcsv($export, $this->parseColumns($settings), $delimiter);
113
114
            // Loop trough data
115 3
            foreach ($data as $element) {
0 ignored issues
show
Bug introduced by
The expression $data of type array|string 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...
116
117
                // Fetch element in case of element id
118 3
                if (is_numeric($element)) {
119 2
                    $element = craft()->elements->getElementById($element, $settings['type']);
120 2
                }
121
122
                // Get fields
123 3
                $fields = $this->parseFields($settings, $element);
124
125
                // Gather row data
126 3
                $rows = array();
127
128
                // Loop trough the fields
129 3
                foreach ($fields as $handle => $data) {
130
131
                    // Parse element data
132 3
                    $data = $this->parseElementData($handle, $data);
133
134
                    // Parse field data
135 3
                    $data = $this->parseFieldData($handle, $data);
136
137
                    // Encode and add to rows
138 2
                    $rows[] = StringHelper::convertToUTF8($data);
139 2
                }
140
141
                // Add rows to export
142 2
                fputcsv($export, $rows, $delimiter);
143 2
            }
144 2
        }
145
146
        // Close buffer and return data
147 5
        fclose($export);
148 5
        $data = ob_get_clean();
149
150
        // Use windows friendly newlines
151 5
        $data = str_replace("\n", "\r\n", $data);
152
153
        // Return the data to controller
154 5
        return $data;
155
    }
156
157
    /**
158
     * Get service to use for exporting.
159
     *
160
     * @param string $elementType
161
     *
162
     * @return object|bool
163
     */
164 4
    public function getService($elementType)
165
    {
166
        // Check if there's a service for this element type elsewhere
167 4
        $service = craft()->plugins->callFirst('registerExportService', array(
168 4
            'elementType' => $elementType,
169 4
        ));
170
171
        // If not, do internal check
172 4
        if ($service == null) {
173
174
            // Get from right elementType
175 4
            $service = 'export_'.strtolower($elementType);
176 4
        }
177
178
        // Check if elementtype can be imported
179 4
        if (isset(craft()->$service) && craft()->$service instanceof IExportElementType) {
180
181
            // Return this service
182 3
            return craft()->$service;
183
        }
184
185 1
        return false;
186
    }
187
188
    /**
189
     * Get data from sources.
190
     *
191
     * @param array $settings
192
     *
193
     * @return array
194
     */
195 6
    protected function getData(array $settings)
196
    {
197
        // Get other sources
198 6
        $sources = craft()->plugins->call('registerExportSource', array($settings));
199
200
        // Loop through sources, see if we can get any data
201 6
        $data = array();
202 6
        foreach ($sources as $plugin) {
203 1
            if (is_array($plugin)) {
204 1
                foreach ($plugin as $source) {
205 1
                    $data[] = $source;
206 1
                }
207 1
            }
208 6
        }
209
210
        // Cut up data from source
211 6
        if (array_key_exists('offset', $settings)) {
212 2
            $data = array_slice($data, $settings['offset'], $settings['limit']);
213 2
        }
214
215
        // If no data from source, get data by ourselves
216 6
        if (!count($data)) {
217
218
            // Find data
219 5
            $criteria = $this->_service->setCriteria($settings);
220
221
            // Gather element ids
222 5
            $data = $criteria->ids();
223 5
        }
224
225 6
        return $data;
226
    }
227
228
    /**
229
     * Parse fields.
230
     *
231
     * @param array $settings
232
     * @param       $element
233
     *
234
     * @return array
235
     */
236 3
    protected function parseFields(array $settings, $element)
237
    {
238 3
        $fields = array();
239
240
        // Only get element attributes and content attributes
241 3
        $attributes = $element;
242 3
        if ($element instanceof BaseElementModel) {
1 ignored issue
show
Bug introduced by
The class Craft\BaseElementModel does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
243
244
            // Get service
245 2
            $attributes = $this->_service->getAttributes($settings['map'], $element);
246 2
        }
247
248
        // Loop through the map
249 3
        foreach ($settings['map'] as $handle => $data) {
250
251
            // Only get checked fields
252 3
            if ($data['checked'] == '1' && (array_key_exists($handle, $attributes) || array_key_exists(substr($handle, 0, 5), $attributes))) {
253
254
                // Fill them with data
255 3
                $fields[$handle] = $attributes[$handle];
256 3
            }
257 3
        }
258
259 3
        return $fields;
260
    }
261
262
    /**
263
     * Parse column names.
264
     *
265
     * @param array $settings [description]
266
     *
267
     * @return string
268
     */
269 3
    protected function parseColumns(array $settings)
270
    {
271 3
        $columns = array();
272
273
        // Loop trough map
274 3
        foreach ($settings['map'] as $handle => $data) {
275
276
            // If checked
277 3
            if ($data['checked'] == 1) {
278
279
                // Add column
280 3
                $columns[] = StringHelper::convertToUTF8($data['label']);
281 3
            }
282 3
        }
283
284 3
        return $columns;
285
    }
286
287
    /**
288
     * Parse reserved element values.
289
     *
290
     * @param  $handle
291
     * @param  $data
292
     *
293
     * @return string
294
     */
295 3
    protected function parseElementData($handle, $data)
296
    {
297
        switch ($handle) {
298
299 3
            case ExportModel::HandleAuthor:
300
301
                // Get username of author
302 1
                $data = craft()->users->getUserById($data)->username;
303
304 1
                break;
305
306 3
            case ExportModel::HandleEnabled:
307
308
                // Make data human readable
309
                switch ($data) {
310
311 2
                    case '0':
312 1
                        $data = Craft::t('No');
313 1
                        break;
314
315 1
                    case '1':
316 1
                        $data = Craft::t('Yes');
317 1
                        break;
318
319
                }
320
321 2
                break;
322
323 3
            case ExportModel::HandlePostDate:
324 3
            case ExportModel::HandleExpiryDate:
325
326
                // Resolve to string
327 1
                $data = (string) $data;
328
329 1
                break;
330
331
        }
332
333 3
        return $data;
334
    }
335
336
    /**
337
     * Parse field values.
338
     *
339
     * @param string $handle
340
     * @param mixed  $data
341
     *
342
     * @return string
343
     */
344 11
    public function parseFieldData($handle, $data)
345
    {
346
347
        // Do we have any data at all
348 11
        if (!is_null($data)) {
349
350
            // Get field info
351 11
            $field = craft()->fields->getFieldByHandle($handle);
352
353
            // If it's a field ofcourse
354 10
            if (!is_null($field)) {
355
356
                // For some fieldtypes the're special rules
357 10
                switch ($field->type) {
358
359 10
                    case ExportModel::FieldTypeEntries:
360 10
                    case ExportModel::FieldTypeCategories:
361 10
                    case ExportModel::FieldTypeAssets:
362 10
                    case ExportModel::FieldTypeUsers:
363
364
                        // Show names
365 1
                        $data = $data instanceof ElementCriteriaModel ? implode(', ', $data->find()) : $data;
1 ignored issue
show
Bug introduced by
The class Craft\ElementCriteriaModel does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
366
367 1
                        break;
368
369 9
                    case ExportModel::FieldTypeLightswitch:
370
371
                        // Make data human readable
372
                        switch ($data) {
373
374 2
                            case '0':
375 1
                                $data = Craft::t('No');
376 1
                                break;
377
378 1
                            case '1':
379 1
                                $data = Craft::t('Yes');
380 1
                                break;
381
382
                        }
383
384 2
                        break;
385
386 7
                    case ExportModel::FieldTypeTable:
387
388
                        // Parse table checkboxes
389 1
                        $table = array();
390 1
                        foreach ($data as $row) {
0 ignored issues
show
Bug introduced by
The expression $data of type object|integer|double|string|array|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...
391
392
                            // Keep track of column #
393 1
                            $i = 1;
394
395
                            // Loop through columns
396 1
                            foreach ($row as $column => $value) {
397
398
                                // Get column
399 1
                                $column = isset($field->settings['columns'][$column]) ? $field->settings['columns'][$column] : (isset($field->settings['columns']['col'.$i]) ? $field->settings['columns']['col'.$i] : array('type' => 'dummy'));
400
401
                                // Keep track of column #
402 1
                                $i++;
403
404
                                // Parse
405 1
                                $table[] = $column['type'] == 'checkbox' ? ($value == 1 ? Craft::t('Yes') : Craft::t('No')) : $value;
406 1
                            }
407 1
                        }
408
409
                        // Return parsed data as array
410 1
                        $data = $table;
411
412 1
                        break;
413
414 6
                    case ExportModel::FieldTypeRichText:
415 6
                    case ExportModel::FieldTypeDate:
416 6
                    case ExportModel::FieldTypeRadioButtons:
417 6
                    case ExportModel::FieldTypeDropdown:
418
419
                        // Resolve to string
420 1
                        $data = (string) $data;
421
422 1
                        break;
423
424 5
                    case ExportModel::FieldTypeCheckboxes:
425 5
                    case ExportModel::FieldTypeMultiSelect:
426
427
                        // Parse multi select values
428 1
                        $multi = array();
429 1
                        foreach ($data as $row) {
0 ignored issues
show
Bug introduced by
The expression $data of type object|integer|double|string|array|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...
430 1
                            $multi[] = $row->value;
431 1
                        }
432
433
                        // Return parsed data as array
434 1
                        $data = $multi;
435
436 1
                        break;
437
438 10
                }
439 10
            }
440
441
            // Get other operations
442 10
            craft()->plugins->call('registerExportOperation', array(&$data, $handle));
443 10
        } else {
444
445
            // Don't return null, return empty
446 1
            $data = '';
447
        }
448
449
        // If it's an object or an array, make it a string
450 10
        if (is_array($data)) {
451 2
            $data = StringHelper::arrayToString(ArrayHelper::filterEmptyStringsFromArray(ArrayHelper::flattenArray($data)), ', ');
452 2
        }
453
454
        // If it's an object, make it a string
455 10
        if (is_object($data)) {
456 1
            $data = StringHelper::arrayToString(ArrayHelper::filterEmptyStringsFromArray(ArrayHelper::flattenArray(get_object_vars($data))), ', ');
457 1
        }
458
459 10
        return $data;
460
    }
461
}
462