Completed
Push — master ( 6375fb...517bde )
by Bukashk0zzz
06:43
created

Generator   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 345
Duplicated Lines 6.38 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 55
lcom 1
cbo 8
dl 22
loc 345
rs 6
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 21 7
A generate() 0 30 5
A addHeader() 0 9 1
A addFooter() 0 6 1
A addShopInfo() 0 8 3
A addCurrency() 0 7 1
A addCategory() 12 12 2
A addDelivery() 10 10 2
B addOffer() 0 29 8
A addCurrencies() 0 13 3
A addCategories() 0 13 3
A addDeliveries() 0 13 3
A addOffers() 0 13 3
A addOfferDeliveryOptions() 0 7 2
A addOfferParams() 0 17 4
A addOfferCondition() 0 10 2
A addOfferElement() 0 21 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Generator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Generator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Bukashk0zzzYmlGenerator
5
 *
6
 * (c) Denis Golubovskiy <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Bukashk0zzz\YmlGenerator;
13
14
use Bukashk0zzz\YmlGenerator\Model\Category;
15
use Bukashk0zzz\YmlGenerator\Model\Currency;
16
use Bukashk0zzz\YmlGenerator\Model\Delivery;
17
use Bukashk0zzz\YmlGenerator\Model\Offer\OfferCondition;
18
use Bukashk0zzz\YmlGenerator\Model\Offer\OfferGroupAwareInterface;
19
use Bukashk0zzz\YmlGenerator\Model\Offer\OfferInterface;
20
use Bukashk0zzz\YmlGenerator\Model\Offer\OfferParam;
21
use Bukashk0zzz\YmlGenerator\Model\ShopInfo;
22
23
/**
24
 * Class Generator
25
 */
26
class Generator
27
{
28
    /**
29
     * @var string
30
     */
31
    private $tmpFile;
32
33
    /**
34
     * @var \XMLWriter
35
     */
36
    private $writer;
37
38
    /**
39
     * @var Settings
40
     */
41
    private $settings;
42
43
    /**
44
     * Generator constructor.
45
     *
46
     * @param Settings $settings
47
     */
48
    public function __construct($settings = null)
49
    {
50
        $this->settings = $settings instanceof Settings ? $settings : new Settings();
51
        $this->writer = new \XMLWriter();
52
53
        if ($this->settings->getOutputFile() !== null && $this->settings->getReturnResultYMLString()) {
54
            throw new \LogicException('Only one destination need to be used ReturnResultYMLString or OutputFile');
55
        }
56
57
        if ($this->settings->getReturnResultYMLString()) {
58
            $this->writer->openMemory();
59
        } else {
60
            $this->tmpFile = $this->settings->getOutputFile() !== null ? \tempnam(\sys_get_temp_dir(), 'YMLGenerator') : 'php://output';
61
            $this->writer->openURI($this->tmpFile);
62
        }
63
64
        if ($this->settings->getIndentString()) {
65
            $this->writer->setIndentString($this->settings->getIndentString());
66
            $this->writer->setIndent(true);
67
        }
68
    }
69
70
    /**
71
     * @param ShopInfo $shopInfo
72
     * @param array    $currencies
73
     * @param array    $categories
74
     * @param array    $offers
75
     * @param array    $deliveries
76
     *
77
     * @return bool
78
     */
79
    public function generate(ShopInfo $shopInfo, array $currencies, array $categories, array $offers, array $deliveries = [])
80
    {
81
        try {
82
            $this->addHeader();
83
84
            $this->addShopInfo($shopInfo);
85
            $this->addCurrencies($currencies);
86
            $this->addCategories($categories);
87
88
            if (\count($deliveries) !== 0) {
89
                $this->addDeliveries($deliveries);
90
            }
91
92
            $this->addOffers($offers);
93
            $this->addFooter();
94
95
            if ($this->settings->getReturnResultYMLString()) {
96
                return $this->writer->flush();
97
            }
98
99
            if (null !== $this->settings->getOutputFile()) {
100
                \copy($this->tmpFile, $this->settings->getOutputFile());
101
                @\unlink($this->tmpFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
102
            }
103
104
            return true;
105
        } catch (\Exception $exception) {
106
            throw new \RuntimeException(\sprintf('Problem with generating YML file: %s', $exception->getMessage()), 0, $exception);
107
        }
108
    }
109
110
    /**
111
     * Add document header
112
     */
113
    protected function addHeader()
114
    {
115
        $this->writer->startDocument('1.0', $this->settings->getEncoding());
116
        $this->writer->startDTD('yml_catalog', null, 'shops.dtd');
117
        $this->writer->endDTD();
118
        $this->writer->startElement('yml_catalog');
119
        $this->writer->writeAttribute('date', \date('Y-m-d H:i'));
120
        $this->writer->startElement('shop');
121
    }
122
123
    /**
124
     * Add document footer
125
     */
126
    protected function addFooter()
127
    {
128
        $this->writer->fullEndElement();
129
        $this->writer->fullEndElement();
130
        $this->writer->endDocument();
131
    }
132
133
    /**
134
     * Adds shop element data. (See https://yandex.ru/support/webmaster/goods-prices/technical-requirements.xml#shop)
135
     *
136
     * @param ShopInfo $shopInfo
137
     */
138
    protected function addShopInfo(ShopInfo $shopInfo)
139
    {
140
        foreach ($shopInfo->toArray() as $name => $value) {
141
            if ($value !== null) {
142
                $this->writer->writeElement($name, $value);
143
            }
144
        }
145
    }
146
147
    /**
148
     * @param Currency $currency
149
     */
150
    protected function addCurrency(Currency $currency)
151
    {
152
        $this->writer->startElement('currency');
153
        $this->writer->writeAttribute('id', $currency->getId());
154
        $this->writer->writeAttribute('rate', $currency->getRate());
155
        $this->writer->endElement();
156
    }
157
158
    /**
159
     * @param Category $category
160
     */
161 View Code Duplication
    protected function addCategory(Category $category)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
    {
163
        $this->writer->startElement('category');
164
        $this->writer->writeAttribute('id', $category->getId());
165
166
        if ($category->getParentId() !== null) {
167
            $this->writer->writeAttribute('parentId', $category->getParentId());
168
        }
169
170
        $this->writer->text($category->getName());
171
        $this->writer->fullEndElement();
172
    }
173
174
    /**
175
     * @param Delivery $delivery
176
     */
177 View Code Duplication
    protected function addDelivery(Delivery $delivery)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
    {
179
        $this->writer->startElement('option');
180
        $this->writer->writeAttribute('cost', $delivery->getCost());
181
        $this->writer->writeAttribute('days', $delivery->getDays());
182
        if ($delivery->getOrderBefore() !== null) {
183
            $this->writer->writeAttribute('order-before', $delivery->getOrderBefore());
184
        }
185
        $this->writer->endElement();
186
    }
187
188
    /**
189
     * @param OfferInterface $offer
190
     */
191
    protected function addOffer(OfferInterface $offer)
192
    {
193
        $this->writer->startElement('offer');
194
        $this->writer->writeAttribute('id', $offer->getId());
195
        $this->writer->writeAttribute('available', $offer->isAvailable() ? 'true' : 'false');
196
197
        if ($offer->getType() !== null) {
198
            $this->writer->writeAttribute('type', $offer->getType());
199
        }
200
201
        if ($offer instanceof OfferGroupAwareInterface && $offer->getGroupId() !== null) {
202
            $this->writer->writeAttribute('group_id', $offer->getGroupId());
203
        }
204
205
        foreach ($offer->toArray() as $name => $value) {
206
            if (\is_array($value)) {
207
                foreach ($value as $itemValue) {
208
                    $this->addOfferElement($name, $itemValue);
209
                }
210
            } else {
211
                $this->addOfferElement($name, $value);
212
            }
213
        }
214
        $this->addOfferParams($offer);
215
        $this->addOfferDeliveryOptions($offer);
216
        $this->addOfferCondition($offer);
217
218
        $this->writer->fullEndElement();
219
    }
220
221
    /**
222
     * Adds <currencies> element. (See https://yandex.ru/support/webmaster/goods-prices/technical-requirements.xml#currencies)
223
     *
224
     * @param array $currencies
225
     */
226
    private function addCurrencies(array $currencies)
227
    {
228
        $this->writer->startElement('currencies');
229
230
        /** @var Currency $currency */
231
        foreach ($currencies as $currency) {
232
            if ($currency instanceof Currency) {
233
                $this->addCurrency($currency);
234
            }
235
        }
236
237
        $this->writer->fullEndElement();
238
    }
239
240
    /**
241
     * Adds <categories> element. (See https://yandex.ru/support/webmaster/goods-prices/technical-requirements.xml#categories)
242
     *
243
     * @param array $categories
244
     */
245
    private function addCategories(array $categories)
246
    {
247
        $this->writer->startElement('categories');
248
249
        /** @var Category $category */
250
        foreach ($categories as $category) {
251
            if ($category instanceof Category) {
252
                $this->addCategory($category);
253
            }
254
        }
255
256
        $this->writer->fullEndElement();
257
    }
258
259
    /**
260
     * Adds <delivery-option> element. (See https://yandex.ru/support/partnermarket/elements/delivery-options.xml)
261
     *
262
     * @param array $deliveries
263
     */
264
    private function addDeliveries(array $deliveries)
265
    {
266
        $this->writer->startElement('delivery-options');
267
268
        /** @var Delivery $delivery */
269
        foreach ($deliveries as $delivery) {
270
            if ($delivery instanceof Delivery) {
271
                $this->addDelivery($delivery);
272
            }
273
        }
274
275
        $this->writer->fullEndElement();
276
    }
277
278
    /**
279
     * Adds <offers> element. (See https://yandex.ru/support/webmaster/goods-prices/technical-requirements.xml#offers)
280
     *
281
     * @param array $offers
282
     */
283
    private function addOffers(array $offers)
284
    {
285
        $this->writer->startElement('offers');
286
287
        /** @var OfferInterface $offer */
288
        foreach ($offers as $offer) {
289
            if ($offer instanceof OfferInterface) {
290
                $this->addOffer($offer);
291
            }
292
        }
293
294
        $this->writer->fullEndElement();
295
    }
296
297
    /**
298
     * @param OfferInterface $offer
299
     */
300
    private function addOfferDeliveryOptions(OfferInterface $offer)
301
    {
302
        $options = $offer->getDeliveryOptions();
303
        if (!empty($options)) {
304
            $this->addDeliveries($options);
305
        }
306
    }
307
308
    /**
309
     * @param OfferInterface $offer
310
     */
311
    private function addOfferParams(OfferInterface $offer)
312
    {
313
        /** @var OfferParam $param */
314
        foreach ($offer->getParams() as $param) {
315
            if ($param instanceof OfferParam) {
316
                $this->writer->startElement('param');
317
318
                $this->writer->writeAttribute('name', $param->getName());
319
                if ($param->getUnit()) {
320
                    $this->writer->writeAttribute('unit', $param->getUnit());
321
                }
322
                $this->writer->text($param->getValue());
323
324
                $this->writer->endElement();
325
            }
326
        }
327
    }
328
329
    /**
330
     * @param OfferInterface $offer
331
     */
332
    private function addOfferCondition(OfferInterface $offer)
333
    {
334
        $params = $offer->getCondition();
335
        if ($params instanceof OfferCondition) {
336
            $this->writer->startElement('condition');
337
            $this->writer->writeAttribute('type', $params->getType());
338
            $this->writer->writeElement('reason', $params->getReasonText());
339
            $this->writer->endElement();
340
        }
341
    }
342
343
    /**
344
     * @param string $name
345
     * @param mixed  $value
346
     *
347
     * @return bool
348
     */
349
    private function addOfferElement($name, $value)
350
    {
351
        if ($value === null) {
352
            return false;
353
        }
354
355
        if ($value instanceof Cdata) {
356
            $this->writer->startElement($name);
357
            $this->writer->writeCdata((string) $value);
358
            $this->writer->endElement();
359
360
            return true;
361
        }
362
363
        if (\is_bool($value)) {
364
            $value = $value ? 'true' : 'false';
365
        }
366
        $this->writer->writeElement($name, $value);
367
368
        return true;
369
    }
370
}
371