Refiller   B
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 270
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 42
c 0
b 0
f 0
lcom 1
cbo 8
dl 0
loc 270
ccs 0
cts 145
cp 0
rs 8.295

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getName() 0 4 1
A getTitle() 0 4 1
A isCanRefill() 0 4 2
C refill() 0 61 18
B isCanSearch() 0 19 6
C search() 0 45 8
A getSourceForFill() 0 11 3
A refillFromSearchResult() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like Refiller 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 Refiller, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * AnimeDb package.
4
 *
5
 * @author    Peter Gribanov <[email protected]>
6
 * @copyright Copyright (c) 2011, Peter Gribanov
7
 * @license   http://opensource.org/licenses/GPL-3.0 GPL v3
8
 */
9
namespace AnimeDb\Bundle\ShikimoriFillerBundle\Service;
10
11
use AnimeDb\Bundle\CatalogBundle\Plugin\Fill\Refiller\RefillerInterface;
12
use AnimeDb\Bundle\ShikimoriBrowserBundle\Service\Browser;
13
use AnimeDb\Bundle\CatalogBundle\Plugin\Fill\Refiller\Item as ItemRefiller;
14
use AnimeDb\Bundle\CatalogBundle\Entity\Item;
15
use AnimeDb\Bundle\CatalogBundle\Entity\Source;
16
use AnimeDb\Bundle\CatalogBundle\Entity\Name;
17
18
class Refiller implements RefillerInterface
19
{
20
    /**
21
     * @var string
22
     */
23
    const NAME = 'shikimori';
24
25
    /**
26
     * @var string
27
     */
28
    const TITLE = 'Shikimori.org';
29
30
    /**
31
     * @var array
32
     */
33
    protected $supported_fields = [
34
        self::FIELD_DATE_END,
35
        self::FIELD_DATE_PREMIERE,
36
        self::FIELD_DURATION,
37
        self::FIELD_EPISODES_NUMBER,
38
        self::FIELD_GENRES,
39
        self::FIELD_IMAGES,
40
        self::FIELD_NAMES,
41
        self::FIELD_STUDIO,
42
        self::FIELD_SOURCES,
43
        self::FIELD_SUMMARY,
44
    ];
45
46
    /**
47
     * @var Browser
48
     */
49
    private $browser;
50
51
    /**
52
     * @var Filler
53
     */
54
    protected $filler;
55
56
    /**
57
     * @var Search
58
     */
59
    protected $search;
60
61
    /**
62
     * @param Browser $browser
63
     * @param Filler $filler
64
     * @param Search $search
65
     */
66
    public function __construct(Browser $browser, Filler $filler, Search $search)
67
    {
68
        $this->browser = $browser;
69
        $this->filler = $filler;
70
        $this->search = $search;
71
    }
72
73
    /**
74
     * @return string
75
     */
76
    public function getName()
77
    {
78
        return self::NAME;
79
    }
80
81
    /**
82
     * @return string
83
     */
84
    public function getTitle()
85
    {
86
        return self::TITLE;
87
    }
88
89
    /**
90
     * Is can refill item from source.
91
     *
92
     * @param Item $item
93
     * @param string $field
94
     *
95
     * @return bool
96
     */
97
    public function isCanRefill(Item $item, $field)
98
    {
99
        return in_array($field, $this->supported_fields) && $this->getSourceForFill($item);
100
    }
101
102
    /**
103
     * Refill item field from source.
104
     *
105
     * @param Item $item
106
     * @param string $field
107
     *
108
     * @return Item
109
     */
110
    public function refill(Item $item, $field)
111
    {
112
        if (!($url = $this->getSourceForFill($item))) {
113
            return $item;
114
        }
115
116
        // get data
117
        preg_match(Filler::REG_ITEM_ID, $url, $match);
118
        $path = str_replace('#ID#', $match['id'], Filler::FILL_URL);
119
        $body = $this->browser->get($path);
120
121
        switch ($field) {
122
            case self::FIELD_DATE_END:
123
                if ($body['released_on']) {
124
                    $item->setDateEnd(new \DateTime($body['released_on']));
125
                }
126
                break;
127
            case self::FIELD_DATE_PREMIERE:
128
                $item->setDatePremiere(new \DateTime($body['aired_on']));
129
                break;
130
            case self::FIELD_DURATION:
131
                $item->setDuration($body['duration']);
132
                break;
133
            case self::FIELD_EPISODES_NUMBER:
134
                $ep_num = $body['episodes_aired'] ? $body['episodes_aired'] : $body['episodes'];
135
                $item->setEpisodesNumber($ep_num.($body['ongoing'] ? '+' : ''));
136
                break;
137
            case self::FIELD_GENRES:
138
                $new_item = $this->filler->setGenres(new Item(), $body);
139
                foreach ($new_item->getGenres() as $new_genre) {
140
                    $item->addGenre($new_genre);
141
                }
142
                break;
143
            case self::FIELD_IMAGES:
144
                $this->filler->setImages($item, $body);
145
                break;
146
            case self::FIELD_NAMES:
147
                $new_item = $this->filler->setNames(new Item(), $body);
148
                // set main name in top of names list
149
                $names = $new_item->getNames()->toArray();
150
                array_unshift($names, (new Name())->setName($new_item->getName()));
151
                foreach ($names as $new_name) {
152
                    $item->addName($new_name);
153
                }
154
                break;
155
            case self::FIELD_SOURCES:
156
                $new_item = $this->filler->setSources(new Item(), $body);
157
                foreach ($new_item->getSources() as $new_source) {
158
                    $item->addSource($new_source);
159
                }
160
                break;
161
            case self::FIELD_STUDIO:
162
                $this->filler->setStudio($item, $body);
163
                break;
164
            case self::FIELD_SUMMARY:
165
                $item->setSummary($body['description']);
166
                break;
167
        }
168
169
        return $item;
170
    }
171
172
    /**
173
     * @param Item $item
174
     * @param string $field
175
     *
176
     * @return bool
177
     */
178
    public function isCanSearch(Item $item, $field)
179
    {
180
        if (!in_array($field, $this->supported_fields)) {
181
            return false;
182
        }
183
184
        if ($this->isCanRefill($item, $field) || $item->getName()) {
185
            return true;
186
        }
187
188
        /* @var $name Name */
189
        foreach ($item->getNames() as $name) {
190
            if ($name->getName()) {
191
                return true;
192
            }
193
        }
194
195
        return false;
196
    }
197
198
    /**
199
     * Search items for refill.
200
     *
201
     * @param Item $item
202
     * @param string $field
203
     *
204
     * @return ItemRefiller[]
205
     */
206
    public function search(Item $item, $field)
207
    {
208
        // can refill from source. not need search
209
        if ($url = $this->getSourceForFill($item)) {
210
            return [
211
                new ItemRefiller(
212
                    $item->getName(),
213
                    ['url' => $url],
214
                    $url,
215
                    $item->getCover(),
216
                    $item->getSummary()
217
                ),
218
            ];
219
        }
220
221
        // get name for search
222
        if (!($name = $item->getName())) {
223
            foreach ($item->getNames() as $name) {
224
                if ($name) {
225
                    break;
226
                }
227
            }
228
        }
229
230
        $result = [];
231
        // do search
232
        if ($name) {
233
            $result = $this->search->search(['name' => $name]);
234
            foreach ($result as $key => $item) {
235
                if ($query = parse_url($item->getLink(), PHP_URL_QUERY)) {
0 ignored issues
show
Bug introduced by
The method getLink does only exist in AnimeDb\Bundle\CatalogBu...Plugin\Fill\Search\Item, but not in AnimeDb\Bundle\CatalogBu...ugin\Fill\Refiller\Item.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
236
                    parse_str($query, $query);
237
                    $link = array_values($query)[0]['url'];
238
                    $result[$key] = new ItemRefiller(
239
                        $item->getName(),
240
                        ['url' => $link],
241
                        $link,
242
                        $item->getImage(),
243
                        $item->getDescription()
244
                    );
245
                }
246
            }
247
        }
248
249
        return $result;
250
    }
251
252
    /**
253
     * Refill item field from search result.
254
     *
255
     * @param Item $item
256
     * @param string $field
257
     * @param array $data
258
     *
259
     * @return Item
260
     */
261
    public function refillFromSearchResult(Item $item, $field, array $data)
262
    {
263
        if (!empty($data['url'])) {
264
            $item->addSource((new Source())->setUrl($data['url']));
265
            $item = $this->refill($item, $field);
266
        }
267
268
        return $item;
269
    }
270
271
    /**
272
     * @param Item $item
273
     *
274
     * @return string
275
     */
276
    public function getSourceForFill(Item $item)
277
    {
278
        /* @var $source Source */
279
        foreach ($item->getSources() as $source) {
280
            if (strpos($source->getUrl(), $this->browser->getHost()) === 0) {
281
                return $source->getUrl();
282
            }
283
        }
284
285
        return '';
286
    }
287
}
288