This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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\AniDbFillerBundle\Service; |
||
10 | |||
11 | use AnimeDb\Bundle\CatalogBundle\Plugin\Fill\Filler\Filler as FillerPlugin; |
||
12 | use AnimeDb\Bundle\AniDbBrowserBundle\Service\Browser; |
||
13 | use Doctrine\Bundle\DoctrineBundle\Registry; |
||
14 | use AnimeDb\Bundle\AppBundle\Service\Downloader; |
||
15 | use AnimeDb\Bundle\CatalogBundle\Entity\Item; |
||
16 | use AnimeDb\Bundle\CatalogBundle\Entity\Name; |
||
17 | use AnimeDb\Bundle\CatalogBundle\Entity\Source; |
||
18 | use AnimeDb\Bundle\CatalogBundle\Entity\Genre; |
||
19 | use AnimeDb\Bundle\AniDbFillerBundle\Form\Type\Filler as FillerForm; |
||
20 | use Symfony\Component\DomCrawler\Crawler; |
||
21 | use Knp\Menu\ItemInterface; |
||
22 | use AnimeDb\Bundle\AppBundle\Service\Downloader\Entity\EntityInterface; |
||
23 | |||
24 | class Filler extends FillerPlugin |
||
25 | { |
||
26 | /** |
||
27 | * @var string |
||
28 | */ |
||
29 | const NAME = 'anidb'; |
||
30 | |||
31 | /** |
||
32 | * @var string |
||
33 | */ |
||
34 | const TITLE = 'AniDB.net'; |
||
35 | |||
36 | /** |
||
37 | * RegExp for get item id. |
||
38 | * |
||
39 | * @var string |
||
40 | */ |
||
41 | const REG_ITEM_ID = '#/perl\-bin/animedb\.pl\?show=anime&aid=(?<id>\d+)#'; |
||
42 | |||
43 | /** |
||
44 | * @var Browser |
||
45 | */ |
||
46 | protected $browser; |
||
47 | |||
48 | /** |
||
49 | * @var Registry |
||
50 | */ |
||
51 | protected $doctrine; |
||
52 | |||
53 | /** |
||
54 | * @var Downloader |
||
55 | */ |
||
56 | protected $downloader; |
||
57 | |||
58 | /** |
||
59 | * @var SummaryCleaner |
||
60 | */ |
||
61 | protected $cleaner; |
||
62 | |||
63 | /** |
||
64 | * @var string |
||
65 | */ |
||
66 | protected $locale; |
||
67 | |||
68 | /** |
||
69 | * AniDB category to genre. |
||
70 | * |
||
71 | * <code> |
||
72 | * { from: to, ... } |
||
73 | * </code> |
||
74 | * |
||
75 | * @var array |
||
76 | */ |
||
77 | protected $category_to_genre = [ |
||
78 | 'Alternative History' => 'History', |
||
79 | 'Anti-War' => 'War', |
||
80 | 'Badminton' => 'Sport', |
||
81 | 'Bakumatsu - Meiji Period' => 'History', |
||
82 | 'Band' => 'Music', |
||
83 | 'Baseball' => 'Sport', |
||
84 | 'Basketball' => 'Sport', |
||
85 | 'Battle Royale' => 'War', |
||
86 | 'Board Games' => 'Game', |
||
87 | 'Boxing' => 'Sport', |
||
88 | 'Catholic School' => 'School', |
||
89 | 'Chess' => 'Sport', |
||
90 | 'Clubs' => 'School', |
||
91 | 'College' => 'School', |
||
92 | 'Combat' => 'Action', |
||
93 | 'Conspiracy' => 'Thriller', |
||
94 | 'Contemporary Fantasy' => 'Fantasy', |
||
95 | 'Cops' => 'Police', |
||
96 | 'Daily Life' => 'Slice of life', |
||
97 | 'Dark Elf' => 'Fantasy', |
||
98 | 'Dark Fantasy' => 'Fantasy', |
||
99 | 'Dodgeball' => 'Sport', |
||
100 | 'Dragon' => 'Fantasy', |
||
101 | 'Edo Period' => 'Fantasy', |
||
102 | 'Elementary School' => 'School', |
||
103 | 'Elf' => 'Fantasy', |
||
104 | 'Fairies' => 'Fantasy', |
||
105 | 'Fantasy World' => 'Fantasy', |
||
106 | 'Feudal Warfare' => 'War', |
||
107 | 'Football' => 'Sport', |
||
108 | 'Formula Racing' => 'Sport', |
||
109 | 'Ghost' => 'Supernatural', |
||
110 | 'Go' => 'Game', |
||
111 | 'Golf' => 'Sport', |
||
112 | 'Gunfights' => 'War', |
||
113 | 'Gymnastics' => 'Sport', |
||
114 | 'Heian Period' => 'History', |
||
115 | 'High Fantasy' => 'Fantasy', |
||
116 | 'High School' => 'School', |
||
117 | 'Historical' => 'History', |
||
118 | 'Ice Skating' => 'Sport', |
||
119 | 'Inline Skating' => 'Sport', |
||
120 | 'Jousting' => 'Sport', |
||
121 | 'Judo' => 'Sport', |
||
122 | 'Kendo' => 'Sport', |
||
123 | 'Law and Order' => 'Police', |
||
124 | 'Magic Circles' => 'Magic', |
||
125 | 'Mahjong' => 'Game', |
||
126 | 'Mahou Shoujo' => 'Mahoe shoujo', |
||
127 | 'Martial Arts' => 'Martial arts', |
||
128 | 'Military' => 'War', |
||
129 | 'Motorsport' => 'Sport', |
||
130 | 'Muay Thai' => 'Sport', |
||
131 | 'Ninja' => 'Samurai', |
||
132 | 'Pirate' => 'Adventure', |
||
133 | 'Post-apocalypse' => 'Apocalyptic fiction', |
||
134 | 'Post-War' => 'War', |
||
135 | 'Proxy Battles' => 'War', |
||
136 | 'Reverse Harem' => 'Harem', |
||
137 | 'Rugby' => 'Sport', |
||
138 | 'School Dormitory' => 'School', |
||
139 | 'School Excursion' => 'School', |
||
140 | 'School Festival' => 'School', |
||
141 | 'School Life' => 'School', |
||
142 | 'School Sports Festival' => 'School', |
||
143 | 'Sci-Fi' => 'Sci-fi', |
||
144 | 'Sengoku Period' => 'History', |
||
145 | 'Shougi' => 'Game', |
||
146 | 'Shoujo Ai' => 'Shoujo-ai', |
||
147 | 'Shounen Ai' => 'Shounen-ai', |
||
148 | 'Spellcasting' => 'Magic', |
||
149 | 'Sports' => 'Sport', |
||
150 | 'Street Racing' => 'Cars', |
||
151 | 'Swimming' => 'Sport', |
||
152 | 'Swordplay' => 'Sport', |
||
153 | 'Tennis' => 'Sport', |
||
154 | 'Victorian Period' => 'History', |
||
155 | 'Volleyball' => 'Sport', |
||
156 | 'Witch' => 'Magic', |
||
157 | 'World War I' => 'War', |
||
158 | 'World War II' => 'War', |
||
159 | 'Wrestling' => 'Action', |
||
160 | ]; |
||
161 | |||
162 | /** |
||
163 | * @param Browser $browser |
||
164 | * @param Registry $doctrine |
||
165 | * @param Downloader $downloader |
||
166 | * @param SummaryCleaner $cleaner |
||
167 | * @param $locale |
||
168 | */ |
||
169 | public function __construct( |
||
170 | Browser $browser, |
||
171 | Registry $doctrine, |
||
172 | Downloader $downloader, |
||
173 | SummaryCleaner $cleaner, |
||
174 | $locale |
||
175 | ) { |
||
176 | $this->browser = $browser; |
||
177 | $this->doctrine = $doctrine; |
||
178 | $this->downloader = $downloader; |
||
179 | $this->cleaner = $cleaner; |
||
180 | $this->locale = $locale; |
||
181 | } |
||
182 | |||
183 | /** |
||
184 | * @return string |
||
185 | */ |
||
186 | public function getName() |
||
187 | { |
||
188 | return self::NAME; |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * @return string |
||
193 | */ |
||
194 | public function getTitle() |
||
195 | { |
||
196 | return self::TITLE; |
||
197 | } |
||
198 | |||
199 | /** |
||
200 | * @return FillerForm |
||
201 | */ |
||
202 | public function getForm() |
||
203 | { |
||
204 | return new FillerForm($this->browser->getHost()); |
||
0 ignored issues
–
show
|
|||
205 | } |
||
206 | |||
207 | /** |
||
208 | * Build menu for plugin. |
||
209 | * |
||
210 | * @param ItemInterface $item |
||
211 | * |
||
212 | * @return ItemInterface |
||
213 | */ |
||
214 | public function buildMenu(ItemInterface $item) |
||
215 | { |
||
216 | return parent::buildMenu($item) |
||
217 | ->setLinkAttribute('class', 'icon-label icon-label-plugin-anidb'); |
||
218 | } |
||
219 | |||
220 | /** |
||
221 | * Fill item from source. |
||
222 | * |
||
223 | * @param array $data |
||
224 | * |
||
225 | * @return Item|null |
||
226 | */ |
||
227 | public function fill(array $data) |
||
228 | { |
||
229 | if (empty($data['url']) || !is_string($data['url']) || |
||
230 | strpos($data['url'], $this->browser->getHost()) !== 0 || |
||
231 | !preg_match(self::REG_ITEM_ID, $data['url'], $match) |
||
232 | ) { |
||
233 | return null; |
||
234 | } |
||
235 | |||
236 | $body = $this->browser->get('anime', ['aid' => $match['id']]); |
||
237 | |||
238 | $item = new Item(); |
||
239 | $item->setEpisodesNumber($body->filter('episodecount')->text()); |
||
240 | $item->setDatePremiere(new \DateTime($body->filter('startdate')->text())); |
||
241 | $item->setDateEnd(new \DateTime($body->filter('enddate')->text())); |
||
242 | $item->setSummary($this->cleaner->clean($body->filter('description')->text())); |
||
243 | |||
244 | // set main source |
||
245 | $source = new Source(); |
||
246 | $source->setUrl($data['url']); |
||
247 | $item->addSource($source); |
||
248 | |||
249 | // add url to offsite |
||
250 | $source = new Source(); |
||
251 | $source->setUrl($body->filter('url')->text()); |
||
252 | $item->addSource($source); |
||
253 | |||
254 | // set complex data |
||
255 | $this->setCover($item, $body, $match['id']); |
||
256 | $this->setNames($item, $body); |
||
257 | $this->setEpisodes($item, $body); |
||
258 | $this->setType($item, $body); |
||
259 | $this->setGenres($item, $body); |
||
260 | |||
261 | return $item; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @param Item $item |
||
266 | * @param Crawler $body |
||
267 | * |
||
268 | * @return Item |
||
269 | */ |
||
270 | public function setNames(Item $item, Crawler $body) |
||
271 | { |
||
272 | $titles = $body->filter('titles > title'); |
||
273 | $names = []; |
||
274 | /* @var $title \DOMElement */ |
||
275 | foreach ($titles as $title) { |
||
276 | $lang = substr($title->attributes->item(0)->nodeValue, 0, 2); |
||
277 | if ($lang != 'x-') { |
||
278 | $names[$lang][$title->getAttribute('type')] = $title->nodeValue; |
||
279 | } |
||
280 | } |
||
281 | |||
282 | // set main name |
||
283 | if (!empty($names[$this->locale])) { |
||
284 | $item->setName($this->getNameForLocale($this->locale, $names)); |
||
285 | View Code Duplication | } elseif ($this->locale != 'en' && !empty($names['en'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
286 | $item->setName($this->getNameForLocale('en', $names)); |
||
287 | } else { |
||
288 | $item->setName($this->getNameForLocale(array_keys($names)[0], $names)); |
||
289 | } |
||
290 | |||
291 | // set other names |
||
292 | $other = []; |
||
293 | foreach ($names as $locales) { |
||
294 | foreach ($locales as $name) { |
||
295 | $other[] = $name; |
||
296 | } |
||
297 | } |
||
298 | $other = array_unique($other); |
||
299 | sort($other); |
||
300 | |||
301 | foreach ($other as $name) { |
||
302 | $item->addName((new Name())->setName($name)); |
||
303 | } |
||
304 | |||
305 | return $item; |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * @param Item $item |
||
310 | * @param Crawler $body |
||
311 | * @param string $id |
||
312 | * |
||
313 | * @return Item |
||
314 | */ |
||
315 | public function setCover(Item $item, Crawler $body, $id) |
||
316 | { |
||
317 | if ($image = $body->filter('picture')->text()) { |
||
318 | try { |
||
319 | $image = $this->browser->getImageUrl($image); |
||
320 | if ($path = parse_url($image, PHP_URL_PATH)) { |
||
321 | $ext = pathinfo($path, PATHINFO_EXTENSION); |
||
322 | $item->setCover(self::NAME.'/'.$id.'/cover.'.$ext); |
||
323 | $this->uploadImageFromUrl($image, $item); |
||
324 | } |
||
325 | } catch (\Exception $e) { |
||
326 | // error while retrieving images is not critical |
||
327 | } |
||
328 | } |
||
329 | |||
330 | return $item; |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * @param Item $item |
||
335 | * @param Crawler $body |
||
336 | * |
||
337 | * @return Item |
||
338 | */ |
||
339 | public function setEpisodes(Item $item, Crawler $body) |
||
340 | { |
||
341 | $episodes = ''; |
||
342 | foreach ($body->filter('episodes > episode') as $episode) { |
||
343 | $episode = new Crawler($episode); |
||
344 | $episodes .= $episode->filter('epno')->text().'. '.$this->getEpisodeTitle($episode)."\n"; |
||
345 | } |
||
346 | $item->setEpisodes(trim($episodes)); |
||
347 | |||
348 | return $item; |
||
349 | } |
||
350 | |||
351 | /** |
||
352 | * @param Crawler $episode |
||
353 | * |
||
354 | * @return string |
||
355 | */ |
||
356 | protected function getEpisodeTitle(Crawler $episode) |
||
357 | { |
||
358 | $titles = []; |
||
359 | /* @var $title \DOMElement */ |
||
360 | foreach ($episode->filter('title') as $title) { |
||
361 | $lang = substr($title->attributes->item(0)->nodeValue, 0, 2); |
||
362 | if ($lang == $this->locale) { |
||
363 | return $title->nodeValue; |
||
364 | } |
||
365 | if ($lang != 'x-') { |
||
366 | $titles[$lang] = $title->nodeValue; |
||
367 | } |
||
368 | } |
||
369 | |||
370 | // get EN lang or first |
||
371 | if (!empty($titles['en'])) { |
||
372 | return $titles['en']; |
||
373 | } else { |
||
374 | return array_shift($titles); |
||
375 | } |
||
376 | } |
||
377 | |||
378 | /** |
||
379 | * @param Item $item |
||
380 | * @param Crawler $body |
||
381 | * |
||
382 | * @return Item |
||
383 | */ |
||
384 | public function setType(Item $item, Crawler $body) |
||
385 | { |
||
386 | $rename = [ |
||
387 | 'TV Series' => 'TV', |
||
388 | 'Movie' => 'Feature', |
||
389 | 'Web' => 'ONA', |
||
390 | ]; |
||
391 | $type = $body->filter('anime > type')->text(); |
||
392 | $type = isset($rename[$type]) ? $rename[$type] : $type; |
||
393 | |||
394 | return $item->setType( |
||
395 | $this |
||
396 | ->doctrine |
||
397 | ->getRepository('AnimeDbCatalogBundle:Type') |
||
398 | ->findOneBy(['name' => $type]) |
||
399 | ); |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * @param Item $item |
||
404 | * @param Crawler $body |
||
405 | * |
||
406 | * @return Item |
||
407 | */ |
||
408 | public function setGenres(Item $item, Crawler $body) |
||
409 | { |
||
410 | $repository = $this->doctrine->getRepository('AnimeDbCatalogBundle:Genre'); |
||
411 | $categories = $body->filter('categories > category > name'); |
||
412 | foreach ($categories as $category) { |
||
413 | if (isset($this->category_to_genre[$category->nodeValue])) { |
||
414 | $genre = $repository->findOneBy(['name' => $this->category_to_genre[$category->nodeValue]]); |
||
415 | } else { |
||
416 | $genre = $repository->findOneBy(['name' => $category->nodeValue]); |
||
417 | } |
||
418 | if ($genre instanceof Genre) { |
||
419 | $item->addGenre($genre); |
||
420 | } |
||
421 | } |
||
422 | |||
423 | return $item; |
||
424 | } |
||
425 | |||
426 | /** |
||
427 | * @param string $url |
||
428 | * @param EntityInterface $entity |
||
429 | * |
||
430 | * @return bool |
||
431 | */ |
||
432 | protected function uploadImageFromUrl($url, EntityInterface $entity) |
||
433 | { |
||
434 | return $this->downloader->image($url, $this->downloader->getRoot().$entity->getWebPath()); |
||
435 | } |
||
436 | |||
437 | /** |
||
438 | * @param string $locale |
||
439 | * @param array $names |
||
440 | * |
||
441 | * @return string |
||
442 | */ |
||
443 | View Code Duplication | protected function getNameForLocale($locale, &$names) |
|
0 ignored issues
–
show
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. ![]() |
|||
444 | { |
||
445 | if (isset($names[$locale]['main'])) { |
||
446 | $name = $names[$locale]['main']; |
||
447 | unset($names[$locale]['main']); |
||
448 | } elseif (isset($names[$locale]['official'])) { |
||
449 | $name = $names[$locale]['official']; |
||
450 | unset($names[$locale]['official']); |
||
451 | } else { |
||
452 | $name = array_shift($names[$locale]); |
||
453 | } |
||
454 | |||
455 | return $name; |
||
456 | } |
||
457 | |||
458 | /** |
||
459 | * @param string $url |
||
460 | * |
||
461 | * @return bool |
||
462 | */ |
||
463 | public function isSupportedUrl($url) |
||
464 | { |
||
465 | return strpos($url, $this->browser->getHost()) === 0; |
||
466 | } |
||
467 | } |
||
468 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.