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\ShikimoriFillerBundle\Service; |
||
10 | |||
11 | use AnimeDb\Bundle\CatalogBundle\Plugin\Fill\Filler\Filler as FillerPlugin; |
||
12 | use AnimeDb\Bundle\ShikimoriBrowserBundle\Service\Browser; |
||
13 | use Doctrine\Bundle\DoctrineBundle\Registry; |
||
14 | use AnimeDb\Bundle\CatalogBundle\Entity\Item; |
||
15 | use AnimeDb\Bundle\CatalogBundle\Entity\Name; |
||
16 | use AnimeDb\Bundle\CatalogBundle\Entity\Source; |
||
17 | use AnimeDb\Bundle\CatalogBundle\Entity\Genre; |
||
18 | use AnimeDb\Bundle\CatalogBundle\Entity\Studio; |
||
19 | use AnimeDb\Bundle\CatalogBundle\Entity\Image; |
||
20 | use AnimeDb\Bundle\AppBundle\Service\Downloader; |
||
21 | use AnimeDb\Bundle\ShikimoriFillerBundle\Form\Type\Filler as FillerForm; |
||
22 | use Knp\Menu\ItemInterface; |
||
23 | use AnimeDb\Bundle\AppBundle\Service\Downloader\Entity\EntityInterface; |
||
24 | |||
25 | class Filler extends FillerPlugin |
||
26 | { |
||
27 | /** |
||
28 | * @var string |
||
29 | */ |
||
30 | const NAME = 'shikimori'; |
||
31 | |||
32 | /** |
||
33 | * @var string |
||
34 | */ |
||
35 | const TITLE = 'Shikimori.org'; |
||
36 | |||
37 | /** |
||
38 | * Path to item. |
||
39 | * |
||
40 | * @var string |
||
41 | */ |
||
42 | const FILL_URL = '/animes/#ID#'; |
||
43 | |||
44 | /** |
||
45 | * RegExp for get item id. |
||
46 | * |
||
47 | * @var string |
||
48 | */ |
||
49 | const REG_ITEM_ID = '#/animes/z?(?<id>\d+)\-#'; |
||
50 | |||
51 | /** |
||
52 | * Path to item screenshots. |
||
53 | * |
||
54 | * @var string |
||
55 | */ |
||
56 | const FILL_IMAGES_URL = '/animes/#ID#/screenshots'; |
||
57 | |||
58 | /** |
||
59 | * World-art item url. |
||
60 | * |
||
61 | * @var string |
||
62 | */ |
||
63 | const WORLD_ART_URL = 'http://www.world-art.ru/animation/animation.php?id=#ID#'; |
||
64 | |||
65 | /** |
||
66 | * MyAnimeList item url. |
||
67 | * |
||
68 | * @var string |
||
69 | */ |
||
70 | const MY_ANIME_LIST_URL = 'http://myanimelist.net/anime/#ID#'; |
||
71 | |||
72 | /** |
||
73 | * AniDB item url. |
||
74 | * |
||
75 | * @var string |
||
76 | */ |
||
77 | const ANI_DB_URL = 'http://anidb.net/perl-bin/animedb.pl?show=anime&aid=#ID#'; |
||
78 | |||
79 | /** |
||
80 | * @var Browser |
||
81 | */ |
||
82 | private $browser; |
||
83 | |||
84 | /** |
||
85 | * @var Registry |
||
86 | */ |
||
87 | private $doctrine; |
||
88 | |||
89 | /** |
||
90 | * @var Downloader |
||
91 | */ |
||
92 | private $downloader; |
||
93 | |||
94 | /** |
||
95 | * @var string |
||
96 | */ |
||
97 | protected $locale; |
||
98 | |||
99 | /** |
||
100 | * @param Browser $browser |
||
101 | * @param Registry $doctrine |
||
102 | * @param Downloader $downloader |
||
103 | * @param string $locale |
||
104 | */ |
||
105 | public function __construct(Browser $browser, Registry $doctrine, Downloader $downloader, $locale) |
||
106 | { |
||
107 | $this->browser = $browser; |
||
108 | $this->doctrine = $doctrine; |
||
109 | $this->downloader = $downloader; |
||
110 | $this->locale = $locale; |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * @return string |
||
115 | */ |
||
116 | public function getName() |
||
117 | { |
||
118 | return self::NAME; |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * @return string |
||
123 | */ |
||
124 | public function getTitle() |
||
125 | { |
||
126 | return self::TITLE; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * @return FillerForm |
||
131 | */ |
||
132 | public function getForm() |
||
133 | { |
||
134 | return new FillerForm($this->browser->getHost()); |
||
0 ignored issues
–
show
|
|||
135 | } |
||
136 | |||
137 | /** |
||
138 | * @param ItemInterface $item |
||
139 | * |
||
140 | * @return ItemInterface |
||
141 | */ |
||
142 | public function buildMenu(ItemInterface $item) |
||
143 | { |
||
144 | return parent::buildMenu($item) |
||
145 | ->setLinkAttribute('class', 'icon-label icon-label-plugin-shikimori'); |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Fill item from source. |
||
150 | * |
||
151 | * @param array $data |
||
152 | * |
||
153 | * @return Item|null |
||
154 | */ |
||
155 | public function fill(array $data) |
||
156 | { |
||
157 | if (empty($data['url']) || !is_string($data['url']) || |
||
158 | strpos($data['url'], $this->browser->getHost()) !== 0 || |
||
159 | !preg_match(self::REG_ITEM_ID, $data['url'], $match) |
||
160 | ) { |
||
161 | return null; |
||
162 | } |
||
163 | $path = str_replace('#ID#', $match['id'], self::FILL_URL); |
||
164 | $body = $this->browser->get($path); |
||
165 | |||
166 | $item = new Item(); |
||
167 | $item->setDuration($body['duration']); |
||
168 | $item->setSummary($body['description']); |
||
169 | $item->setDatePremiere(new \DateTime($body['aired_on'])); |
||
170 | if ($body['released_on']) { |
||
171 | $item->setDateEnd(new \DateTime($body['released_on'])); |
||
172 | } |
||
173 | $ep_num = $body['episodes_aired'] ? $body['episodes_aired'] : $body['episodes']; |
||
174 | $item->setEpisodesNumber($ep_num.($body['ongoing'] ? '+' : '')); |
||
175 | |||
176 | // set main source |
||
177 | $source = new Source(); |
||
178 | $source->setUrl($data['url']); |
||
179 | $item->addSource($source); |
||
180 | |||
181 | // set complex data |
||
182 | $this->setSources($item, $body); |
||
183 | $this->setCover($item, $body); |
||
184 | $this->setType($item, $body); |
||
185 | $this->setNames($item, $body); |
||
186 | $this->setGenres($item, $body); |
||
187 | $this->setStudio($item, $body); |
||
188 | |||
189 | if (!empty($data['frames'])) { |
||
190 | $this->setImages($item, $body); |
||
191 | } |
||
192 | |||
193 | return $item; |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * @param Item $item |
||
198 | * @param array $body |
||
199 | * |
||
200 | * @return Item |
||
201 | */ |
||
202 | public function setSources(Item $item, $body) |
||
203 | { |
||
204 | $sources = [ |
||
205 | 'ani_db_id' => self::ANI_DB_URL, |
||
206 | 'world_art_id' => self::WORLD_ART_URL, |
||
207 | 'myanimelist_id' => self::MY_ANIME_LIST_URL, |
||
208 | ]; |
||
209 | |||
210 | foreach ($sources as $key => $url) { |
||
211 | if (!empty($body[$key])) { |
||
212 | $source = new Source(); |
||
213 | $source->setUrl(str_replace('#ID#', $body[$key], $url)); |
||
214 | $item->addSource($source); |
||
215 | } |
||
216 | } |
||
217 | |||
218 | return $item; |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * @param Item $item |
||
223 | * @param array $body |
||
224 | * |
||
225 | * @return Item |
||
226 | */ |
||
227 | public function setNames(Item $item, $body) |
||
228 | { |
||
229 | $names = []; |
||
230 | // set a name based on the locale |
||
231 | if ($this->locale == 'ru' && $body['russian']) { |
||
232 | $names = array_merge( |
||
233 | [$body['name']], |
||
234 | $body['english'], |
||
235 | $body['japanese'], |
||
236 | $body['synonyms'] |
||
237 | ); |
||
238 | $item->setName($body['russian']); |
||
239 | } elseif ($this->locale == 'ja' && $body['japanese']) { |
||
240 | $item->setName(array_shift($body['japanese'])); |
||
241 | $names = array_merge( |
||
242 | [$body['name']], |
||
243 | [$body['russian']], |
||
244 | $body['english'], |
||
245 | $body['japanese'], |
||
246 | $body['synonyms'] |
||
247 | ); |
||
248 | } |
||
249 | |||
250 | // default list names |
||
251 | if (!$item->getName()) { |
||
252 | $names = array_merge( |
||
253 | [$body['russian']], |
||
254 | $body['english'], |
||
255 | $body['japanese'], |
||
256 | $body['synonyms'] |
||
257 | ); |
||
258 | $item->setName($body['name']); |
||
259 | } |
||
260 | |||
261 | foreach ($names as $value) { |
||
262 | if ($value != $body['name']) { |
||
263 | $item->addName((new Name())->setName($value)); |
||
264 | } |
||
265 | } |
||
266 | |||
267 | return $item; |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * @param Item $item |
||
272 | * @param array $body |
||
273 | * |
||
274 | * @return Item |
||
275 | */ |
||
276 | public function setCover(Item $item, $body) |
||
277 | { |
||
278 | if (!empty($body['image']) && !empty($body['image']['original'])) { |
||
279 | try { |
||
280 | if ($path = parse_url($body['image']['original'], PHP_URL_PATH)) { |
||
281 | $item->setCover(self::NAME.'/'.$body['id'].'/cover.'.pathinfo($path, PATHINFO_EXTENSION)); |
||
282 | $this->uploadImage($this->browser->getHost().$body['image']['original'], $item); |
||
283 | } |
||
284 | } catch (\Exception $e) { |
||
285 | // error while retrieving images is not critical |
||
286 | } |
||
287 | } |
||
288 | |||
289 | return $item; |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * @param Item $item |
||
294 | * @param array $body |
||
295 | * |
||
296 | * @return Item |
||
297 | */ |
||
298 | public function setType(Item $item, $body) |
||
299 | { |
||
300 | $rename = [ |
||
301 | 'Movie' => 'Feature', |
||
302 | 'Music' => 'Music video', |
||
303 | 'Special' => 'TV-special', |
||
304 | ]; |
||
305 | $type = ucfirst($body['kind']); |
||
306 | $type = isset($rename[$type]) ? $rename[$type] : $type; |
||
307 | |||
308 | return $item->setType( |
||
309 | $this |
||
310 | ->doctrine |
||
311 | ->getRepository('AnimeDbCatalogBundle:Type') |
||
312 | ->findOneBy(['name' => $type]) |
||
313 | ); |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * @param Item $item |
||
318 | * @param array $body |
||
319 | * |
||
320 | * @return Item |
||
321 | */ |
||
322 | public function setGenres(Item $item, $body) |
||
323 | { |
||
324 | $repository = $this->doctrine->getRepository('AnimeDbCatalogBundle:Genre'); |
||
325 | $rename = [ |
||
326 | 'Martial Arts' => 'Martial arts', |
||
327 | 'Shoujo Ai' => 'Shoujo-ai', |
||
328 | 'Shounen Ai' => 'Shounen-ai', |
||
329 | 'Sports' => 'Sport', |
||
330 | 'Slice of Life' => 'Slice of life', |
||
331 | 'Sci-Fi' => 'Sci-fi', |
||
332 | 'Historical' => 'History', |
||
333 | 'Military' => 'War', |
||
334 | ]; |
||
335 | |||
336 | foreach ($body['genres'] as $genre) { |
||
337 | $genre = isset($rename[$genre['name']]) ? $rename[$genre['name']] : $genre['name']; |
||
338 | $genre = $repository->findOneBy(['name' => $genre]); |
||
339 | if ($genre instanceof Genre) { |
||
340 | $item->addGenre($genre); |
||
341 | } |
||
342 | } |
||
343 | |||
344 | return $item; |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * @param Item $item |
||
349 | * @param array $body |
||
350 | * |
||
351 | * @return Item |
||
352 | */ |
||
353 | public function setStudio(Item $item, $body) |
||
354 | { |
||
355 | $repository = $this->doctrine->getRepository('AnimeDbCatalogBundle:Studio'); |
||
356 | $rename = [ |
||
357 | 'Arms' => 'Arms Corporation', |
||
358 | 'Mushi Productions' => 'Mushi Production', |
||
359 | 'Film Roman, Inc.' => 'Film Roman', |
||
360 | 'Tezuka Production' => 'Tezuka Productions', |
||
361 | 'CoMix Wave' => 'CoMix Wave Inc.', |
||
362 | ]; |
||
363 | |||
364 | foreach ($body['studios'] as $studio) { |
||
365 | $name = isset($rename[$studio['name']]) ? $rename[$studio['name']] : $studio['name']; |
||
366 | $name = $studio['name'] != $studio['filtered_name'] ? [$name, $studio['filtered_name']] : $name; |
||
367 | $studio = $repository->findOneBy(['name' => $name]); |
||
368 | if ($studio instanceof Studio) { |
||
369 | $item->setStudio($studio); |
||
370 | break; |
||
371 | } |
||
372 | } |
||
373 | |||
374 | return $item; |
||
375 | } |
||
376 | |||
377 | /** |
||
378 | * @param Item $item |
||
379 | * @param array $body |
||
380 | * |
||
381 | * @return Item |
||
382 | */ |
||
383 | public function setImages(Item $item, $body) |
||
384 | { |
||
385 | $images = $this->browser->get(str_replace('#ID#', $body['id'], self::FILL_IMAGES_URL)); |
||
386 | |||
387 | foreach ($images as $image) { |
||
388 | if ($path = parse_url($image['original'], PHP_URL_PATH)) { |
||
389 | $image = new Image(); |
||
390 | $image->setSource(self::NAME.'/'.$body['id'].'/'.pathinfo($path, PATHINFO_BASENAME)); |
||
391 | if ($this->uploadImage($this->browser->getHost().$image['original'], $image)) { |
||
392 | $item->addImage($image); |
||
393 | } |
||
394 | } |
||
395 | } |
||
396 | |||
397 | return $item; |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * @param string $url |
||
402 | * @param EntityInterface $entity |
||
403 | * |
||
404 | * @return bool |
||
405 | */ |
||
406 | protected function uploadImage($url, EntityInterface $entity) |
||
407 | { |
||
408 | return $this->downloader->image($url, $this->downloader->getRoot().$entity->getWebPath()); |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * @param string $url |
||
413 | * |
||
414 | * @return bool |
||
415 | */ |
||
416 | public function isSupportedUrl($url) |
||
417 | { |
||
418 | return strpos($url, $this->browser->getHost()) === 0; |
||
419 | } |
||
420 | } |
||
421 |
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.