Completed
Pull Request — master (#37)
by
unknown
04:17
created

YmlCatalog::gc()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 0
cts 0
cp 0
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
crap 6
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
15
/**
16
 * Yml генератор каталога.
17
 *
18
 * @package pastuhov\ymlcatalog
19
 */
20
class YmlCatalog
21
{
22
    /**
23
     * @var BaseFileStream
24
     */
25
    protected $handle;
26
    /**
27
     * @var string
28
     */
29
    protected $shopClass;
30
    /**
31
     * @var string
32
     */
33
    protected $currencyClass;
34
    /**
35
     * @var string
36
     */
37
    protected $categoryClass;
38
    /**
39
     * @var null|string
40
     */
41
    protected $localDeliveryCostClass;
42
    /**
43
     * @var string
44
     */
45
    protected $offerClass;
46
    /**
47
     * @var null|string
48
     */
49
    protected $date;
50
51
    /**
52
     * @var null|callable
53
     */
54
    protected $onValidationError;
55
56
    /**
57
     * @var null|string
58
     */
59
    protected $customOfferClass;
60
61
    /**
62
     * @var null|string
63
     */
64
    protected $deliveryOptionClass;
65
    
66
    /**
67
     * @var ActiveQuery
68
     */
69
    private $query = null;
70
71 9
    /**
72
     * @var BatchQueryResult
73
     */
74 2
    private $queryIterator = null;
75
76
    /**
77
     * @var ActiveDataProvider
78
     */
79
    private $dataProvider = null;
80
81
    /**
82 9
     * @var Pagination
83 9
     */
84 9
    private $pagination = null;
85 9
86 9
    /**
87 9
     * @param BaseFileStream $handle
88 9
     * @param string $shopClass class name
89 9
     * @param string $currencyClass class name
90 9
     * @param string $categoryClass class name
91 9
     * @param string $localDeliveryCostClass class name
92
     * @param array $offerClasses
93
     * @param null|string $date
94
     * @param null|callable $onValidationError
95
     * @param null|string $customOfferClass
96 9
     */
97
    public function __construct(
98 9
        BaseFileStream $handle,
99
        $shopClass,
100 9
        $currencyClass,
101 9
        $categoryClass,
102 9
        $localDeliveryCostClass = null,
103 9
        Array $offerClasses,
104 6
        $date = null,
105
        $onValidationError = null,
106 9
        $customOfferClass = null,
107 9
        $deliveryOptionClass = null
108 9
    ) {
109 9
        $this->handle = $handle;
110 9
        $this->shopClass = $shopClass;
111 9
        $this->currencyClass = $currencyClass;
112 9
        $this->categoryClass = $categoryClass;
113 9
        $this->localDeliveryCostClass = $localDeliveryCostClass;
114 9
        $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...
115 9
        $this->date = $date;
116 9
        $this->onValidationError = $onValidationError;
117 9
        $this->customOfferClass = $customOfferClass;
118 4
        $this->deliveryOptionClass = $deliveryOptionClass;
119 6
    }
120 8
121
    /**
122 6
     * @throws Exception
123 6
     */
124
    public function generate()
125
    {
126
        $date = $this->getDate();
127
128 9
        $this->write(
129
            '<?xml version="1.0" encoding="utf-8"?>' . PHP_EOL .
130 9
            '<!DOCTYPE yml_catalog SYSTEM "shops.dtd">' . PHP_EOL .
131
            '<yml_catalog date="' . $date . '">' . PHP_EOL
132 9
        );
133 3
134 2
        $this->writeTag('shop');
135
        $this->writeModel(new Shop(), new $this->shopClass());
136 9
        $this->writeTag('currencies');
137
        $this->writeEachModel($this->currencyClass);
138
        $this->writeTag('/currencies');
139
        $this->writeTag('categories');
140
        $this->writeEachModel($this->categoryClass);
141
        $this->writeTag('/categories');
142
        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...
143 9
            $this->writeTag('delivery-options');
144 6
            $this->writeModel(new DeliveryOption(), \Yii::createObject($this->deliveryOptionClass));
145 9
            $this->writeTag('/delivery-options');
146 9
        }
147
        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...
148
            $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...
149
        }
150
        $this->writeTag('offers');
151 9
        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...
152 6
            $this->writeEachModel($offerClass);
153 9
        }
154 9
        $this->writeTag('/offers');
155
        $this->writeTag('/shop');
156
157
        $this->write('</yml_catalog>');
158
    }
159
160
    /**
161 9
     * @return null|string
162
     */
163 9
    protected function getDate()
164 9
    {
165 9
        $date = $this->date;
166 9
167
        if ($date === null) {
168 9
            $date = Yii::$app->formatter->asDatetime(new \DateTime(), 'php:Y-m-d H:i');
169 6
        }
170
171 9
        return $date;
172 9
    }
173 6
174 4
    /**
175 9
     * @param string $string
176 6
     * @throws \Exception
177 4
     */
178
    protected function write($string)
179 9
    {
180 9
        $this->handle->write($string);
181 9
    }
182 6
183 3
    /**
184
     * @param string $string tag name
185 6
     */
186 9
    protected function writeTag($string)
187
    {
188 9
        $this->write('<' . $string . '>' . PHP_EOL);
189
    }
190 9
191
    /**
192
     * @param BaseModel $model
193
     * @param $valuesModel
194
     * @throws Exception
195 9
     */
196
    protected function writeModel(BaseModel $model, $valuesModel)
197 9
    {
198 9
        if (method_exists($valuesModel, 'getParams')) {
199
            $model->setParams($valuesModel->getParams());
200 9
        }
201
        if (method_exists($valuesModel, 'getPictures')) {
202
            $model->setPictures($valuesModel->getPictures());
203 9
        }
204
        if(method_exists($valuesModel, 'getDeliveryOptions')) {
205 9
            $model->setDeliveryOptions($valuesModel->getDeliveryOptions());
206 9
        }
207 9
208 6
        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...
209 6
            $string = $model->getYml();
210 9
            $this->write($string);
211
        }
212
    }
213
214
    /**
215
     * @param string|array $modelClass class name
216
     */
217 9
    protected function writeEachModel($modelClass)
218
    {
219 9
        $findParams = [];
220
        $this->query = null;
221 9
        $this->dataProvider = null;
222 9
        $this->pagination = null;
223 6
        $this->queryIterator = null;
224 9
225 9
        if (is_array($modelClass) && array_key_exists('findParams', $modelClass)) {
226 3
            $findParams = $modelClass['findParams'];
227 3
            unset($modelClass['findParams']);
228 6
        }
229 6
230 4
        if (is_array($modelClass) && array_key_exists('query', $modelClass)) {
231
            if ($modelClass['query'] instanceof ActiveQuery) {
0 ignored issues
show
Bug introduced by
The class pastuhov\ymlcatalog\ActiveQuery 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...
232
                $this->query = $modelClass['query'];
233
            }
234 9
            unset($modelClass['query']);
235
        }
236
237
        if (
238
            is_array($modelClass)
239
            && array_key_exists('dataProvider', $modelClass)
240
        ) {
241
            $this->dataProvider = $modelClass['dataProvider'];
242
            unset($modelClass['dataProvider']);
243
244
            if (!$this->dataProvider instanceof ActiveDataProvider) {
0 ignored issues
show
Bug introduced by
The class pastuhov\ymlcatalog\ActiveDataProvider 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...
245
                if ($this->dataProvider === true) {
246
                    $this->dataProvider = new ActiveDataProvider([
247
                        'query' => ($this->query ?: (\Yii::createObject($modelClass))::findYml($findParams)),
248
                        'pagination' => [
249
                            'pageSize' => 100
250
                        ],
251
                    ]);
252
                } else {
253
                    unset($this->dataProvider);
254
                }
255
            }
256
        }
257
258
        $class = \Yii::createObject($modelClass);
259
260
        $newModel = $this->getNewModel($class);
261
262
        if (!$this->dataProvider && !$this->query) {
263
            if (!$this->query) {
264
                $this->query = $class::findYml($findParams);
265
            }
266
        }
267
268
        if ($this->query && !$this->dataProvider) {
269
            $this->queryIterator = $this->query->batch();
270
        }
271
272
        while (($models = $this->getModels()) !== false) {
273
            foreach ($models as $model) {
274
                $this->writeModel($newModel, $model);
275
            }
276
            $this->gc();
277
        }
278
    }
279
    
280
    /**
281
     * @return Model[]|false
282
     */
283
    protected function getModels()
284
    {
285
        $result = false;
286
287
        if ($this->dataProvider) {
288
            if (!$this->pagination) {
289
                $this->pagination = $this->dataProvider->getPagination();
290
                $this->pagination->setPage(0);
291
                $page = 0;
292
            } else {
293
                $page = $this->pagination->getPage() + 1;
294
            }
295
            if ($page == 0 || $page < $this->pagination->pageCount) {
296
                if ($page > 0) {
297
                    $this->pagination->setPage($page);
298
                    $this->dataProvider->prepare(true);
299
                }
300
                $result = $this->dataProvider->getModels();
301
            }
302
        } else {
303
            $this->queryIterator->next();
304
            if ($this->queryIterator->valid()) {
305
                $result = $this->queryIterator->current();
306
            }
307
        }
308
309
        return $result;
310
    }
311
312
    /**
313
     * @param $modelClass
314
     * @return Category|Currency|SimpleOffer
315
     * @throws Exception
316
     */
317
    protected function getNewModel($modelClass)
318
    {
319
        $obj = is_object($modelClass) ? $modelClass : \Yii::createObject($modelClass);
320
321
        if ($obj instanceof CurrencyInterface) {
322
            $model = new Currency();
323
        } elseif ($obj instanceof CategoryInterface) {
324
            $model = new Category();
325
        } elseif ($obj instanceof CustomOfferInterface && $this->customOfferClass !== null && class_exists($this->customOfferClass)) {
326
            $model = \Yii::createObject($this->customOfferClass);
327
        } elseif ($obj instanceof SimpleOfferInterface) {
328
            $model = new SimpleOffer();
329
        } else {
330
            throw new Exception('Model ' . get_class($obj) . ' has unknown interface');
331
        }
332
333
        return $model;
334
    }
335
336
    /**
337
     * Performs PHP memory garbage collection.
338
     */
339
    protected function gc()
340
    {
341
        if (!gc_enabled()) {
342
            gc_enable();
343
        }
344
        gc_collect_cycles();
345
    }
346
}
347