Completed
Pull Request — master (#1761)
by Martin
06:11
created

ExportHelper::generateContentArray()   D

Complexity

Conditions 16
Paths 21

Size

Total Lines 47
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 47
rs 4.9897
c 0
b 0
f 0
cc 16
eloc 28
nc 21
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace luya\helpers;
4
5
use yii\db\ActiveQueryInterface;
6
use yii\db\ActiveRecordInterface;
7
use luya\helpers\ArrayHelper;
8
use yii\helpers\Html;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, luya\helpers\Html.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
use luya\Exception;
10
11
/**
12
 * Exporting into Formats.
13
 *
14
 * @author Basil Suter <[email protected]>
15
 * @since 1.0.0
16
 */
17
class ExportHelper
18
{
19
    /**
20
     * Export an Array or ActiveQuery instance into a CSV formated string.
21
     *
22
     * @param array|ActiveQueryInterface $input The data to export into a csv
23
     * @param array $keys Defines which keys should be packed into the generated CSV. The defined keys does not change the sort behavior of the generated csv.
24
     * @param string $header Whether the column name should be set as header inside the csv or not.
25
     * @return string The generated CSV as string.
26
     */
27
    public static function csv($input, array $keys = [], $header = true)
28
    {
29
        $delimiter = ",";
30
        $input = self::transformInput($input);
31
        $array = self::generateContentArray($input, $keys, $header);
0 ignored issues
show
Bug introduced by
It seems like $input defined by self::transformInput($input) on line 30 can also be of type object; however, luya\helpers\ExportHelper::generateContentArray() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Documentation introduced by
$keys is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
It seems like $header defined by parameter $header on line 27 can also be of type string; however, luya\helpers\ExportHelper::generateContentArray() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
32
33
        return self::generateOutputString($array, $delimiter);
34
    }
35
36
    /**
37
     * Export an Array or ActiveQuery instance into a Excel formatted string.
38
     *
39
     * @param array|ActiveQueryInterface $input
40
     * @param array $keys Defines which keys should be packed into the generated xlsx. The defined keys does not change the sort behavior of the generated xls.
41
     * @param bool $header
42
     * @return mixed
43
     * @throws Exception
44
     * @since 1.0.4
45
     */
46
    public static function xlsx($input, array $keys = [], $header = true)
47
    {
48
        $input = self::transformInput($input);
49
50
        $array = self::generateContentArray($input, $keys, $header);
0 ignored issues
show
Bug introduced by
It seems like $input defined by self::transformInput($input) on line 48 can also be of type object; however, luya\helpers\ExportHelper::generateContentArray() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Documentation introduced by
$keys is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
51
52
        $writer = new XLSXWriter();
53
        $writer->writeSheet($array);
54
55
        return $writer->writeToStdOut();
56
    }
57
58
    /**
59
     * Check type of input and return correct array.
60
     *
61
     * @param array|object $input
62
     * @return array
63
     * @since 1.0.4
64
     */
65
    protected static function transformInput($input)
66
    {
67
        if ($input instanceof ActiveQueryInterface) {
68
            return $input->all();
69
        }
70
71
        return $input;
72
    }
73
74
    /**
75
     * Generate content by rows.
76
     *
77
     * @param array $contentRows
78
     * @param string$delimiter
79
     * @param string $keys
80
     * @param bool $generateHeader
81
     * @return array
82
     * @throws Exception
83
     * @since 1.0.4
84
     */
85
    protected static function generateContentArray($contentRows, $keys, $generateHeader = true)
86
    {
87
        if (is_scalar($contentRows)) {
88
            throw new Exception("Content must be either an array, object or traversable");
89
        }
90
91
        $attributeKeys = $keys;
92
        $header = [];
93
        $rows = [];
94
        $i = 0;
95
        foreach ($contentRows as $content) {
96
            // handle rows content
97
            if (!empty($keys) && is_array($content)) {
98
                foreach ($content as $k => $v) {
99
                    if (!in_array($k, $keys)) {
100
                        unset($content[$k]);
101
                    }
102
                }
103
            } elseif (!empty($keys) && is_object($content)) {
104
                $attributeKeys[get_class($content)] = $keys;
105
            }
106
            $rows[$i] = ArrayHelper::toArray($content, $attributeKeys, false);
0 ignored issues
show
Documentation introduced by
$attributeKeys is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
107
108
            // handler header
109
            if ($i == 0 && $generateHeader) {
110
                if ($content instanceof ActiveRecordInterface) {
111
                    foreach ($content as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $content of type object<yii\db\ActiveRecordInterface> is not traversable.
Loading history...
112
                        if (empty($keys)) {
113
                            $header[] = $content->getAttributeLabel($k);
0 ignored issues
show
Bug introduced by
The method getAttributeLabel() does not exist on yii\db\ActiveRecordInterface. Did you maybe mean getAttribute()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
114
                        } elseif (in_array($k, $keys)) {
115
                            $header[] = $content->getAttributeLabel($k);
0 ignored issues
show
Bug introduced by
The method getAttributeLabel() does not exist on yii\db\ActiveRecordInterface. Did you maybe mean getAttribute()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
116
                        }
117
                    }
118
                } else {
119
                    $header = array_keys($rows[0]);
120
                }
121
            }
122
123
            $i++;
124
        }
125
126
        if ($generateHeader) {
127
            return ArrayHelper::merge([$header], $rows);
128
        }
129
130
        return $rows;
131
    }
132
133
    /**
134
     * @param array $input
135
     * @param $delimiter
136
     * @return null|string
137
     * @since 1.0.4
138
     */
139
    protected static function generateOutputString(array $input, $delimiter)
140
    {
141
        $output = null;
142
        foreach ($input as $row) {
143
            $output.= self::generateRow($row, $delimiter, '"');
144
        }
145
146
        return $output;
147
    }
148
149
    /**
150
     * Generate a row by its items.
151
     *
152
     * @param array $row
153
     * @param string $delimiter
154
     * @param string $enclose
155
     * @return string
156
     * @since 1.0.4
157
     */
158
    protected static function generateRow(array $row, $delimiter, $enclose)
159
    {
160
        array_walk($row, function (&$item) use ($enclose) {
161
            if (!is_scalar($item)) {
162
                $item = "array";
163
            }
164
            $item = $enclose.Html::encode($item).$enclose;
165
        });
166
167
        return implode($delimiter, $row) . PHP_EOL;
168
    }
169
}
170