]> Raphaël G. Git Repositories - blogbundle/blob - DataFixtures/ORM/Fixtures.php
Add repository classes
[blogbundle] / DataFixtures / ORM / Fixtures.php
1 <?php
2
3 namespace AppBundle\DataFixtures\ORM;
4
5 #Article.php ArticleTranslation.php Author.php Keyword.php KeywordTranslation.php Language.php Site.php SiteTranslation.php
6
7 class Fixtures extends \Doctrine\Bundle\FixturesBundle\Fixture {
8 public function load(\Doctrine\Common\Persistence\ObjectManager $manager) {
9 //Language tree
10 $langTree = array(
11 'fr' => array(
12 'iso6391' => 'fr',
13 'iso6393' => 'fra',
14 'locales' => array(
15 'fr' => 'Français',
16 'en' => 'French'
17 )
18 ),
19 'en' => array(
20 'iso6391' => 'en',
21 'iso6393' => 'eng',
22 'locales' => array(
23 'en' => 'English',
24 'fr' => 'Anglais'
25 )
26 )
27 );
28
29 //Create languages
30 $languages = array();
31 foreach($langTree as $langs) {
32 $language = new \Rapsys\BlogBundle\Entity\Language();
33 $language->setIso6391($langs['iso6391']);
34 $language->setIso6393($langs['iso6393']);
35 $language->setCreated(new \DateTime('now'));
36 $language->setUpdated(new \DateTime('now'));
37 $manager->persist($language);
38 $languages[$langs['iso6391']] = $language;
39 unset($language);
40 }
41
42 //Flush to get the ids
43 $manager->flush();
44
45 //Create language translations
46 foreach($langTree as $target => $langs) {
47 foreach($langs['locales'] as $lang => $title) {
48 $languageTranslation = new \Rapsys\BlogBundle\Entity\LanguageTranslation();
49 $languageTranslation->setTitle($title);
50 $languageTranslation->setLanguage($languages[$lang]);
51 $languageTranslation->setLanguageId($languages[$lang]->getId());
52 $languageTranslation->setTarget($languages[$target]);
53 $languageTranslation->setTargetId($languages[$target]->getId());
54 $languageTranslation->setCreated(new \DateTime('now'));
55 $languageTranslation->setUpdated(new \DateTime('now'));
56 $manager->persist($languageTranslation);
57 $manager->flush();
58 unset($languageTranslation);
59 }
60 }
61
62 //Create 3 sites
63 $sites = array();
64 foreach(array('blog.rapsys.eu', 'www.rapsys.eu', 'www.aoihime.eu') as $domain) {
65 $site = new \Rapsys\BlogBundle\Entity\Site();
66 $site->setDomain($domain);
67 $site->setCreated(new \DateTime('now'));
68 $site->setUpdated(new \DateTime('now'));
69 $manager->persist($site);
70 $sites[] = $site;
71 unset($site);
72 }
73
74 //Author tree
75 $authorTree = array(
76 'Ernest Hemingway' => array(
77 'fr' => 'Né le 21 juillet 1899 à Oak Park dans l\'Illinois aux États-Unis, mort le 2 juillet 1961 à Ketchum, fût un écrivain, journaliste et correspondant de guerre américain.',
78 'en' => 'Born on July 21, 1899 in Oak Park, Illinois, deceased on July 2, 1961 in Ketchum, was an American novelist, short story writer and journalist.'
79 ),
80 'William Shakespeare' => array(
81 'fr' => 'Baptisé le 26 avril 1564 à Stratford-upon-Avon et mort le 23 avril 1616 dans la même ville, est considéré comme l\'un des plus grands poètes, dramaturges et écrivains de la culture anglaise.',
82 'en' => 'Baptised on April 26, 1564, deceased on April 23, 1616, was an English poet, playwright and actor, widely regarded as the greatest writer in the English language and the world\'s pre-eminent dramatist.'
83 ),
84 'George Orwell' => array(
85 'fr' => 'Eric Arthur Blair, né le 25 juin 1903 à Motihari pendant la période du Raj britannique et mort le 21 janvier 1950 à Londres, plus connu sous son nom de plume, est un écrivain et journaliste anglais.',
86 'en' => 'Eric Arthur Blair, born on June 25, 1903, deceased on January 21, 1950, better known by his pen name, was an English novelist, essayist, journalist, and critic.'
87 )
88 );
89
90 //Create 3 authors
91 $authors = array();
92 foreach($authorTree as $name => $data) {
93 $author = new \Rapsys\BlogBundle\Entity\Author();
94 $author->setName($name);
95 $author->setCreated(new \DateTime('now'));
96 $author->setUpdated(new \DateTime('now'));
97 $manager->persist($author);
98 //Flush to get the id
99 $manager->flush();
100 $authors[] = $author;
101 foreach($data as $lang => $description) {
102 $authorTranslation = new \Rapsys\BlogBundle\Entity\AuthorTranslation();
103 $authorTranslation->setDescription($description);
104 $authorTranslation->setAuthor($author);
105 $authorTranslation->setAuthorId($author->getId());
106 $authorTranslation->setLanguage($languages[$lang]);
107 $authorTranslation->setLanguageId($languages[$lang]->getId());
108 $authorTranslation->setCreated(new \DateTime('now'));
109 $authorTranslation->setUpdated(new \DateTime('now'));
110 $manager->persist($authorTranslation);
111 }
112 unset($author);
113 }
114
115 //Keyword tree
116 $keywordTree = array(
117 'disqus' => array(
118 'fr' => 'Disqus',
119 'en' => 'Disqus'
120 ),
121 'varnish' => array(
122 'fr' => 'Varnish',
123 'en' => 'Varnish'
124 ),
125 'cidr' => array(
126 'fr' => 'Agrégation d\'adresses IP',
127 'en' => 'IP addresses aggregation'
128 ),
129 );
130
131 //Create 3 keywords
132 $keywords = array();
133 foreach($keywordTree as $name => $data) {
134 $keyword = new \Rapsys\BlogBundle\Entity\Keyword();
135 $keyword->setCreated(new \DateTime('now'));
136 $keyword->setUpdated(new \DateTime('now'));
137 $manager->persist($keyword);
138 //Flush to get the id
139 $manager->flush();
140 $keywords[] = $keyword;
141 foreach($data as $lang => $title) {
142 $keywordTranslation = new \Rapsys\BlogBundle\Entity\KeywordTranslation();
143 $keywordTranslation->setTitle($title);
144 $keywordTranslation->setKeyword($keyword);
145 $keywordTranslation->setKeywordId($keyword->getId());
146 $keywordTranslation->setLanguage($languages[$lang]);
147 $keywordTranslation->setLanguageId($languages[$lang]->getId());
148 $keywordTranslation->setCreated(new \DateTime('now'));
149 $keywordTranslation->setUpdated(new \DateTime('now'));
150 $manager->persist($keywordTranslation);
151 }
152 unset($keyword);
153 }
154
155 //Article tree
156 $articleTree = array(
157 array(
158 'fr' => array(
159 'title' => 'Comment détecter la tranparence dans des images PNG en PHP de manière fiable',
160 'description' => 'J\'ai récemment du trouver comment détecter en PHP les images PNG transparentes.
161 Les codes trouvés ne semblaient pas fonctionner de manière satisfaisante pour les différents types de PNG à contrôler.
162 Voici la fonction que j\'ai fini par utiliser.',
163 'body' => 'J\'ai récemment du trouver comment détecter en PHP les images PNG transparentes.
164 Les codes trouvés ne semblaient pas fonctionner de manière satisfaisante pour les différents types de PNG à contrôler.
165 J\'ai fini par utiliser la fonction suivante:
166 [code=php]
167 function png_has_transparency($im) {
168 //Retrieve content from imagick object
169 $content = $im->getImageBlob();
170
171 //Detect 32bit png (each pixel has tranparency level)
172 if (ord(substr($content, 25, 1)) & 4) {
173 //Fetch iterator
174 $p = $im->getPixelIterator();
175
176 //Loop on each row
177 foreach($p as $r) {
178 //Loop on each row pixel
179 foreach($r as $pix) {
180 //Check if pixel has partial transparency
181 if ($pix->getColorValue(Imagick::COLOR_ALPHA) != 1) {
182 return true;
183 }
184 }
185 }
186 //Check 8bit png transparency
187 } elseif (stripos($content, \'PLTE\') !== false || stripos($content, \'tRNS\') !== false) {
188 return true;
189 }
190
191 //Didn\'t found clue of transparency
192 return false;
193 }
194 [/code]
195
196 Cette fonction fonctionne avec les deux seules possibilitées : PNG 32bit et 8bit.
197
198 Le premier cas est un PNG 32bit avec transparence activé, on doit alors vérifier l\'opacité de chaque pixel savoir si l\'image a de la transparence ou non.
199
200 Le second cas est un PNG 8 bit, on a simplement à détecter un marqueur de transparence dans le contenu du fichier.
201
202 Dans cette configuration de fonction, on lit seulement une partie du PNG 32bit jusqu\'à détection d\'un pixel transparent ou on analyse le contenu jusqu\'à trouver un marqueur de transparence dans un PNG 8bit.
203
204 Les pires cas seront un PNG 32bit avec marqueur de transparence sans pixel transparent ou PNG 8bit sans marqueur de transparence.
205
206 Selon les probabilités de rencontrer les différents cas de transparence vous pouvez être intéressé pour renverser l\'ordre des tests de cette fonction.
207
208 Un grand merci à ces articles qui expliquent plus en détail comment fonctionne les différentes parties de ce code:
209 [ul]
210 [li]https://www.jonefox.com/blog/2011/04/15/how-to-detect-transparency-in-png-images/[/li]
211 [li]http://camendesign.com/code/uth1_is-png-32bit[/li]
212 [li]https://stackoverflow.com/questions/5495275/how-to-check-if-an-image-has-transparency-using-gd[/li]
213 [/ul]
214
215 En espérant que ça aidera quelqu\'un quelque part.'
216 ),
217 'en' => array(
218 'title' => 'How to reliably detect PNG image transparency with PHP',
219 'description' => 'I recently had to find out if a PNG has transparency using PHP.
220 All the code I found didn\'t seemed to work correctly for the various kind of PNG I had to deal with.
221 Here is the function I used.',
222 'body' => 'I recently had to find out if a PNG has transparency using PHP.
223 All the code I found didn\'t seemed to work correctly for the various kind of PNG I had to deal with.
224
225 I finished using the following function:
226 [code=php]
227 function png_has_transparency($im) {
228 //Retrieve content from imagick object
229 $content = $im->getImageBlob();
230
231 //Detect 32bit png (each pixel has tranparency level)
232 if (ord(substr($content, 25, 1)) & 4) {
233 //Fetch iterator
234 $p = $im->getPixelIterator();
235
236 //Loop on each row
237 foreach($p as $r) {
238 //Loop on each row pixel
239 foreach($r as $pix) {
240 //Check if pixel has partial transparency
241 if ($pix->getColorValue(Imagick::COLOR_ALPHA) != 1) {
242 return true;
243 }
244 }
245 }
246 //Check 8bit png transparency
247 } elseif (stripos($content, \'PLTE\') !== false || stripos($content, \'tRNS\') !== false) {
248 return true;
249 }
250
251 //Didn\'t found clue of transparency
252 return false;
253 }
254 [/code]
255
256 This function works with the only two transparency possibilities: 32bit and 8bit PNG.
257
258 The first case is a 32bit PNG with transparency enabled, we have then to check every pixel to detect if it has transparent part or not.
259
260 The second case is a 8bit PNG, then we only have to look the file content for transparency markers.
261
262 In this function configuration, we only read part of the file in 32bit PNG until we detect one transparent pixel or parse content until transparency marker is detected in 8bit PNG.
263
264 The worst case scenario will be 32bit PNG with transparency flag without transparency or 8bit PNG without transparency flag.
265
266 Depending on how likely you are to have transparency in each cases you might want to reverse the flow of this function.
267
268 Big thanks to these articles which expains how these parts work in a bit more detail:
269 [ul]
270 [li]https://www.jonefox.com/blog/2011/04/15/how-to-detect-transparency-in-png-images/[/li]
271 [li]http://camendesign.com/code/uth1_is-png-32bit[/li]
272 [li]https://stackoverflow.com/questions/5495275/how-to-check-if-an-image-has-transparency-using-gd[/li]
273 [/ul]
274
275 Hope this helps someone else out there.'
276 )
277 ),
278 array(
279 'fr' => array(
280 'title' => 'Mise en cache de webservice avec varnish',
281 'description' => 'J\'ai eu récemment à trouver comment mettre en cache les réponse d\'un webservice.
282 Voici la configuration varnish qui a répondu à mes besoins.',
283 'body' => 'J\'ai eu récemment à trouver comment mettre en cache les réponse d\'un webservice.
284
285 L\'API RESTfull du webservice sert de passerelle entre un API privé HATEOAS et un client générant plus de 500 000 requêtes par jour.
286
287 La première surprise est qu\'un client bien élevé, envoyant un en-tête Authorization: Bearer <token>, ne sera pas mis en cache par Varnish par défaut !
288
289 Forçons le fonctionnement standard avec l\'en-tête pour le préfixe de l\'uri de notre webservice:
290 [code=varnish]
291 sub vcl_recv {
292 # Force la mise en cache de la réponse même avec req.http.Authorization présent
293 if (req.http.Authorization) {
294 if (req.url ~ "^/webservice/uri/prefix/") {
295 return (lookup);
296 }
297 }
298 }
299 [/code]
300
301 Ce changement a des conséquences sur la sécurité, n\'importe qui autorisé à interroger Varnish sera en mesure de récupérer un résultat en cache sans s\'identifier.
302
303 Il peut être une bonne idée de valider la valeur de l\'en-tête Authorization avant de fournir le résultat en cache.
304
305 Notre webservice a trois réponses possibles :
306 [ul]
307 [li]200: les données en JSON[/li]
308 [li]404: données non trouvées[/li]
309 [li]410: données plus jamais disponibles[/li]
310 [/ul]
311
312 Mettons en cache les résultats selon le code de retour :
313 [code=varnish]
314 sub vcl_fetch {
315 if (req.url ~ "^/webservice/uri/prefix/") {
316 if (beresp.status == 404) {
317 set beresp.ttl = 7d;
318 }
319 if (beresp.status == 410) {
320 set beresp.ttl = 7d;
321 }
322 if (beresp.status == 200) {
323 set beresp.ttl = 24h;
324 }
325 }
326 }
327 [/code]
328
329 Avec cette configuration, on a divisé par 5 la quantité de demandes sur notre passerelle
330 pour le client qui n\'était pas en mesure de mettre en cache lui-même nos résultats.'
331 ),
332 'en' => array(
333 'title' => 'Caching webservice with varnish',
334 'description' => 'I recently had to find a way to cache a webservice anwsers.
335 Here is the Varnish configuration fitting my needs.',
336 'body' => 'I recently had to find a way to cache a webservice anwsers.
337
338 The webservice is a RESTfull API serving as a gateway between a private HATEOAS API and a client generating more than 500 000 requests a day.
339
340 The first surprise is that if your well educated client, sending you a header Authorization: Bearer <token>, will not be cached by default by Varnish !
341
342 Let\'s force back the standard behaviour with this header for our webservice uri prefix:
343 [code=varnish]
344 sub vcl_recv {
345 # Force cache response even with req.http.Authorization set
346 if (req.http.Authorization) {
347 if (req.url ~ "^/webservice/uri/prefix/") {
348 return (lookup);
349 }
350 }
351 }
352 [/code]
353
354 This has security implication, now anyone allowed to request varnish will be able to retrieve a cached result without authentification.
355
356 It may be a good idea to validate the Authorization header value before serving the cached result.
357
358 Now, our webservice has three possibles answers :
359 [ul]
360 [li]200: the data in JSON[/li]
361 [li]404: data was not found[/li]
362 [li]410: data is not available anymore[/li]
363 [/ul]
364
365 Let\'s cache our results depending on the reponse code:
366 [code=varnish]
367 sub vcl_fetch {
368 if (req.url ~ "^/webservice/uri/prefix/") {
369 if (beresp.status == 404) {
370 set beresp.ttl = 7d;
371 }
372 if (beresp.status == 410) {
373 set beresp.ttl = 7d;
374 }
375 if (beresp.status == 200) {
376 set beresp.ttl = 24h;
377 }
378 }
379 }
380 [/code]
381
382 With this configuration, we divided by 5 the quantity of request on our gateway
383 from the client who was not able to cache our result himself.'
384 )
385 ),
386 array(
387 'fr' => array(
388 'title' => 'Gestion des plages d\'IP en PHP/MySQL',
389 'description' => 'J\'ai récemment du ',
390 'description' => 'J\'ai eu récemment à trouver comment restreindre l\'accès à un service en ligne à certaines plages d\'IP. Voici la solution qui a répondu à mes besoins.',
391 'body' => 'J\'ai récemment du autoriser l\'accès à un service en ligne à seulement quelques plages d\'IP.
392
393 Premièrement, voyons comment calculer la première et la dernière adresse IP d\'une plage (bloc CIDR) avec sa base et son masque :
394 [code=php]
395 $range = array(\'127.0.0.1\', 8);
396 function rangeBegin($range) {
397 return $range[0];
398 }
399 function rangeEnd($range) {
400 return long2ip(ip2long($range[0]) | ((1 << (32 - $range[1])) - 1));
401 }
402 [/code]
403
404 Maintenant comment vérifier si une IP est présente dans une plage (bloc CIDR) :
405 [code=php]
406 $ip = \'127.0.0.1\';
407 $range = array(\'127.0.0.1\', 8);
408 function ipInRange($ip, $range) {
409 if (ip2long($range[0]) <= ip2long($ip) && ip2long($ip) <= (ip2long($range[0]) | ((1 << (32 - $range[1])) - 1))) {
410 return true;
411 }
412 return false;
413 }
414 [/code]
415
416 En premier bonus, comment récupérer les plages d\'IP d\'amazon :
417 [code=php]
418 function fetchAmazonRange() {
419 //Init array
420 $amazonRanges = array();
421
422 $ctx = stream_context_create(
423 array(
424 \'http\' => array(
425 \'method\' => \'GET\',
426 \'max_redirects\' => 0,
427 \'timeout\' => 5,
428 \'ignore_errors\' => false,
429 \'header\' => array(
430 \'Connection: close\',
431 \'Accept: application/json\'
432 )
433 )
434 )
435 );
436
437 //Fetch json
438 if (($json = file_get_contents(\'https://ip-ranges.amazonaws.com/ip-ranges.json\', false, $ctx)) === false) {
439 return null;
440 }
441
442 //Decode it
443 if (($json = json_decode($json)) === null || empty($json->prefixes)) {
444 return false;
445 }
446
447 //Deal with prefixes
448 foreach($json->prefixes as $range) {
449 //Skip ipv6 and invalid ranges
450 if (empty($range->ip_prefix)||!preg_match(\'/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\/([0-9]+)$/\', $range->ip_prefix, $matche
451 s)) {
452 continue;
453 }
454 //Remove whole match
455 array_shift($matches);
456 //Add ip and mask
457 $amazonRanges[] = $matches;
458 }
459
460 //Send back result
461 return $amazonRanges;
462 }
463 [/code]
464
465 Urls pour les plages d\'IP de Microsoft Azure :
466 [ul]
467 [li]https://www.microsoft.com/en-us/download/details.aspx?id=41653[/li]
468 [li]https://msdn.microsoft.com/library/mt757330.aspx[/li]
469 [/ul]'
470 ),
471 'en' => array(
472 'title' => 'Dealing with IP range in PHP/MySQL',
473 'description' => 'I recently had to deal with CIDR blocks to tighten some webservice security.
474 Here is how I designed it to fulfill my needs.',
475 'body' => 'I recently had to deal with CIDR blocks to tighten some webservice security.
476
477 First let\'s see how to compute the first and last address of an IP range with just the block base IP and mask:
478 [code=php]
479 $range = array(\'127.0.0.1\', 8);
480 function rangeBegin($range) {
481 return $range[0];
482 }
483 function rangeEnd($range) {
484 return long2ip(ip2long($range[0]) | ((1 << (32 - $range[1])) - 1));
485 }
486 [/code]
487
488 How to detect if an IP is present in a CIDR block:
489 [code=php]
490 $ip = \'127.0.0.1\';
491 $range = array(\'127.0.0.1\', 8);
492 function ipInRange($ip, $range) {
493 if (ip2long($range[0]) <= ip2long($ip) && ip2long($ip) <= (ip2long($range[0]) | ((1 << (32 - $range[1])) - 1))) {
494 return true;
495 }
496 return false;
497 }
498 [/code]
499
500 As a first bonus, how to retrieve amazon IP ranges:
501 [code=php]
502 function fetchAmazonRange() {
503 //Init array
504 $amazonRanges = array();
505
506 $ctx = stream_context_create(
507 array(
508 \'http\' => array(
509 \'method\' => \'GET\',
510 \'max_redirects\' => 0,
511 \'timeout\' => 5,
512 \'ignore_errors\' => false,
513 \'header\' => array(
514 \'Connection: close\',
515 \'Accept: application/json\'
516 )
517 )
518 )
519 );
520
521 //Fetch json
522 if (($json = file_get_contents(\'https://ip-ranges.amazonaws.com/ip-ranges.json\', false, $ctx)) === false) {
523 return null;
524 }
525
526 //Decode it
527 if (($json = json_decode($json)) === null || empty($json->prefixes)) {
528 return false;
529 }
530
531 //Deal with prefixes
532 foreach($json->prefixes as $range) {
533 //Skip ipv6 and invalid ranges
534 if (empty($range->ip_prefix)||!preg_match(\'/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\/([0-9]+)$/\', $range->ip_prefix, $matche
535 s)) {
536 continue;
537 }
538 //Remove whole match
539 array_shift($matches);
540 //Add ip and mask
541 $amazonRanges[] = $matches;
542 }
543
544 //Send back result
545 return $amazonRanges;
546 }
547 [/code]
548
549 Microsoft Azure Ip ranges urls:
550 [ul]
551 [li]https://www.microsoft.com/en-us/download/details.aspx?id=41653[/li]
552 [li]https://msdn.microsoft.com/library/mt757330.aspx[/li]
553 [/ul]'
554 )
555 ),
556 );
557
558 //Create 3 articles
559 foreach($articleTree as $i => $data) {
560 $article = new \Rapsys\BlogBundle\Entity\Article();
561 $article->setCreated(new \DateTime('now'));
562 $article->setUpdated(new \DateTime('now'));
563 $article->addKeyword($keywords[$i]);
564 $article->setSite($sites[$i]);
565 $article->setAuthor($authors[$i]);
566 $manager->persist($article);
567 //Flush to get the id
568 $manager->flush();
569 $articles[] = $article;
570 foreach($data as $lang => $datas) {
571 $articleTranslation = new \Rapsys\BlogBundle\Entity\ArticleTranslation();
572 $articleTranslation->setTitle($datas['title']);
573 $articleTranslation->setDescription($datas['description']);
574 $articleTranslation->setBody($datas['body']);
575 $articleTranslation->setArticle($article);
576 $articleTranslation->setArticleId($article->getId());
577 $articleTranslation->setLanguage($languages[$lang]);
578 $articleTranslation->setLanguageId($languages[$lang]->getId());
579 $articleTranslation->setCreated(new \DateTime('now'));
580 $articleTranslation->setUpdated(new \DateTime('now'));
581 $manager->persist($articleTranslation);
582 }
583 unset($article);
584 }
585
586 $manager->flush();
587 }
588 }
589
590 //TODO:
591 //- add disqus article
592 //- add code for ip range detection
593 //- add code for amazon ip range detection
594 //- add code for microsoft ip range detection
595 //- add ??? article*/