Completed
Pull Request — master (#46)
by
unknown
10:20
created

YmlCatalog::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 1.0428

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
ccs 13
cts 20
cp 0.65
rs 8.8571
cc 1
eloc 23
nc 1
nop 11
crap 1.0428

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
namespace pastuhov\ymlcatalog;
3
4
use pastuhov\ymlcatalog\models\BaseModel;
5
use pastuhov\ymlcatalog\models\Category;
6
use pastuhov\ymlcatalog\models\Currency;
7
use pastuhov\ymlcatalog\models\LocalDeliveryCost;
8
use pastuhov\ymlcatalog\models\Shop;
9
use pastuhov\ymlcatalog\models\SimpleOffer;
10
use pastuhov\ymlcatalog\models\DeliveryOption;
11
use Yii;
12
use pastuhov\FileStream\BaseFileStream;
13
use yii\base\Exception;
14
use yii\data\ActiveDataProvider;
15
use yii\db\ActiveQuery;
16
17
/**
18
 * Yml генератор каталога.
19
 *
20
 * @package pastuhov\ymlcatalog
21
 */
22
class YmlCatalog
23
{
24
    /**
25
     * @var BaseFileStream
26
     */
27
    protected $handle;
28
    /**
29
     * @var string
30
     */
31
    protected $shopClass;
32
    /**
33
     * @var string
34
     */
35
    protected $currencyClass;
36
    /**
37
     * @var string
38
     */
39
    protected $categoryClass;
40
    /**
41
     * @var null|string
42
     */
43
    protected $localDeliveryCostClass;
44
    /**
45
     * @var string
46
     */
47
    protected $offerClass;
48
    /**
49
     * @var null|string
50
     */
51
    protected $date;
52
53
    /**
54
     * @var null|callable
55
     */
56
    protected $onValidationError;
57
58
    /**
59
     * @var null|string
60
     */
61
    protected $customOfferClass;
62
63
    /**
64
     * @var null|string
65
     */
66
    protected $customCategoryClass;
67
68
    /**
69
     * @var null|string
70
     */
71
    protected $deliveryOptionClass;
72
73
    /**
74
     * @param BaseFileStream $handle
75
     * @param string $shopClass class name
76
     * @param string $currencyClass class name
77
     * @param string $categoryClass class name
78
     * @param string $localDeliveryCostClass class name
79 5
     * @param array $offerClasses
80
     * @param null|string $date
81
     * @param null|callable $onValidationError
82
     * @param null|string $customOfferClass
83
     * @param null|string $customCategoryClass
84
     */
85
    public function __construct(
86
        BaseFileStream $handle,
87
        $shopClass,
88
        $currencyClass,
89
        $categoryClass,
90
        $localDeliveryCostClass = null,
91 5
        Array $offerClasses,
92 5
        $date = null,
93 5
        $onValidationError = null,
94 5
        $customOfferClass = null,
95 5
        $deliveryOptionClass = null,
96 5
        $customCategoryClass = null
97 5
    ) {
98 5
        $this->handle = $handle;
99 5
        $this->shopClass = $shopClass;
100 5
        $this->currencyClass = $currencyClass;
101 5
        $this->categoryClass = $categoryClass;
102
        $this->localDeliveryCostClass = $localDeliveryCostClass;
103
        $this->offerClasses = $offerClasses;
0 ignored issues
show
Bug introduced by
The property offerClasses does not seem to exist. Did you mean offerClass?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
104
        $this->date = $date;
105
        $this->onValidationError = $onValidationError;
106 5
        $this->customOfferClass = $customOfferClass;
107
        $this->deliveryOptionClass = $deliveryOptionClass;
108 5
        $this->customCategoryClass = $customCategoryClass;
109
    }
110 5
111 5
    /**
112 5
     * @throws Exception
113 5
     */
114 5
    public function generate()
115
    {
116 5
        $date = $this->getDate();
117 5
118 5
        $this->write(
119 5
            '<?xml version="1.0" encoding="utf-8"?>' . PHP_EOL .
120 5
            '<!DOCTYPE yml_catalog SYSTEM "shops.dtd">' . PHP_EOL .
121 5
            '<yml_catalog date="' . $date . '">' . PHP_EOL
122 5
        );
123 5
124 5
        $this->writeTag('shop');
125 4
        $this->writeModel(new Shop(), new $this->shopClass());
126 4
        $this->writeTag('currencies');
127 4
        $this->writeEachModel($this->currencyClass);
128 4
        $this->writeTag('/currencies');
129 5
        $this->writeTag('categories');
130 1
        $this->writeEachModel($this->categoryClass);
131 1
        $this->writeTag('/categories');
132 5
        if($this->deliveryOptionClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->deliveryOptionClass of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
133 5
            $this->writeTag('delivery-options');
134 5
            $this->writeModel(new DeliveryOption(), \Yii::createObject($this->deliveryOptionClass));
135 4
            $this->writeTag('/delivery-options');
136 4
        }
137 4
        if($this->localDeliveryCostClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->localDeliveryCostClass of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
138
            $this->writeModel(new LocalDeliveryCost(), \Yii::createObject($this->localDeliveryCostClass));
0 ignored issues
show
Deprecated Code introduced by
The class pastuhov\ymlcatalog\models\LocalDeliveryCost has been deprecated.

This class, trait or interface has been deprecated.

Loading history...
139 4
        }
140 4
        $this->writeTag('offers');
141
        foreach ($this->offerClasses as $offerClass) {
0 ignored issues
show
Bug introduced by
The property offerClasses does not seem to exist. Did you mean offerClass?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
142
            $this->writeEachModel($offerClass);
143
        }
144
        $this->writeTag('/offers');
145 5
        $this->writeTag('/shop');
146
147 5
        $this->write('</yml_catalog>');
148
    }
149 5
150 1
    /**
151 1
     * @return null|string
152
     */
153 5
    protected function getDate()
154
    {
155
        $date = $this->date;
156
157
        if ($date === null) {
158
            $date = Yii::$app->formatter->asDatetime(new \DateTime(), 'php:Y-m-d H:i');
159
        }
160 5
161
        return $date;
162 5
    }
163 5
164
    /**
165
     * @param string $string
166
     * @throws \Exception
167
     */
168 5
    protected function write($string)
169
    {
170 5
        $this->handle->write($string);
171 5
    }
172
173
    /**
174
     * @param string $string tag name
175
     */
176
    protected function writeTag($string)
177
    {
178 5
        $this->write('<' . $string . '>' . PHP_EOL);
179
    }
180 5
181 5
    /**
182 5
     * @param BaseModel $model
183 5
     * @param $valuesModel
184 5
     * @throws Exception
185 5
     */
186 5
    protected function writeModel(BaseModel $model, $valuesModel)
187 5
    {
188 5
        if (method_exists($valuesModel, 'getParams')) {
189
            $model->setParams($valuesModel->getParams());
190 5
        }
191 5
        if (method_exists($valuesModel, 'getPictures')) {
192 5
            $model->setPictures($valuesModel->getPictures());
193 5
        }
194 5
        if(method_exists($valuesModel, 'getDeliveryOptions')) {
195
            $model->setDeliveryOptions($valuesModel->getDeliveryOptions());
196
        }
197
198
        if($model->loadModel($valuesModel, $this->onValidationError)) {
0 ignored issues
show
Bug introduced by
It seems like $this->onValidationError can also be of type callable; however, pastuhov\ymlcatalog\models\BaseModel::loadModel() does only seem to accept null, 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...
199
            $string = $model->getYml();
200
            $this->write($string);
201
        }
202 5
    }
203
204
    /**
205
     * @param string|array $modelClass class name or yii configuration array. You can also set params:
206
     *      `findParams`:   array of additional find params;
207 5
     *      `query`:        ActiveQuery object to generate yml use already created object;
208
     *      `dataProvider`: ActiveDataProvider or true to generate yml with pagination;
209
     */
210
    protected function writeEachModel($modelClass)
211
    {
212 5
        /**
213
         * @var mixed
214
         */
215
        $findParams = [];
216
217 5
        /**
218
         * @var ActiveQuery
219 5
         */
220 4
        $query = null;
221 4
222 4
        /**
223 4
         * @var ActiveDataProvider
224 4
         */
225 4
        $dataProvider = null;
226 4
227
        if (is_array($modelClass)) {
228
            foreach (['findParams', 'query', 'dataProvider'] as $name) {
229
                if (array_key_exists($name, $modelClass)) {
230
                    $$name = $modelClass[$name];
231 5
                    unset($modelClass[$name]);
232
                }
233
            }
234
        }
235
236 5
        /**
237 5
         * @var BaseFindYmlInterface $class
238 5
         */
239 5
        $class = \Yii::createObject($modelClass);
240
241 5
        /**
242
         * @var ReaderInterface
243 5
         */
244
        $reader = ReaderFactory::build(
245 5
            $class,
246 5
            $dataProvider,
247 5
            $query,
248 5
            $findParams
249 5
        );
250 5
251 5
        $newModel = $this->getNewModel($class);
252
253
        foreach ($reader as $models) {
254
            foreach ($models as $model) {
255
                $this->writeModel($newModel, $model);
256
            }
257
            $this->gc();
258 5
        }
259
    }
260 5
261
    /**
262 5
     * @param $modelClass
263 5
     * @return Category|Currency|SimpleOffer
264 5
     * @throws Exception
265 5
     */
266 5
    protected function getNewModel($modelClass)
267 1
    {
268 5
        $obj = is_object($modelClass) ? $modelClass : \Yii::createObject($modelClass);
269 4
270 4
        if ($obj instanceof CurrencyInterface) {
271
            $model = new Currency();
272
        } elseif ($obj instanceof CustomCategoryInterface && !empty($this->customCategoryClass) && class_exists($this->customCategoryClass)) {
273
            $model = \Yii::createObject($this->customCategoryClass);
274 5
        } elseif ($obj instanceof CategoryInterface) {
275
            $model = new Category();
276
        } elseif ($obj instanceof CustomOfferInterface && $this->customOfferClass !== null && class_exists($this->customOfferClass)) {
277
            $model = \Yii::createObject($this->customOfferClass);
278
        } elseif ($obj instanceof SimpleOfferInterface) {
279
            $model = new SimpleOffer();
280 5
        } else {
281
            throw new Exception('Model ' . get_class($obj) . ' has unknown interface');
282 5
        }
283
284
        return $model;
285 5
    }
286 5
287
    /**
288
     * Performs PHP memory garbage collection.
289
     */
290
    protected function gc()
291
    {
292
        if (!gc_enabled()) {
293
            gc_enable();
294
        }
295
        gc_collect_cycles();
296
    }
297
}
298