{"tag":"php","articles":{"projects\/avris-booster-quick-start-of-new-projects":{"key":"projects\/avris-booster-quick-start-of-new-projects","type":"article","published":true,"meta":{"createdAt":"2020-04-13T11:56:14+02:00","publishedAt":"2020-04-13T11:56:14+02:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Booster","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-booster-quick-start-of-new-projects"},"content":{"en":{"slug":"avris-booster-quick-start-of-new-projects","title":"Avris Booster: Quick start of new projects","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/booster_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022329.18322295806\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADQUlEQVRIib2WS3IjVRBFT75PValUalltdxA0joABMzbQO2MXLIGFsA5mDBgYogMCW2pZn\/pmMijZVskf2jbqO3tV73Pz3nyZT84+\/GjscDYbM52MABCBi49z8lFKVTUoRlU1mAmvgQicpo7SR9LoWa5Kmlbv\/v\/08y\/2xPpX4f1XM9IkPjmnrGo+\/r24I6Sqt4QuLi6o6hrvPJvtFucEJ47z828Yj8fPJlTVLWkS+PW3j\/zw\/dd0qvz+xyXfvn\/Ln38t+O78lHo35wZhf4M8z4lJQvCe0ShDzXh39o4Yw73DPgcicLlYcTIZcblYY2ZkaaBuWoJ3\/HO1YjrJhmvM7Fah9WpF23VMp9MXETiEasenT0tEBBFH13WIgHMOU6OYTHoXnLtdMwg9zTKsalksN2AK9FydGKqKSUQwTBtEBCSCtYiAqhJjpKkbvPcYRpam1G3H7GT2gHwQgkdVh5\/3c6jrOjbbhs56Am+nY8x66W9wOO6\/WU\/wAOuypaxaVA3DwNjNM5pOSHzHmyIj2UuJgWVlWXK1LNmWNUn0JF5Isgwzw4lgXcVs9kC0j0BV2Yv3vkK+V2hg2R4fAJLgaWNABNq2YnttZGmkGCVk+RsO5z8FEcH7x+vWjbL7ew5yyHtPEhpUHeI8qp7MC1WjbKs+b\/JR8tmEXoJ7ls3nc5qmoSgK\/E5Skf4mqGpv305iMzBTnI90JjgUrLuLNgSKongWoYFlznmyLCeElqrzBBzgOTt5flG8wXMshgPLzBQ1o25aRnkkTZIHb88xEfYPNDNMW9qm5HpRIidTkpggcty82ce9OtS2HWZ9sUrT9MsrtD9YLq9Zr1eI8wgwnU7J8\/yoBA7jHViWJJG6yTBTzOB6tcKHgHPHU8mJGzTvwS0zoOtafAhkSSTEBCfutt\/ckD9sFcIrCAuPF0ZVpW1a6rphs17vlBImxRhxDhGH7DZQVUKIjEYZzvmXEzpAOExaw26T2oknphFxDuc8zjnG49ED27zm0SkDtQeWCeyqshBCwtnZ6X07\/u8H71OWhRDJ8xwRoSgmRzn\/vzDoZavVirZt6bq+f81mJ1+GxGOWmRlX8zkC5OMx680GVDHxOOeP1un3OfwL1QqaGbIjT+wAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/booster_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022329.18322295806\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EI got super annoyed having to set up all the dependencies for each project every time I started one,\nand especially implementing user management... Log in, register, confirm email, forgot password,\nMFA, change email, impersonate, manage avatars, over and over again, booooooring!\u003C\/p\u003E\n\u003Cp\u003ESo here it is: a template for quickstarting new projects,\nwith all of the above (and more!) included out of the box!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/booster_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022658.36644591611\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADQUlEQVRIib2WS3IjVRBFT75PValUalltdxA0joABMzbQO2MXLIGFsA5mDBgYogMCW2pZn\/pmMijZVskf2jbqO3tV73Pz3nyZT84+\/GjscDYbM52MABCBi49z8lFKVTUoRlU1mAmvgQicpo7SR9LoWa5Kmlbv\/v\/08y\/2xPpX4f1XM9IkPjmnrGo+\/r24I6Sqt4QuLi6o6hrvPJvtFucEJ47z828Yj8fPJlTVLWkS+PW3j\/zw\/dd0qvz+xyXfvn\/Ln38t+O78lHo35wZhf4M8z4lJQvCe0ShDzXh39o4Yw73DPgcicLlYcTIZcblYY2ZkaaBuWoJ3\/HO1YjrJhmvM7Fah9WpF23VMp9MXETiEasenT0tEBBFH13WIgHMOU6OYTHoXnLtdMwg9zTKsalksN2AK9FydGKqKSUQwTBtEBCSCtYiAqhJjpKkbvPcYRpam1G3H7GT2gHwQgkdVh5\/3c6jrOjbbhs56Am+nY8x66W9wOO6\/WU\/wAOuypaxaVA3DwNjNM5pOSHzHmyIj2UuJgWVlWXK1LNmWNUn0JF5Isgwzw4lgXcVs9kC0j0BV2Yv3vkK+V2hg2R4fAJLgaWNABNq2YnttZGmkGCVk+RsO5z8FEcH7x+vWjbL7ew5yyHtPEhpUHeI8qp7MC1WjbKs+b\/JR8tmEXoJ7ls3nc5qmoSgK\/E5Skf4mqGpv305iMzBTnI90JjgUrLuLNgSKongWoYFlznmyLCeElqrzBBzgOTt5flG8wXMshgPLzBQ1o25aRnkkTZIHb88xEfYPNDNMW9qm5HpRIidTkpggcty82ce9OtS2HWZ9sUrT9MsrtD9YLq9Zr1eI8wgwnU7J8\/yoBA7jHViWJJG6yTBTzOB6tcKHgHPHU8mJGzTvwS0zoOtafAhkSSTEBCfutt\/ckD9sFcIrCAuPF0ZVpW1a6rphs17vlBImxRhxDhGH7DZQVUKIjEYZzvmXEzpAOExaw26T2oknphFxDuc8zjnG49ED27zm0SkDtQeWCeyqshBCwtnZ6X07\/u8H71OWhRDJ8xwRoSgmRzn\/vzDoZavVirZt6bq+f81mJ1+GxGOWmRlX8zkC5OMx680GVDHxOOeP1un3OfwL1QqaGbIjT+wAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/booster_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022658.36644591611\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EI got super annoyed having to set up all the dependencies for each project every time I started one,\nand especially implementing user management... Log in, register, confirm email, forgot password,\nMFA, change email, impersonate, manage avatars, over and over again, booooooring!\u003C\/p\u003E\n\u003Cp\u003ESo here it is: a template for quickstarting new projects,\nwith all of the above (and more!) included out of the box!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["booster","quickstart","symfony","php","webpack","bootstrap","javascript","css","user management","template"],"hasMore":false,"image":"https:\/\/avris.it\/image\/booster_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/booster_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/booster_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022164.59161147903\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EI got super annoyed having to set up all the dependencies for each project every time I started one,\nand especially implementing user management... Log in, register, confirm email, forgot password,\nMFA, change email, impersonate, manage avatars, over and over again, booooooring!\u003C\/p\u003E\n\u003Cp\u003ESo here it is: a template for quickstarting new projects,\nwith all of the above (and more!) included out of the box!\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/booster_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/booster_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022164.59161147903\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EI got super annoyed having to set up all the dependencies for each project every time I started one,\nand especially implementing user management... Log in, register, confirm email, forgot password,\nMFA, change email, impersonate, manage avatars, over and over again, booooooring!\u003C\/p\u003E\n\u003Cp\u003ESo here it is: a template for quickstarting new projects,\nwith all of the above (and more!) included out of the box!\u003C\/p\u003E","words":64,"readTime":null,"lang":"en"}}},"projects\/avris-twemoji-backend-generated-twitter-emoji":{"key":"projects\/avris-twemoji-backend-generated-twitter-emoji","type":"article","published":true,"meta":{"createdAt":"2020-02-08T22:49:48+01:00","publishedAt":"2020-02-08T22:49:48+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/packagist.org\/packages\/avris\/twemoji","displayUrl":"avris\/twemoji"},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Twemoji","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-twemoji-backend-generated-twitter-emoji"},"content":{"en":{"slug":"avris-twemoji-backend-generated-twitter-emoji","title":"Avris Twemoji \u2013 Backend-generated Twitter Emoji","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/twemoji_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHlUlEQVRIiX2Xya8dRxXGf2eo6r73vfsmPz87ozMRCAEsAhEREUqIlEkooIAYAlIEC1iAWPAnsGfHAoFYwwqxCUECgUgiJRIEBCETEQlxJsfYsd9w33S7qw6LvnYQIEpd3S11DV99X506X8vV19wcUSpCELVCVMydWgpBoCK4GqZKcmc2O6TWChEQgQAigjAUEUFEgOBiiYh5rZeeREUEVARzx0RomhEeIUQEpRYkQIBSCsIwsKoOQEWhVpIq\/XwCRObtuNReBEBQlXcnl4vvQtSCqBHxbj9DSGpoLfjFDoISVCJAQxAdJiMEVQeEvlQMQRFCFFOdszEsBBm+za\/5zXA3ZrMOEYgYFiii6LyvqeJmJFU8RKGWOcEDI0QgIagaboqJYKK4KRKVWiu1lmEA92H1taJql6RSEaJWgmB5ZYXN8xeACiGIQ9SCqkCAmZLMSH4RkAAqUOdsCYjoXAZDUcyMbIlx00D07O3v07jTJCd7otZKKT19KSAM+6VWAC47tsHhzhSoA2kRqOY5KMV0kDib4qKJiA4JA6nopU0qqNlArSVUFBVDxVloFlgdLXNhuoWGEiVoPKGeIQohQt93A0MCJ66+mrdffQ2RQAJMBSGAhEjMJVOSKa4p0wfziLGLsQKqAztqmBrZnWwJxRBJNDlx7dFFdvemHHYdx45vMNvfY7q9g4mQXalRuf2eO3j5+RdZyg1BDJEFqA6iqIIIJFOyGx6akDSXC6EGmCiqhori5rga2TMuQjInSSJpw9raMp\/96udZW13hdz\/7Ja+feh1rFkk+RNNkeZHb77qD5x57kpV2BBQiYgCjggqYCaYDa+6Ckxukd2qNub4Aiqih5ogYbo5qws3mNXHn\/Z\/goW99kZeefoFHvv9TEoUbL7ucqJWd3Smeja989zs8\/fNHuXJpiVnpqNETUQbpdB5dadhDOSmm4NIuULuC1DqcQwGIIeKoJZI5qkYyp02ZsTtf++bnuPfBO5le2OHRHz5C1hGr45brb7ic3a1zbO0dcNe3v0xSoZ56g\/ddtsF0f4+DfkapPaV2oJXkhpngriQXUjJcxkvIrIdSCRj2CIpIQsUQT2R12pQYp8RDD9\/NvQ\/eCcAzTzxLomGyOGFx3LBzfp8TH\/0Q9zxwO942PPW9H7OxvoxGx9Z20PeJEgOgkIpqkLLjLqSkeBZcRhPIPRSQUAhFUBAHTZg6yZycEidPXsunv\/DJSylh+8yUI8eOcsWJDU7cfBVX3XIdC+tLRASv\/+pxFkaZUhdpm57cVJBKjY6+nxG1Qw1UA3MjN44nwZksQ1+IkAFMDNFlOK4JxZH5AfjwN+6\/dDID3Pf1u\/nPEhG8+fgfmZ09x9FrjnC4lel2N1nSDrGKWFBrR+0PEXrUBHMl5Yw3Da6TCakw5BkUDyVhZHFyGFmNjHLLyau54oq1\/wLw76WWwsu\/\/gM7L71Mu6C0y2OO3nQl\/dYC26cFlRlCB5KJkhCpQ4Qlx3LGmhE+WZlQCjBnyBA8nFaMFqXFaKpw28du+L9gpme3eP4XTzJ75yyjBlDo9vfYe6fDZcZ4uaF2ZTh7DFQSMpdL3bHcos0IP742ou+DWqCGoqEoRgohYbShNEW48br1\/wlkf3uPvz3xLG\/+6UWydIzaOqSOCtFBPTRolDxSIikmMZcpoaZYymhukNRAHuEf2GjYO+iYHRZmfaWUSqkVCUGiYkUwEdZXx5dA7E73OfXSm7z6l1d487l\/0ETH2AuRCk4layU0wAJmQ66UFCQP3AQzQVNCcgvJIGfIDXiLP7D6FrsHlem0Y3u3Mu2DnUPYm8FBB7NeKD386Ac\/YX93n81z2+ye3yaXQqKQqLRaGVnPQgqW2spsXGESpBBaG2yGlA71wLJhNeg6w5jQHRraJrYujOlokTtvXo+lNnFwMFglt0yIU3FCEgWnaqavEKKgzvHjl1H7ws729jzHGdmERmEhw7HVMesLsJK32Vg3FhaE105vMt3fp+SOlcuv4LHf\/J5XTx+y1FY2NiYcdsFTz+8iiytroSkjalhKeNMianhu8JSx1KBq5KbFPJE88\/FbP0LX9WTg2PoRullhbXmZ82fOsTYZIwf7NKWjbJ3jyKKyugBJe5L1rCz03HhyzObpKWdPbzMZgUvP0dvuZu+vv8Vv+syXaBaX6Cu0CxM8Nag6Yk7yhIghDHlHQpBSKUtjPODK9WX+eXaTYh0aLfvtKiwvMzma2Dpzjpi17CYlJ6NNlWqVg6bS2Qpr74XN6SukkdB4T39hj8X334sXjOnePlhitrODyC6ggxeKwYAPXgikVCiVc6cr9eCAZ2ohmyElkAisFl7AcCqtJSbJmbiz3CTGSXAtjDNMDzpu\/eASb1xQ6vmepRFMNt\/iPfd9GL9w6u\/Mug71TC0Vb0bw7j8EAnhKqCoSQdRAYwCgtSJ9f8ng1+4QF2PzzGu0ucVFWF5c4sjSCh6Vvb1tVpcm\/PlF5+1TI85sHfDqOweMctDojE\/1byDr198UtRT62YzaHZIWJkgIZXaApmZIB7XHcwsweG5Aa6AIEhc99ODFNQKhYiK4Ci6gVLIZqkHfdwiFjYmz3Brn9web2zjcdgL+Bf4aPDyLSPA9AAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/twemoji_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Ca href=\u0022https:\/\/github.com\/twitter\/twemoji\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Twemoji\u003C\/a\u003E is a great way to make emoji\u0027s on your website independent of system and browser.\nBut alas, it requires JavaScript...\u003C\/p\u003E\n\u003Cp\u003EUnless you just use this library to replace emojis with \u003Ccode\u003E\u0026lt;img\u0026gt;\u003C\/code\u003E tags in your backend.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/twemoji_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHlUlEQVRIiX2Xya8dRxXGf2eo6r73vfsmPz87ozMRCAEsAhEREUqIlEkooIAYAlIEC1iAWPAnsGfHAoFYwwqxCUECgUgiJRIEBCETEQlxJsfYsd9w33S7qw6LvnYQIEpd3S11DV99X506X8vV19wcUSpCELVCVMydWgpBoCK4GqZKcmc2O6TWChEQgQAigjAUEUFEgOBiiYh5rZeeREUEVARzx0RomhEeIUQEpRYkQIBSCsIwsKoOQEWhVpIq\/XwCRObtuNReBEBQlXcnl4vvQtSCqBHxbj9DSGpoLfjFDoISVCJAQxAdJiMEVQeEvlQMQRFCFFOdszEsBBm+za\/5zXA3ZrMOEYgYFiii6LyvqeJmJFU8RKGWOcEDI0QgIagaboqJYKK4KRKVWiu1lmEA92H1taJql6RSEaJWgmB5ZYXN8xeACiGIQ9SCqkCAmZLMSH4RkAAqUOdsCYjoXAZDUcyMbIlx00D07O3v07jTJCd7otZKKT19KSAM+6VWAC47tsHhzhSoA2kRqOY5KMV0kDib4qKJiA4JA6nopU0qqNlArSVUFBVDxVloFlgdLXNhuoWGEiVoPKGeIQohQt93A0MCJ66+mrdffQ2RQAJMBSGAhEjMJVOSKa4p0wfziLGLsQKqAztqmBrZnWwJxRBJNDlx7dFFdvemHHYdx45vMNvfY7q9g4mQXalRuf2eO3j5+RdZyg1BDJEFqA6iqIIIJFOyGx6akDSXC6EGmCiqhori5rga2TMuQjInSSJpw9raMp\/96udZW13hdz\/7Ja+feh1rFkk+RNNkeZHb77qD5x57kpV2BBQiYgCjggqYCaYDa+6Ckxukd2qNub4Aiqih5ogYbo5qws3mNXHn\/Z\/goW99kZeefoFHvv9TEoUbL7ucqJWd3Smeja989zs8\/fNHuXJpiVnpqNETUQbpdB5dadhDOSmm4NIuULuC1DqcQwGIIeKoJZI5qkYyp02ZsTtf++bnuPfBO5le2OHRHz5C1hGr45brb7ic3a1zbO0dcNe3v0xSoZ56g\/ddtsF0f4+DfkapPaV2oJXkhpngriQXUjJcxkvIrIdSCRj2CIpIQsUQT2R12pQYp8RDD9\/NvQ\/eCcAzTzxLomGyOGFx3LBzfp8TH\/0Q9zxwO942PPW9H7OxvoxGx9Z20PeJEgOgkIpqkLLjLqSkeBZcRhPIPRSQUAhFUBAHTZg6yZycEidPXsunv\/DJSylh+8yUI8eOcsWJDU7cfBVX3XIdC+tLRASv\/+pxFkaZUhdpm57cVJBKjY6+nxG1Qw1UA3MjN44nwZksQ1+IkAFMDNFlOK4JxZH5AfjwN+6\/dDID3Pf1u\/nPEhG8+fgfmZ09x9FrjnC4lel2N1nSDrGKWFBrR+0PEXrUBHMl5Yw3Da6TCakw5BkUDyVhZHFyGFmNjHLLyau54oq1\/wLw76WWwsu\/\/gM7L71Mu6C0y2OO3nQl\/dYC26cFlRlCB5KJkhCpQ4Qlx3LGmhE+WZlQCjBnyBA8nFaMFqXFaKpw28du+L9gpme3eP4XTzJ75yyjBlDo9vfYe6fDZcZ4uaF2ZTh7DFQSMpdL3bHcos0IP742ou+DWqCGoqEoRgohYbShNEW48br1\/wlkf3uPvz3xLG\/+6UWydIzaOqSOCtFBPTRolDxSIikmMZcpoaZYymhukNRAHuEf2GjYO+iYHRZmfaWUSqkVCUGiYkUwEdZXx5dA7E73OfXSm7z6l1d487l\/0ETH2AuRCk4layU0wAJmQ66UFCQP3AQzQVNCcgvJIGfIDXiLP7D6FrsHlem0Y3u3Mu2DnUPYm8FBB7NeKD386Ac\/YX93n81z2+ye3yaXQqKQqLRaGVnPQgqW2spsXGESpBBaG2yGlA71wLJhNeg6w5jQHRraJrYujOlokTtvXo+lNnFwMFglt0yIU3FCEgWnaqavEKKgzvHjl1H7ws729jzHGdmERmEhw7HVMesLsJK32Vg3FhaE105vMt3fp+SOlcuv4LHf\/J5XTx+y1FY2NiYcdsFTz+8iiytroSkjalhKeNMianhu8JSx1KBq5KbFPJE88\/FbP0LX9WTg2PoRullhbXmZ82fOsTYZIwf7NKWjbJ3jyKKyugBJe5L1rCz03HhyzObpKWdPbzMZgUvP0dvuZu+vv8Vv+syXaBaX6Cu0CxM8Nag6Yk7yhIghDHlHQpBSKUtjPODK9WX+eXaTYh0aLfvtKiwvMzma2Dpzjpi17CYlJ6NNlWqVg6bS2Qpr74XN6SukkdB4T39hj8X334sXjOnePlhitrODyC6ggxeKwYAPXgikVCiVc6cr9eCAZ2ohmyElkAisFl7AcCqtJSbJmbiz3CTGSXAtjDNMDzpu\/eASb1xQ6vmepRFMNt\/iPfd9GL9w6u\/Mug71TC0Vb0bw7j8EAnhKqCoSQdRAYwCgtSJ9f8ng1+4QF2PzzGu0ucVFWF5c4sjSCh6Vvb1tVpcm\/PlF5+1TI85sHfDqOweMctDojE\/1byDr198UtRT62YzaHZIWJkgIZXaApmZIB7XHcwsweG5Aa6AIEhc99ODFNQKhYiK4Ci6gVLIZqkHfdwiFjYmz3Brn9web2zjcdgL+Bf4aPDyLSPA9AAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/twemoji_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Ca href=\u0022https:\/\/github.com\/twitter\/twemoji\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Twemoji\u003C\/a\u003E is a great way to make emoji\u0027s on your website independent of system and browser.\nBut alas, it requires JavaScript...\u003C\/p\u003E\n\u003Cp\u003EUnless you just use this library to replace emojis with \u003Ccode\u003E\u0026lt;img\u0026gt;\u003C\/code\u003E tags in your backend.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["php","javascript","twitter","emoji","twemoji","backend","symfony","twig"],"hasMore":false,"image":"https:\/\/avris.it\/image\/twemoji_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/twemoji_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/twemoji_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Ca href=\u0022https:\/\/github.com\/twitter\/twemoji\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Twemoji\u003C\/a\u003E is a great way to make emoji\u0027s on your website independent of system and browser.\nBut alas, it requires JavaScript...\u003C\/p\u003E\n\u003Cp\u003EUnless you just use this library to replace emojis with \u003Ccode\u003E\u0026lt;img\u0026gt;\u003C\/code\u003E tags in your backend.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/twemoji_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/twemoji_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Ca href=\u0022https:\/\/github.com\/twitter\/twemoji\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Twemoji\u003C\/a\u003E is a great way to make emoji\u0027s on your website independent of system and browser.\nBut alas, it requires JavaScript...\u003C\/p\u003E\n\u003Cp\u003EUnless you just use this library to replace emojis with \u003Ccode\u003E\u0026lt;img\u0026gt;\u003C\/code\u003E tags in your backend.\u003C\/p\u003E","words":38,"readTime":null,"lang":"en"}}},"projects\/avris-fontawesomeoptimiser":{"key":"projects\/avris-fontawesomeoptimiser","type":"article","published":true,"meta":{"createdAt":"2020-01-09T23:04:10+01:00","publishedAt":"2020-01-09T23:04:10+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/packagist.org\/packages\/avris\/fontawesome-optimiser","displayUrl":"avris\/fontawesome-optimiser"},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/FontAwesomeOptimiser","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-fontawesomeoptimiser"},"content":{"en":{"slug":"avris-fontawesomeoptimiser","title":"Avris FontAwesomeOptimiser","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/fontawesomeoptimiser_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHvElEQVRIiX2X249lx1XGf+tStfc53eecnpnunhmPPZM4IRDZxEqUCEsWionkXARGuSiJAlKAB3gIykP+BN554wGBeIaniJdcpKBEJJESCQIi4DjOKA4ztmdsy\/b07fTpPnvvqsXDPjNGEKW0b9Ku2vXV99Va69ty\/R2PRZSKEEStEBVzp5ZCEKgIroapktzpujW1VoiACAQQEYSxiQgiAgT3W0RszvrgTlREQEUwd0yEppngEUJEUGpBAgQopSCMH1bVEago1EpSZdhMgMimHw\/6iwAIqvL25HL\/WYhaEDUi3h5nCEkNrQW\/P0BQgkoEaAii42SEoOqAMJSKIShCiGKqGzbGhSDju82xuRjuRtf1iEDEuEARRTdjTRU3I6niIQq1bAgeGSECCUHVcFNMBBPFTZGo1FqptYwfcB9XXyuq9kAqFSFqJQgWOzsc3jsAKoQgDlELqgIBZkoyI\/l9QAKoQN2wJSCiGxkMRTEzsiWmTQMxsDo7o3GnSU72RK2VUgaGUkAY90utAFy9vM\/6ZAnUkbQIVPMGlGI6SpxNcdFERI+EgVT0wSYV1Gyk1hIqioqh4mw1W1yYLDhYHqGhRAkaT6hniEKIMAz9yJDAjevXee3WS4gEEmAqCAEkRGIjmZJMcU2ZIdhEjN2PFVAd2VHD1MjuZEsohkiiyYl37m1zulqy7nsuX9mnO1uxPD7BRMiu1Kg89dEP8+LzLzDPDUGMkQWojqKogggkU7IbHpqQtJELoQaYKKqGiuLmuBrZMy5CMidJImnDxYsLPv3Hn+XihR3++avf5OXbL2PNNsnHaJottnnqIx\/mJ9\/9ATvtBChExAhGBRUwE0xH1twFJzfI4NQaG30BFFFDzREx3BzVhJttzsTTn\/htvvDnn+fmj37K1\/7qH0gU3nP1IaJWTk6XeDb+8C++wo\/+8Rs8PJ\/TlZ4aAxFllE430ZXGPZSTYgou7Ra1L0itYx4KQAwRRy2RzFE1kjltykzd+ZMvfYaPfepplgcnfONvvkbWCRemLe9690OcHr3J0eqcj3z5D0gq1Nuv8BtX91merTgfOkodKLUHrSQ3zAR3JbmQkuEynSPdAKUSMO4RFJGEiiGeyOq0KTFNiS988Rk+9qmnAfjP7z9HomG2PWN72nBy74wbH3wfH332Kbxt+OFf\/h37uws0eo6Og2FIlBgBhVRUg5QddyElxbPgMplBHqCAhEIogoI4aMLUSebklHjiiXfy+5\/7nQcl4fj1JZcu73Htxj43HnuERz7wKFu7cyKCl7\/1PbYmmVK3aZuB3FSQSo2eYeiI2qMGqoG5kRvHk+DMFjAUImQEE2N0GY5rQnFkkwC\/+GefeJCZAT7+p8\/wf1tEcOd7\/0b3xpvsveMS66NMf3rIXHvEKmJBrT11WCMMqAnmSsoZbxpcZzNSYawzKB5Kwsji5DCyGhnlA09c59q1i\/8PwP9utRRe\/Kd\/5eTmi7RbSruYsvfehxmOtjh+VVDpEHqQTJSESB0jLDmWM9ZM8NnOjFKADUOG4OG0YrQoLUZThSd\/692\/EszyjSOe\/\/oP6N56g0kDKPRnK1Zv9bh0TBcNtS9j7jFQSchGLnXHcos2E\/zKxQnDENQCNRQNRTFSCAmjDaUpwnse3f2lQM6OV\/zs+89x599fIEvPpK1j6agQPdS1QaPkiRJJMYmNTAk1xVJGc4OkBvIEf3y\/YXXe060L3VAppVJqRUKQqFgRTITdC9MHIE6XZ9y+eYdbP\/4Fd37y3zTRM\/VCpIJTyVoJDbCAbqyVkoLkgZtgJmhKSG4hGeQMuQFv8Wcv3OX0vLJc9hyfVpZDcLKGVQfnPXSDUAb427\/+e85Ozzh885jTe8fkUkgUEpVWKxMb2ErBvK100wqzIIXQ2mgzpPSoB5YNq0HfG8aMfm1omzg6mNLTIk8\/thvzNnF+Plql3Uv71FAOjleEJPCWviqeW87WHagjoriOWXuscUY2oVHYynB1d5tJOeHqhcKV\/cSF3Sk3f36HLoJOV8z2r\/Ddb\/8Lt15dM28r+\/sz1n3ww+dPke2di6EpI2pYSnzy2d\/j3r0DtGnZ2p6xf+kSP\/vFLd73+OPcvfMKbs6VvV3WXeVkeU6TWy4t5rSeSVJZbE1YvnbA9Ucu0h2+xbVrl2i1RxkwGdhuKw8\/Ehy+2XF6tMQOb+IysPfkM6z+6zvIh\/7oS9FszxkqtFszbly9wlBhb2fB6XrgoYs7rLseN6OUSpSKRXB0suLKYpvV8ozSD0xTgmGgW51zeb7F3nxKPVkyb53FvKWeHrI1nzC1jkd\/fY\/EAS\/99FUmwwGND0wuXybPF3jBWK7OwBLdyQnPLU8B5aYZEsJz7hsvBFIqlIrUSj0\/58e1kM2QEkgEVguJ0Rtv54YtN2buLJrENAmuhWmGN+7e5UO\/OefWS4fUKMwnMDu8y699\/P34we2f0\/U96plaKt5M4O1\/CATwlFBVJIKogcYIQGtFhuGBwa\/9Ghfj8PWXaHOLi7DYnnNpvoNHZbU65sJ8xn+84Lx2e8LrR+fceuucSQ4a7fjd4RVk913vjVoKQ9dR+zVpa4aEULpzNDVjOagDnluA0XMDWgNFkLjvoUcvrhEIFRPBVXABpZLNUA2GoUco7M+cRWvcOxttbuPw5A34HwP7VySOoz7KAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/fontawesomeoptimiser_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFontAwesome provides thousands of icons, but you probably only use a few dozen on your website. Instead of loading all of them as a webfont, you could use SVG sprites.\u003C\/p\u003E\n\u003Cp\u003EThis library is a simple helper that:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eregisters the icons you use in the place you use it,\u003C\/li\u003E\n\u003Cli\u003Edumps a refined set of SVG symbols at the end of your page.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EYou can check out \u003Ca href=\u0022\/blog\/reducing-website-size-by-a-half-by-optimising-bootstrap-and-fontawesome\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-computer-speaker\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E a blog post\u003C\/a\u003E about possible gains.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/fontawesomeoptimiser_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHvElEQVRIiX2X249lx1XGf+tStfc53eecnpnunhmPPZM4IRDZxEqUCEsWionkXARGuSiJAlKAB3gIykP+BN554wGBeIaniJdcpKBEJJESCQIi4DjOKA4ztmdsy\/b07fTpPnvvqsXDPjNGEKW0b9Ku2vXV99Va69ty\/R2PRZSKEEStEBVzp5ZCEKgIroapktzpujW1VoiACAQQEYSxiQgiAgT3W0RszvrgTlREQEUwd0yEppngEUJEUGpBAgQopSCMH1bVEago1EpSZdhMgMimHw\/6iwAIqvL25HL\/WYhaEDUi3h5nCEkNrQW\/P0BQgkoEaAii42SEoOqAMJSKIShCiGKqGzbGhSDju82xuRjuRtf1iEDEuEARRTdjTRU3I6niIQq1bAgeGSECCUHVcFNMBBPFTZGo1FqptYwfcB9XXyuq9kAqFSFqJQgWOzsc3jsAKoQgDlELqgIBZkoyI\/l9QAKoQN2wJSCiGxkMRTEzsiWmTQMxsDo7o3GnSU72RK2VUgaGUkAY90utAFy9vM\/6ZAnUkbQIVPMGlGI6SpxNcdFERI+EgVT0wSYV1Gyk1hIqioqh4mw1W1yYLDhYHqGhRAkaT6hniEKIMAz9yJDAjevXee3WS4gEEmAqCAEkRGIjmZJMcU2ZIdhEjN2PFVAd2VHD1MjuZEsohkiiyYl37m1zulqy7nsuX9mnO1uxPD7BRMiu1Kg89dEP8+LzLzDPDUGMkQWojqKogggkU7IbHpqQtJELoQaYKKqGiuLmuBrZMy5CMidJImnDxYsLPv3Hn+XihR3++avf5OXbL2PNNsnHaJottnnqIx\/mJ9\/9ATvtBChExAhGBRUwE0xH1twFJzfI4NQaG30BFFFDzREx3BzVhJttzsTTn\/htvvDnn+fmj37K1\/7qH0gU3nP1IaJWTk6XeDb+8C++wo\/+8Rs8PJ\/TlZ4aAxFllE430ZXGPZSTYgou7Ra1L0itYx4KQAwRRy2RzFE1kjltykzd+ZMvfYaPfepplgcnfONvvkbWCRemLe9690OcHr3J0eqcj3z5D0gq1Nuv8BtX91merTgfOkodKLUHrSQ3zAR3JbmQkuEynSPdAKUSMO4RFJGEiiGeyOq0KTFNiS988Rk+9qmnAfjP7z9HomG2PWN72nBy74wbH3wfH332Kbxt+OFf\/h37uws0eo6Og2FIlBgBhVRUg5QddyElxbPgMplBHqCAhEIogoI4aMLUSebklHjiiXfy+5\/7nQcl4fj1JZcu73Htxj43HnuERz7wKFu7cyKCl7\/1PbYmmVK3aZuB3FSQSo2eYeiI2qMGqoG5kRvHk+DMFjAUImQEE2N0GY5rQnFkkwC\/+GefeJCZAT7+p8\/wf1tEcOd7\/0b3xpvsveMS66NMf3rIXHvEKmJBrT11WCMMqAnmSsoZbxpcZzNSYawzKB5Kwsji5DCyGhnlA09c59q1i\/8PwP9utRRe\/Kd\/5eTmi7RbSruYsvfehxmOtjh+VVDpEHqQTJSESB0jLDmWM9ZM8NnOjFKADUOG4OG0YrQoLUZThSd\/692\/EszyjSOe\/\/oP6N56g0kDKPRnK1Zv9bh0TBcNtS9j7jFQSchGLnXHcos2E\/zKxQnDENQCNRQNRTFSCAmjDaUpwnse3f2lQM6OV\/zs+89x599fIEvPpK1j6agQPdS1QaPkiRJJMYmNTAk1xVJGc4OkBvIEf3y\/YXXe060L3VAppVJqRUKQqFgRTITdC9MHIE6XZ9y+eYdbP\/4Fd37y3zTRM\/VCpIJTyVoJDbCAbqyVkoLkgZtgJmhKSG4hGeQMuQFv8Wcv3OX0vLJc9hyfVpZDcLKGVQfnPXSDUAb427\/+e85Ozzh885jTe8fkUkgUEpVWKxMb2ErBvK100wqzIIXQ2mgzpPSoB5YNq0HfG8aMfm1omzg6mNLTIk8\/thvzNnF+Plql3Uv71FAOjleEJPCWviqeW87WHagjoriOWXuscUY2oVHYynB1d5tJOeHqhcKV\/cSF3Sk3f36HLoJOV8z2r\/Ddb\/8Lt15dM28r+\/sz1n3ww+dPke2di6EpI2pYSnzy2d\/j3r0DtGnZ2p6xf+kSP\/vFLd73+OPcvfMKbs6VvV3WXeVkeU6TWy4t5rSeSVJZbE1YvnbA9Ucu0h2+xbVrl2i1RxkwGdhuKw8\/Ehy+2XF6tMQOb+IysPfkM6z+6zvIh\/7oS9FszxkqtFszbly9wlBhb2fB6XrgoYs7rLseN6OUSpSKRXB0suLKYpvV8ozSD0xTgmGgW51zeb7F3nxKPVkyb53FvKWeHrI1nzC1jkd\/fY\/EAS\/99FUmwwGND0wuXybPF3jBWK7OwBLdyQnPLU8B5aYZEsJz7hsvBFIqlIrUSj0\/58e1kM2QEkgEVguJ0Rtv54YtN2buLJrENAmuhWmGN+7e5UO\/OefWS4fUKMwnMDu8y699\/P34we2f0\/U96plaKt5M4O1\/CATwlFBVJIKogcYIQGtFhuGBwa\/9Ghfj8PWXaHOLi7DYnnNpvoNHZbU65sJ8xn+84Lx2e8LrR+fceuucSQ4a7fjd4RVk913vjVoKQ9dR+zVpa4aEULpzNDVjOagDnluA0XMDWgNFkLjvoUcvrhEIFRPBVXABpZLNUA2GoUco7M+cRWvcOxttbuPw5A34HwP7VySOoz7KAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/fontawesomeoptimiser_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFontAwesome provides thousands of icons, but you probably only use a few dozen on your website. Instead of loading all of them as a webfont, you could use SVG sprites.\u003C\/p\u003E\n\u003Cp\u003EThis library is a simple helper that:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eregisters the icons you use in the place you use it,\u003C\/li\u003E\n\u003Cli\u003Edumps a refined set of SVG symbols at the end of your page.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EYou can check out \u003Ca href=\u0022\/blog\/reducing-website-size-by-a-half-by-optimising-bootstrap-and-fontawesome\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-computer-speaker\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E a blog post\u003C\/a\u003E about possible gains.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["fontawesome","optimise","font","webfont","icons","symfony","twig","php","js"],"hasMore":false,"image":"https:\/\/avris.it\/image\/fontawesomeoptimiser_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/fontawesomeoptimiser_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/fontawesomeoptimiser_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFontAwesome provides thousands of icons, but you probably only use a few dozen on your website. Instead of loading all of them as a webfont, you could use SVG sprites.\u003C\/p\u003E\n\u003Cp\u003EThis library is a simple helper that:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eregisters the icons you use in the place you use it,\u003C\/li\u003E\n\u003Cli\u003Edumps a refined set of SVG symbols at the end of your page.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EYou can check out \u003Ca href=\u0022\/blog\/reducing-website-size-by-a-half-by-optimising-bootstrap-and-fontawesome.lite\u0022\u003E a blog post\u003C\/a\u003E about possible gains.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/fontawesomeoptimiser_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/fontawesomeoptimiser_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFontAwesome provides thousands of icons, but you probably only use a few dozen on your website. Instead of loading all of them as a webfont, you could use SVG sprites.\u003C\/p\u003E\n\u003Cp\u003EThis library is a simple helper that:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eregisters the icons you use in the place you use it,\u003C\/li\u003E\n\u003Cli\u003Edumps a refined set of SVG symbols at the end of your page.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EYou can check out \u003Ca href=\u0022\/blog\/reducing-website-size-by-a-half-by-optimising-bootstrap-and-fontawesome.lite\u0022\u003E a blog post\u003C\/a\u003E about possible gains.\u003C\/p\u003E","words":71,"readTime":null,"lang":"en"}}},"blog\/technology\/new-year-new-digital-me":{"key":"blog\/technology\/new-year-new-digital-me","type":"article","published":true,"meta":{"createdAt":"2019-12-31T11:22:05+01:00","publishedAt":"2019-12-31T11:22:05+01:00","group":"redesign","category":"blog","subcategory":"technology","slug":"new-year-new-digital-me"},"content":{"en":{"slug":"new-year-new-digital-me","title":"New year \u2013 new (digital) me","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4_small.png\u0022 alt=\u0022Screenshot of the new version of the blog\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022234.07874015748\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADFklEQVRIiaWWz24cRRDGf9XTf2aXJSvHYCLlgEDiyCkQiRtXrhw4IfEaSBx4AA55hQiJIxIvwM0kAiQgcoQcEsmAZGJsrx2CPd6dnunisJ71zu7au0BJo\/7UXfVNTdVXvSu33nlXRUAVAGZxlhlEDAAxVgt9GoyMl+AcYgyqabIpIqgqMUZSE6Dnx1PxVuSCuLEG3377FiLCzs6vpKQcDgZzPrPYWkvIA945rHWE4HHO4pwnBE+WZTjrCN7hg8d7T\/DjNc9z5ONPPlXnHN57fLg4DC0cJoEhzOxN\/ALeO7IsQ0QufZpqTa\/TJiklndtdwfb3D8hDhjCiSjlVVeODZzQckeeBfr+Pnremadcq+F8lVMbI3tM9nHPEKqIITs4oa09KiTzkxBhRFJtZjDFsbLyMtXblDxVV\/U8VAkgpTVqhqgtbsKqpKqqKbYiavFSVlNLEYfppzqfXacL\/YyKCMQZbFEWLzBjTEqExZhKwTJRlWTIYHOH9eOyvr61N\/FbW0LKWNdW5qhvNPVXXNSenp+O4pIgRyjJyWnte7HVZfyFbWilzVSIXuAaUqiq589kdPr\/7BVUsAaWuUyvGiCCMK21EcJml37VYja12X4bNIq1cppPtBz+wu3WfJ99+zeDwoJXItBZbHChnwxLnw9LqANhlk9HSVyrpWUGrClMWk\/1pjmKkdLyQZYaTYU0sayrJKIaRrvdz\/rP40pYtsnRSIJpwGjk5Opw7FxHWruXk+fgWv36tg+t26PdyrHOtj2xL4qIjdrYt08pv7NHWPeKzP\/hrlBgmJZ4VxKOHbG0e8Mprb7Jx8\/UpclBN5z+4Sj8Y6lgRCQvvqtl3zV2hi4bu9+\/uUR7\/yYPdZ\/S6yvrNde5+uUmv2+HDj16C84RUlbIcTSpgTEZVFtS+T27m+RfhlTS0F2u+2nzM91s73Fjr8NYbN9jefc6j3QHvvf8Br55zjBMBRDAy\/l9hvcebEomKBH\/lu4DxlF2QzWOAnx4\/5f6P2wg1x0XJN7\/sc3xW0nHCz7\/tt6cMRWguUeHvYaIoCorh85XG\/h9McAJLnmVpFQAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris4_small.png\u0022 alt=\u0022Screenshot of the new version of the blog\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022234.07874015748\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EIt wasn\u2019t really supposed for the New Year, but I\u2019ve had plenty of free time on my hands\nduring the holiday break, so here it is already: a brand new version of my blog \ud83e\udd73\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4_big.png\u0022 alt=\u0022Screenshot of the new version of the blog\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022468.15748031496\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADFklEQVRIiaWWz24cRRDGf9XTf2aXJSvHYCLlgEDiyCkQiRtXrhw4IfEaSBx4AA55hQiJIxIvwM0kAiQgcoQcEsmAZGJsrx2CPd6dnunisJ71zu7au0BJo\/7UXfVNTdVXvSu33nlXRUAVAGZxlhlEDAAxVgt9GoyMl+AcYgyqabIpIqgqMUZSE6Dnx1PxVuSCuLEG3377FiLCzs6vpKQcDgZzPrPYWkvIA945rHWE4HHO4pwnBE+WZTjrCN7hg8d7T\/DjNc9z5ONPPlXnHN57fLg4DC0cJoEhzOxN\/ALeO7IsQ0QufZpqTa\/TJiklndtdwfb3D8hDhjCiSjlVVeODZzQckeeBfr+Pnremadcq+F8lVMbI3tM9nHPEKqIITs4oa09KiTzkxBhRFJtZjDFsbLyMtXblDxVV\/U8VAkgpTVqhqgtbsKqpKqqKbYiavFSVlNLEYfppzqfXacL\/YyKCMQZbFEWLzBjTEqExZhKwTJRlWTIYHOH9eOyvr61N\/FbW0LKWNdW5qhvNPVXXNSenp+O4pIgRyjJyWnte7HVZfyFbWilzVSIXuAaUqiq589kdPr\/7BVUsAaWuUyvGiCCMK21EcJml37VYja12X4bNIq1cppPtBz+wu3WfJ99+zeDwoJXItBZbHChnwxLnw9LqANhlk9HSVyrpWUGrClMWk\/1pjmKkdLyQZYaTYU0sayrJKIaRrvdz\/rP40pYtsnRSIJpwGjk5Opw7FxHWruXk+fgWv36tg+t26PdyrHOtj2xL4qIjdrYt08pv7NHWPeKzP\/hrlBgmJZ4VxKOHbG0e8Mprb7Jx8\/UpclBN5z+4Sj8Y6lgRCQvvqtl3zV2hi4bu9+\/uUR7\/yYPdZ\/S6yvrNde5+uUmv2+HDj16C84RUlbIcTSpgTEZVFtS+T27m+RfhlTS0F2u+2nzM91s73Fjr8NYbN9jefc6j3QHvvf8Br55zjBMBRDAy\/l9hvcebEomKBH\/lu4DxlF2QzWOAnx4\/5f6P2wg1x0XJN7\/sc3xW0nHCz7\/tt6cMRWguUeHvYaIoCorh85XG\/h9McAJLnmVpFQAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris4_big.png\u0022 alt=\u0022Screenshot of the new version of the blog\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022468.15748031496\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EIt wasn\u2019t really supposed for the New Year, but I\u2019ve had plenty of free time on my hands\nduring the holiday break, so here it is already: a brand new version of my blog \ud83e\udd73\u003C\/p\u003E\n\u003Ch3\u003EWhy?\u003C\/h3\u003E\n\u003Cp\u003EWhere to start? I got increasingly annoyed by the old version.\nThe design started seeming boring and blunt.\nThe code started growing unstable.\nIt was using the \u003Ca href=\u0022\/projects\/micrus-v4\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus framework\u003C\/a\u003E,\nwhich I \u003Ca href=\u0022\/blog\/how-and-why-i-ve-built-and-killed-a-php-framework\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-computer-speaker\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E stopped supporting\u003C\/a\u003E a year ago.\nIt was using \u003Ca href=\u0022https:\/\/gitlab.com\/Avris\/Micrus-Assetic\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus Assetic\u003C\/a\u003E\n\u2013 an outdated method of asset management,\nwhich highly depended on the server configuration and binaries installed.\nThe deployments were \u201ciffy\u201d, to say the least.\nI was afraid of sending even small changes to the server, for fear something might blow up again.\nSome small things I didn\u2019t even notice were wrong: like still warning about Google Analytics tracking,\neven though I migrated to \u003Ca href=\u0022https:\/\/matomo.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Matomo\u003C\/a\u003E ages ago.\u003C\/p\u003E\n\u003Ch3\u003EWhat changed?\u003C\/h3\u003E\n\u003Cp\u003EI decided to keep it simple. To go with a \u201cless is more\u201d approach.\nI\u2019ve removed soooo many features that just weren\u2019t useful enough.\u003C\/p\u003E\n\u003Cp\u003EI used to have an admin panel: except I didn\u2019t need any fancy editor,\nthe main content was written in an (enhanced) \u003Ca href=\u0022https:\/\/www.markdownguide.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Markdown\u003C\/a\u003E anyway.\nSo for this version, I\u2019ve decided for a filesystem-based approach: I\u2019ve ditched the database completely,\nI don\u2019t need any ORM, I don\u2019t need an admin panel. I just have a bunch of \u003Ca href=\u0022https:\/\/gitlab.com\/Avris\/SUML\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E SUML\u003C\/a\u003E files\ntracked by GIT. Using \u003Ca href=\u0022https:\/\/gitlab.com\/Avris\/Esse\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Esse CMS\u003C\/a\u003E.\u003C\/p\u003E\n\u003Cp\u003EI used to have three language versions of the UI \u2013 but why?\nI don\u2019t think anyone really cared about not seeing the names of the categories in Polish,\nwhen most of recent my posts were in English anyway.\nSo now the posts are still available in the language(s) they were written in,\nbut I\u2019m no longer maintaining the multiple versions of the UI, or the features to filter content by language.\nWhat a relief!\u003C\/p\u003E\n\u003Cp\u003EI used to have a \u201crandom post\u201d box. I used to have tag clouds. I used to have an embedded Twitter timeline.\nI used to have the latest posts on the \u201cabout me\u201d page (really?). I used to have a link shortening feature\n(\u201davris.it\/l\/\u0026lt;whatever\u0026gt;\u201d) that I\u2019ve never used, not a single time.\nI used to have a separate controller and database fields to support redirects for legacy URLs from the even earlier version.\nI used to have database fixtures. I used to have a console command to export legacy comments to \u003Ca href=\u0022https:\/\/disqus.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Disqus\u003C\/a\u003E.\nI used to have tooltips and popovers.\nAll gone now! \ud83d\ude0d\u003C\/p\u003E\n\u003Cp\u003EI replaced the infinite scroll with... just loading the whole page at once \ud83e\udd37\u200d\nIt\u2019s not that big for today\u2019s standards anyway \u2013 the heaviest list of posts is 113 KB of compressed HTML.\nInstead, I now lazy load images using the \u003Ccode\u003EIntersectionObserver\u003C\/code\u003E API.\nWay simpler than infinite scroll or pagination \u2013 which aren\u2019t really necessary with this amount of data.\u003C\/p\u003E\n\u003Cp\u003EOh, and the website is now using a fast an sleek \u003Ca href=\u0022https:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Symfony\u003C\/a\u003E setup with an HTTP cache \ud83e\udd70\u003C\/p\u003E\n\u003Cp\u003EDesign-wise, I use the default \u003Ca href=\u0022https:\/\/getbootstrap.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Bootstrap\u003C\/a\u003E theme,\nwith only minor changes of variables and minimal additional styling.\nThe main feature, obviously, are the tilted elements. That\u2019s enough to add some personality to the website,\nwithout making it overcomplicated.\nCompared to the previous version, it\u2019s way more contrasting, better tailored for wider screens and clearer.\u003C\/p\u003E\n\u003Cp\u003EOverall, 41090 (!) lines of code were removed:\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-3-4-diff_big.png\u0022 alt=\u0022424 files changed, 6711 insertions (+), 47801 deletions (-)\u0022 class=\u0022border\u0022 width=\u0022550\u0022 height=\u002289\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAFCAYAAAAt8E4LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABbUlEQVQokZ2RTY4TQQyFP7uqKwlh0skIISSExBHYchPEATgVaxaciQVigRAw+emku6q6K2UWCYPYDBJePVnPz7I\/ef3slT1\/+YLcnzBtsDJSypmbdcv3r9+YzWesNi2xjwz9QN91pJgB4+GS\/\/L438I5hzaeVEZEhGpGe7tBqRy2B1SVWg1V+RNm18wHF\/6r\/vZ4EGqZMDPCLHDqjghwPBxRjNX6BhMh9pH93Y4nT28J8wXd\/siqXWIohsH5jGsaSimMKSMinKvRrleE4Nn+3LF4vKSWQhMCOV88ghHjyHm6kPFv3r2l3bTUanx8\/4G+O+GcEofEYrkg55FHyzkpjXin5JQQ58EqoopvLkc0zuGcAp7iJkIIDENizBnMQARBqNWotQKgqqgqItP9t\/yXT58RFXyYcTx0dPsDZoYIpBgB4w6wK6JpGu\/Zb3\/sQEAwEtB1ghiYGLGPmBlTzleyRhoGDLlOX5TZtSOAGL8AA7fCc1N6DsoAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-3-4-diff_big.png\u0022 alt=\u0022424 files changed, 6711 insertions (+), 47801 deletions (-)\u0022 class=\u0022border\u0022 width=\u0022550\u0022 height=\u002289\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThere\u2019s nothing else in the (programming) world I love more than removing useless code!\u003C\/p\u003E\n\u003Cp\u003EDeployments are now stable, simple, reproducible and revertible, thanks to\n\u003Ca href=\u0022https:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Symfony\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/webpack.js.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Webpack\u003C\/a\u003E,\nlack of database (no db = no migrations),\nand most importantly my recent child: \u003Ca href=\u0022\/projects\/avris-deployer\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Avris Deployer\u003C\/a\u003E.\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-deployer_big.png\u0022 alt=\u0022Console output of deployment\u0022 class=\u0022border\u0022 width=\u0022848\u0022 height=\u0022237\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAKCAYAAADcpvzeAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB1klEQVQ4ja2TMXPUMBCFvyfJdw6JyUDLpKOloOf\/\/w9mAqkokkDu7LP0KNZOLpmbpAAVlvbN0+767ZO+fflsgG4QhzujBK6F\/a8Luov3uE3U8UDfiYvznjbPgLi5\/olbA8T\/XPp6dWUJ1Jt6l0l9Q9tGe0jQhDZG2bgRcTFuggo2tD8JED5Am9JTYsBvFT\/BKZ4S6hv1NpPeNTwJbYSryGeGYtpDQimutjFFliVOvVFp1F2iG2bwUsSE2g2wAjRoYzyDSsSe9Yh5FsUN2iQ8iZaFxwSKZPU+BbnC\/Lvgw9OviVAIgouhGqQjfJmmX8FfrpL6aF1DDeKmQjbeJdSZep\/wlB6llXwk9nFncY7iEdtGKwcj6VE9a4kXjgXCJM+Qtku5tEiYQ1oyKJs8VMpQF\/uuJhZaax19n8UL4SUnsNOc4lm0fYIGrjG+NmaUQJ1Rt8y+b6RZeI7xvmpY\/4Op1S1QMVSFWsl4TNTbDDlwxnXwoG2LZNNrT1680fZJTgFI2xaj6mO3IQ+VdN5QZw43Ja6XMPj6ek6Z8mm91cxpTvnYf+KwHxk+XLJ\/2LEfd5wN53Ql8+P7dTxZwDZb9UzjiI9NvZh15YS3FlNj5GU\/4qzmjtvPOX8BswgOy6gNMP4AAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris4-deployer_big.png\u0022 alt=\u0022Console output of deployment\u0022 class=\u0022border\u0022 width=\u0022848\u0022 height=\u0022237\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EVersion 1 (\u201dSilva Idearum\u201d)\u003C\/h3\u003E\n\u003Cp\u003ESo, to finish this post, let me just quickly show you the four versions I\u2019ve been through so far:\u003C\/p\u003E\n\u003Cp\u003EMy first blog was called \u201cSilva Idearum\u201d (Latin for \u201cForest of ideas\u201d).\nIt was anonymous, spirituality-oriented, Polish-speaking, Joomla-running and with a \u201ctoo much\u201d design.\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris1-post_big.png\u0022 alt=\u0022Screenshot of the post page in version 1\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGJUlEQVRIiS2UO49kZxGGn6rvdk736Z6+THt21pfF69sagYUsIECIhAjxL5BICIiQSEj4AyT8AmIiiEgXgQRItgAhC3vB9trMzM7Ozuz09O2cPt+FYLaSkkqvKqnnKfntL94uH58rg8mCz06\/yvok4k1h0AsLvmButxzdW3B4UOMOLMtux\/V6SW+Fq5j4bLOiDApfvz9jVNWYYnHF4kV4\/p8nKHtSdNjgmR7NSD5wcn1FbwzWWUosbG5a+rMnzCeLP9hHV4bZ6xOwR4yvN4zqPSmCN5F5UN585x7TqcebPV3j0Ww4S2fssDx8fMqTYpn3M\/78xy\/55Y++zzDMCOWIkjbE6hFSOtqLFcE4SjXAjGbMAxwMeh4cj3i+CqyWW\/J0zTx32LsP7jGZLoi9MkxbWhcZhinLizWz114nv3zElQ0Ym3FW8P2a98YLOHiF33zwa0LcIvWQeVjx17895N6rb+HNOSVH7h2\/RUlbar\/ExEgYvcR+d4M4gx8M6UOg7QY0s5cIuwva6y+xd195wG67IZcObYYIzwiaGNrAQTTsB4fYwYBQO3wVsKagqtgq8Kuf\/pzPv\/gUlxJvvPMyk4MRRg2npyfEPtGMDlAZIeMZOe4RqdCqQWPDrn3OZX+HVhJ+GJDhAp9W2O0msu96sjqaxUuM2sBhGPOstITNjnmj9NM5zXiM9w5VJZ99gvQbHsxG3K\/fwFWBv4vnal94LRjuD6fknHHZ0G5ajBRkdki2DTrosWkKN4Hd3pCMZ31zQx4echAcNkWDCyPscI7aEfZORTWsubM\/wZoJ86MxebHAhwYVQUTRyTcpWGK+hbeQ+bYqlELJGX0FcopYa2jUUdc1z59+QteuwVZIqqgpdJstlydnbDEU3bHwE6xBMDqAEnDWoIcv0w7HVPWI5maJEbDe4JxirENEEPEg4LktEUMpCTUBck+KHdlEVAXRQru5RNQhpsI5g0k91lasnn2MNY63H7zLo0cf8rTbYW0xEA1pF0kFqmmgV0+ZvYUZr6jiBicGNSBqoPTk0rO6WSMCIpBTRo2iLiBaiCnSOAMUIKNGKDlS8h41FSKC8TXNZErpr5Cy53ix4Pz0c+yiarjedCA99aTC1iNULNYUejth78Z4tXRtAjaIAESC91g\/wBpFbocgt2eDctsFKIJaj+hznHOU0kFZEsKUWf1f\/MKSVp8xn46ZTO9jTTVi3CvPrzqa+yPw1YvlmVDVZCJ9KpScUDUgDqGQU6HsVkQKooailpvLc549PWc2n7DbtjRNw9PHn7Ludrz3rW9DycS4J8anVBKZ3xlgdsLy4gLvGtquxYbaENOIsAewWBswKmQMJHDekzO07R4o5FzwzoMYJAREQVXo+45HH\/6Jf3\/wF8Jkwj8+PueH3\/saD3\/\/O4Z37\/L+d74LuaeoY992RHuCHnjCZol51uCzwWmDbUum626DOfZovyIJmDCh9IloE5VTmqa55SVnhAxiKCUjovT7Fms9fbfhfAOr88d88NEZunnCbHbM9OgQeWEjoohVri8umZgRuhuS1h2pGPZhjd13heRHCBGjYIwi6lABDYILFWgg5oxKIaf4gpmCtZa+7yg5UxB20TCcHfPuN95nefOQeLHk83+dQAkIGTUWRGhGr7KJheVKafwRaXzG5fUl5WyLPaoMZ9s9W23Q0QIzGCEFVBU1FuuHWDVQCsYqwgChEGNGVagHAyADhni9ptlfc\/lFZHB6SXA13DvAHhySzz8ipRYd3cG6Q+qmZfnsKdglySZyUxGGAVsVy2vn\/+Offo6++RWcH76wJWKMR42j37fc3KyBggDeO3JOGDWkXFAVVA0\/+PFPbuE3BvOzFhtb8voU2W+R5RkKZPGUcYPzd3B+w\/X1GjdsyO0TZDDCeh\/guuP+fIujoMYiqlDAqGKN4OsKZz0FEFEgUXKhlIJznhg7VC0xRiRHpF0S+x0xC+wVkRHN4BK1hl1vsH5IFRyD9oqzx2ckZ9l2icPjI6yaAcbXHBvYdlsiHU4NKSdEFZFISYbdboMYf2tgTrcvBqHv15RcEN0jYjBqiAxRGyj9mqI1g7omhxa9OWUwuUvrK0K6IXrP6NW7ZFdDUmLX8X\/09s8geltpKwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris1-post_big.png\u0022 alt=\u0022Screenshot of the post page in version 1\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EVersion 2\u003C\/h3\u003E\n\u003Cp\u003EAfter coming out and becoming independent from my parents,\nI was finally able to put my name on my posts. New domain and a redesign was also in place.\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris2-home_big.png\u0022 alt=\u0022Screenshot of the homepage in version 2\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEQklEQVRIiZWWO2xcRRSGv3nd1+56d+21DY6xnYctIkAIRxGCCAGK6Hg1FFSICgloEAiJBikUFCkCDQUNLVJ6RKjpKJEQUhwLYYKVxHYSdm3f3bk7MxR3d+MkDlmmOpq5959zzv+f\/16xuroaqrUaIgQ6u7t8deEClWoVYyKiyJDnOQDGGJxzHD9xAiUlhy2lFMaYQ8\/GXJe0tZZOu02kBNZaGs0G9XoD7z2tVuuuC7z3dO0ecZSxc\/MGWhuKwqKkwjnPRK2OEBKtNZubm6ysrPzvjHS73WZxaoKF6QY\/\/7ZNllVozUwjEQghCCEghEBKydbNv\/nsm9d485W3WL\/6O9f+uk3e73D5yp\/YQvP5+19zcvE08\/PzLC8vjy4ZYowT6zPPnGR+QhFHGp1UiSODUZper0cABOC8J00SBIpqd4rrv3Y4Ov0Up559nnZ7h4n+T1z9Y41+J2CMptNu07UWrTVKStI0JYqi8To0u3CEONVIZWjEt9jZ2WF3bx9rLVmWkec5cZKQ7+8T6YhHry2RTR\/n1vU6353\/GELOS2fPcpNjFD1D+5823W6POI7Z292lUqkQxzEhhLs69qBYr6+vcWR+njipUTiNMYYkTZmamgKgXq+X1DUatG\/cYLMvqedbSK84c\/oNxETO3NIM3\/\/wLWdefhFtDEIKiqJPCIGiKJBSjk\/ZnkpY21gnMRUiAvn+HkJqCtvD2hKsKCxRFCMkzC1M0r29TdZMmHriSfTeL0w3Fec+eYflpx9Ha4W1Bc45jDFYa+n3++NTNp0ErDAECppxgzhOmJxsAiClJISADx6tNFprPv3iy3sgXh1FSik2NzeZm5tDaw1QVq31+JTVqkdRyR6ul2OdwBaO7e1tpJR474miaORBSkoQAu\/94DwAgRACUsqS3riGcw7vPVBaRbVaRQ6866GULZ1YZmvrOtb2SLUmTROiOB6MPGitEEKglCJ4TxCQRAnGGJSShHCnyiiKyFyOlALv79iFc26U0MOWWF1dDQc3Ll68yPTsDLE2MPCh0cNAGFA5rGy4vPcIIdjY2KBSqZSUC0Gj2RxbP8AlfS+wlJJKmrF2+TJiVFVASjWYGAGIUdXDylutFrVaDSkyarXagEZBYYvSj5QaKyP9oAMfAniP0XqkhyzLRiItEymtM4RAFMUANCdTNtavQAg0WjMkSUKlWhnhPlRDBykBcM7R7XZZWlq6L8mDAIcBOudoNpuc\/+hDlo\/MsfTcCxxZPE5zanKs7hzaoU6ngxCgtSHLspE2hokPrWC4N5wwhCBNEkIITCweI9QibrfbRDvbrPDfo34wvk9Dttej14vpdnvs75efkNJHPEKUehGD0VdK4ZwbdWl29hGq1Sqvv\/sB17Z2qJGzsPDY6J3DunofZYdRAZCm6ej\/RimFNqYc+wDeO6A0QiEE\/X5\/oKsS49yP62w7jcs7vH2qynszM2NT9i\/Q3AYk6KDr5QAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris2-home_big.png\u0022 alt=\u0022Screenshot of the homepage in version 2\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EVersion 3\u003C\/h3\u003E\n\u003Cp\u003EAs announced in \u003Ca href=\u0022\/blog\/brand-new-blog\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-computer-speaker\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Brand new blog\u003C\/a\u003E\u2026\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris3-home_big.png\u0022 alt=\u0022Screenshot of the homepage in version 3\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u00221603.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAkCAYAAABmMXGeAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFE0lEQVRIia2Wy48cVxXGf+feenRVd0+P7Ywn47ESk4kTiEE8LEACsUECCQHZIRZs2CL+EZD4A9iyjmADUjYIgSJhQmyEIjBCKLKxsScT2zPdPT1Vdd8seqY1j3Y0SHybUl3V+e79zvnOqSt\/uPXnJEqBCKIUSmlaY8mLnKKsECVY4xCBhAAgMWBcoq5ylAgheJJ3xOAhRmT46udTMbpIUqCUJqsHqKIk74\/Iqj55URG9IwGZaBKgQ4QErmuwzmAOptj9Xdxsim8aMm8thfegFSEkwkGDdgGScOOlDWwUHh8U6GpEqTLq1FBoxdNH92mmewRriaYhNDOSDxAiKkWhmU7o9me4zqFUge6tcOO11\/jx975DayM2CNZYunaGFsHZjpgE0zZY0xF8IgaIzhFDQIorbySV5UiezyX3R+i8ABKfePllDlxkajwAKYR57qwlmJZoGnzXEJ0jeEM0Hcl2yDu33jsslELpjHklZF44USAgIoBASiQSpAQxklIixTB\/T4mUIqSEWGsTh0gpISKMx2OUUvT7fZRSADRNQwiBuq7RWh+FICKLuCOoI7LjKMsS7z2TyYQQAj\/76Y\/Ii4KpLdFZASLcfXCw2PA40vNPus9k\/ARFxmBlSDObsHH1JRKCEogxYnyilyuUUidO+lzSGOMJOUeYNJGVfk6KgZ2xY+NCMW+K88h3zuG9X3woInRdR6kszrQIifWRnhdmifzsKOg4fvm7P\/H+Bw8ZDGpsSGTlgO9++VO88sIQ0Zper7cgWIZs2eJf\/vEBv7\/9PvVwxJNpy3C4ws2tdTYv5uiQY42hqmtCCGeIRWS5\/GgNdZbTI3F11MPOxiTnSQeWzphDD5\/N+Qn5p+GN4fHjbRBFVVfM2pboLVlviODpug5IZFm+lHxpTp0x+K7BeU8vS2hvicFTDWu0koU\/T1f9Y3MavUNFj+86ullGWRSYtiN4RxRhMBgsSJdhKen33\/wmNz\/zOvPGn6994cbrdI1hY\/NFnm0\/ZOWFFxfGP42l5j+O4w3RHjSMLqwy29uhHFxckJ6OE2ttOlo8\/TxOehR8GsvadGmhmqbBWkuWZdR1jek66n6fe9v77E92WS0dXSzw2So3ro3Ol1M1GRP2JlRbW4QYefTMMNxP1FnFxitXmZnEWi+nypdFH5KezoleW6d34TIojVbwk7dm8M+\/8uFkk7d+8Vl8SEwPLMUoJzuvT4N3eGPIej3QBV+\/HpleGvF0\/AwRUAIJEJZ31VL5P\/\/NA+78a4x1kW\/cXOPbXxpQ1jfp5TmZVlwc5ljnIUUOB90CIrJcfq+5y9qzv3Otang1fZHB8FsE59FKePu3j7i7k4j+31zpwQ9\/8LUTpM+tfnvvNtu33+ajAtavXUZI9PsVdVVy7+EOt+48ZnfvXb56\/QpwkhSeY\/73bt\/h3v0HiGje+OQW6+uXsS4xGFQ4B+N9S1koRoOS4bBe\/js5bfq2bTFdh840\/f6A8XiKKCjzHKUVs\/0ZohSrq6torc9nfqXmIy8laI3n1+8+oSo1Lio2LxX856MZCWFrM\/KVT6+dT\/4f\/\/aE7d0O7yNbV\/p87volYoKqzGhaQ0QhApmCTKv\/vffj4U1EKYWIYIwhBI9SOb1eceIwHyv\/xK4iJ0bcfuvZHTdUVY+BS1wYlmdiz14xODt8rYv86p1HWBfZmwWezuD+TsuHu92y8JM5\/X9B0hynLNUQk8ZH0EoQSaR09AREKDMhy7IztQD4L+DGHxjr8QA8AAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris3-home_big.png\u0022 alt=\u0022Screenshot of the homepage in version 3\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u00221603.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris3-posts_big.png\u0022 alt=\u0022Screenshot of the list of posts in version 3\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADMElEQVRIib2WzW4cRRSFv1vV1T09f5AZx5EiJAiKWCDxIzZIkYxX7PCCBQs\/BO\/AM\/gV2PEEXrJha4UNwgpKVg6yzHg89sz0X1VdFh0bO1EyvaI2t\/vWUZ1T957WbYkxKhvW2dkZxhim0+kbe+fn54xGI4qixBjBOUdRFDjn8CGQuozZZcWDSY5zbhPVodR1vVHQ\/7gOE9XNek5mJQj0pGJ5tWCQ91gvL0mc4+EHH76BV1VCCAA0PhC852wtPNruv5NHRJCqqjYqqr3iIzhr6KUGAO89Ozs7nJyc3MHu7+9zdHTE8fHxnfze3h4HBwebqA6TTQgAX5c0vsFkGfPSMBmmiAinp6f8\/fLlHexiseCrjx\/z6P4nvDc2JLPf+fnXp8zn8y5UJCLyToCqkqYO5xJ6ec7YtBWirnkyGNO8P+VFYshDZOgjH7mM33qP+Wt7zmS6Yvb0CV989y02PGcTF0AnD5VlyeXlJaPRiMFgQJZlAGzvfsPsnxlfDvsISihLHnz+GT\/ee4F7\/gdJYbnYNfz5cAddf08Xrk4eqqqKEAJZlmFeVSjGSFk1BBVcYqgb2LrXx3vPxapmWSrjfkKIyrifYiV2EXTYqWViU3xUxll6k2+aBmugXC1Ra0mc4+oqkOc5qXimfUG1wpiUzBm8j91athEB+LoA76mqAS5CMsgREYL3eO9pmgYtCvr9Pnmeo6osLhaIEVSVusiYTCZdqLp5yDlH0zSEEIgilMslaZpiE9tG28brdgIYa1BVrDUoEFXZXJ+OX5mIMBwO7+RDCMQQKcsCaxPW6zXOJSRuiqq2LbUW7yMxKqgitwS\/VVAH0dgkJSI4KyS2PbSua9raCjFGjAjD4QgjgjEGY8yNZ4y1ncRAx5bF0KCqqDhiCIh7dQ9jSbMcRBABxdAWIyIIIq2HNEZQpcvQ7FSh69mUZdmdm\/70yzMu1p7pKOWq8BR1YPfTKT98vUXUiKgQNZKYTjQAbJz2twdlkrQHt97wRIWo\/3mwl7b5uq6p65per9e205guvx7wtll2beTX3733ADexbbdgrMFJ65lr3HK5ZLVaEVVJrGFr6\/7N\/usct5\/\/BSOPoERFJpF9AAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris3-posts_big.png\u0022 alt=\u0022Screenshot of the list of posts in version 3\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris3-post_big.png\u0022 alt=\u0022Screenshot of the top of the post page in version 3\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACrElEQVRIib2VTW7cRhBGX1V3s0nOUGNLTrIIggTeBYJhJSfwVQzoBr6FDpO1LxB4lUXuIMABpAiSrJnhX3d5IWlkw3ZIB0E+gADRLLJedX2slpyzMaGzszNUlYODg0+eXVxc0DQN222LqhBCYLvdEkJgTIkiRP6+7vhuvyKEMJXqtfR9Pwn0P+q1N5vm+evtWxChiJFmuZxT6b+SiODnBPoQcM6hqqzXa0SElBJFUdC27S2gGUMSDOXJ\/vKLCf9JZjYP6PDwkMvLyzmhHB8fc3JyQtu2mBl1veCPN7\/x8y8veLL\/\/eT7fg7112pz0zGMLWUsyTlTL35C\/WpyhwBmeehrZWJsc2bpHNkyz49+xcVyVnGzWnZfmbu7DDBVyIZgGJAAUUVEMBv4drVCnaMsy1tIs3k7NKdlT0MklzV7zlOpIgJ\/eqEaRp6iDNm4TCPv0shKlNXqEaqKquJDQICc8zygyQjgWb3EkiFApY7TsSfuNXRdy6M+sRChy5kuZX6MJaYFF9uEU3DdSB0dpZ+GgZkeerO+YnN9hRMhmTEWBU04IBQFv5+e4oeBDJjBD92WMRvdkInBURZCWTjIaZ6H5rTs5atXtG27W0t3CUWgKtxH8UdHR6gIhVdU4Go9MCZjVSlOp3dp1tFxfn7OOI6ICKq6q1REdt64H5Z1vaCoFqw7Y9snCicY8E3j0WmgeUeHmaHOoSKYGTFGvH+w3z1QzgnnPGZGPyaiV\/ox87gpUJ0302aZOqWEc+4WTJW265CuRZ2\/NQ6gqvR9T13XlHVkr1YEIQwj19fvKB8v\/pvf\/j5ZSpnlcgFAJUL+oG1mRlVVO+jNZkOMEYBYCk1Z74qZBPrc4odDzMxY1DXGg2fuUBB5mC+bzYamaQC4ubmh6zq8D7hYgUEd3Sff\/dz9e6J1Rd1KBbTbAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris3-post_big.png\u0022 alt=\u0022Screenshot of the top of the post page in version 3\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris3-post-end_big.png\u0022 alt=\u0022Screenshot of the bottom of the post page in version 3\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACD0lEQVRIia2VTW7bMBBG3wxJmaptpE4CFGgO0C56id46y+56ha56gaABEieWLIkzXeTPRZuatTsrShp8fPpmhhQzc\/bE1dUVqsrZ2dlv366vr1kul3Rdj6qQUqLrOlJKTKXQpBk\/bre8O21JKe3b6lKGYdgLtF6vCaqYP6S6OyKCu6Oq5JwREVR1n9ReoOi+l4ebmxsEMHdCCLg77k5QZSrl+R1A27asVquDaESEWJMYYwAEsYJqADfMQVSIO06JCCqHu+TudUA5t5gZzWxGk9IvJRORZzGAEKskX434JPi36PseM2MYBkopAKgq0zjyZj4nPUKaGTlnYtseDlTTQ22bEdHnTZ+ad9cpMyfGiJlRo\/kqUE1SKUaMSlvx58dOWlXJuq5jsVgwjuOjK9A0s5pz5d+BapJ6z+gotDExmqPANEB\/39M2gaCCilDcWeQjm7qm3jO2tCkxn0VUX6Zq0UYE2DX5mP6BypLN354TgjIWYxqcnAJNOvpU\/jNQTdLl12\/ENCPEwMnJyV9zL04zHy+WhwPVWPzlu7PZ3j8+3bKaJxAQhHU3MpYXjc+fzvnwfnEwkGy32+OK\/n\/jMtaM7t3dHapK0zTc9kZOAXi4XIsZQYVhclJ4mLSkSj8WFvllCGpDn5paRHhtrapIaOgnyCmgPoI7UzHERqxMqBheBnCnTFuiOv04sdls9urvrn8CnvD1+u2+X7sAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris3-post-end_big.png\u0022 alt=\u0022Screenshot of the bottom of the post page in version 3\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EVersion 4 (current)\u003C\/h3\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-home_big.png\u0022 alt=\u0022Screenshot of the homepage in version 4\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u00221666\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAkCAYAAACJ8xqgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEA0lEQVRIiY2Wz4scRRTHP1VdP3pmd2fdDSIkm7\/CeDC5aPAvEEHwIngSD168ePLi3T9CEFFEzFmQEBFFNqd48WwiyLgTd3p3pqu6uspDd+9sT3eyeXTxul\/xXr336lvfLnHr9huJS3Lz6IhlUbBcFqQUL08hhEQrhRCCBMRYE2NCa43RGmMN6t133sYYg7UGay2zvT2Ai+9mGKyxl2ytNhatFVJKhBDNiDH2MuzEOcf5asV6tSRGSGSkFNmf7TOdTjDGjLkhUkqDgCml0dHNPU+Uc258pa4EIdqSYD4\/wVpLnluUUmRZNvCTmz41wxiDUhn\/zv\/hwf2fkbILLJnN9jBG41zg6XnsLXqx+FhZAH8e\/8IfP90DNrYQamKMhDpis\/G2yLFyV2cLVmcF5X8nlMv5BXxi5ygg1OO9lNspA\/z2w5c8evgrk92MLz7\/DHd2CoDRikwpJloyndjRktXYKt88eMSP94955XCPxVnJh58GJkBZOqQUhDqiQkFuZgPf0YCHL1\/nrbt3yCc5mczoOpPntnXSSDFa8RDYY40OIeCc43RZ4JxrQe\/QSl58O+dx3iM++viTVLZG7zwpJVbrNWXp8JWnqirqOnIFnjcl\/378sGeYTqdYY1gWS25cv8GTv59gbVOqc47Dg0MWTxfcPDrir8ePx3rYb8ZqtW7xKFgWS0BQ1x3rCNblGhCcLBYIIQdHcQDslBJ5npNSajYkpQtIpJQ4ODggpUSMkRjjiwG7qioAsqyZViojb8v2zgMQY0TKobu4dfvNF2z3RrTWGKMx2mCMxlqDaflSfPX1t6khWNsj1bx9v0y+1liM0Ugpe6QKbPQ2H87nC8pyRWZ2uHY4I3sGgAESCa36Z0Nt71Ke1xi7jwDqUMEI53XSbVQvYJdqJ4Vz5FJRx0imRk\/mJsMWAc\/NcL+SrHRNSs1OXiVXZrhEotLV\/4423CDDAZAOMoWQkkxK6rq+IuBwxwZsMy8cJnlijL0TorVGaz0IYHS\/zwPYrFxAxEBq\/yVCCLTaBEokmqfRUoqLY+i9R333\/T3Ky5zWvXvft3mP77T3OOfxvsJXjQ4hNAm8+nr\/bvPS\/j46y1icnlLXAaU0QkCsGoekJLJOkJoatrs8AFpVlgghmWYRpSQxZQgBfmIb3BnI1hVG1\/g6UrhBwC0+9J61EIBAAImq7WVoV2zUeQlKaXZ2+iShPnj\/va2b1dYty2zZWrIw1pKNkcT2LhdFgQ9w7WCP5ToxmwhKn0BArgU+JIwSnJeJnXyIwwGwpzu7ZGYXgHXDpfgaQtv9zuZCc5PopGNsUZZlqqoK5xzl82DjPM432rffpe\/DzXuPeO3O3V7V0zxHZBJoACuAqsVYJyGEZ571\/wHlDJySzIsFTgAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris4-home_big.png\u0022 alt=\u0022Screenshot of the homepage in version 4\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u00221666\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-posts_big.png\u0022 alt=\u0022Screenshot of the list of posts in version 4\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADcklEQVRIiaWWz08kVRDHP+9n\/5gZdkFEgrBZ4wE18Wb8EcOiiRf\/BC\/GswlXjx688U9w5GSMx71hPJLswURijAc3JBrWhGGEZqaZ6ffKQ08PAzuQgJV06tv1rVevurpedav3PvpEBFAKRLgUBQhYa1BKATCqqtosNT8TA2nqiVHQWiMxAmCspaoqqqoixtpxek+lIITwk22CNcZpefLkY3q9HkdH\/xBi4Pi4+5LfNNYarHWkSYq1Bu893nuctSRpgrUWZy3OObz3JGPeJzUGtP3s081Lh8RfOiaexCeXtvF9s7i2JVO+tU1rjVJqopurTn62bqQoClRs6ndP6fcHFOcFxhheWViYbCIid8ZFUfxs\/08yABhLkj+gioLUkSeU3BED2Otlu6u0Uj\/T3mwUY0REJrqxX8cAIYTKlmVJHJ+E2zK\/jzT9M91P07jxaSTP86F1zl0hph365ZBuH6JAogJpcUK6tADjgH+dgtPgZUjOENduTda+KBRW1\/gigNUwn0UybzHG3PgQ9lZSQ8cMsNaSZRk8XJ5wu7u7bG9vz1y3s7PD\/sEhTgtp\/4R8cX7CbWxssLxcx5nV4PYmosFleYFSFxhj6qTG9m63y8HBwcyE9vf32dra4uFchyjC6Vkx4fb29iYJzSzC9caaxiLCoBwAijzPKc7PaeU5AKnSrDpPRxt6RuFDxETh3xhZWVrmmy++opetsTg34tHFb\/y4\/wtPnz2\/ca8G33rKtPWsrj0mSn2atYYqQhRhwVreSVvkRvN7p4UfjXh9UFJUgZFpsztcw6xn5B1D8ewt1tff5+3e98DLA\/FKhW5kgKqqCGFIiNAfgTOQWhDgzQ8\/4PPvvgVgUynGQwilFKuP4OvHP7By1KX994jDNzJ+Xdnkj3e\/vBJ\/VqvcOqkH5ZAXBXgdmQ\/n6Ln2ZfW04s\/nhxhjEIloXetOu8ODTouzkxOiqHFlK7TVmKxF5nPSNL0poae3Vsho8KNjFIpRK4eLISKRLM8BIUkSjNaEEHDO0Wq18N6hreVsOEJE0KrmkzSh7TXOufv3ENqy9NrKlV8MqHuIOCTPMgRhrjM3eV3OOUIItNtttNKIRASwxuL91Zl352NfVRUhDgkBiiF4C5mrGzz3htOzM4wxDAYlEiPaaF5dXMRaS1mWKBQxBrSxiBd8MvszMy3\/AclyutTtBlE0AAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris4-posts_big.png\u0022 alt=\u0022Screenshot of the list of posts in version 4\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-post_big.png\u0022 alt=\u0022Screenshot of the top of the post page in version 4\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACVUlEQVRIib2Wz04bMRDGf+P1er2ABKrUC2ovPXLupapEi1SpT0DfgqfgoXqteu1DVOqhQfwvCoRge6aHJGSBQKIU9ZMsfzv+Zjy2x7srb999NANEwIwpBDDwvkJEAEg5j8w2Gp\/JgRgDqoZzDlMFoPKenDM5Z1RHwu6cIlBK+eYnwSbGLra333N+fs7BwSFFCycnpw90Xe4ceF8Tm4j3FSEEQgjU3tPEBu89tffUdU0IgWY8HpoRB5z\/tPNhKmjCVNgEmtBMbePnifPI1nS0I5tzDhG57SdtlPzsfoJ+v4\/oZP+WxOHREVqUto3EOGoAZnY74aK83+9\/98skMRgmcDUAzeoGognNiZwzV1dXOBG8QdXGWx\/rFOhjHMDf37ZFsBLDlIcAhAeap3YCQFUxszutlKK+lDJzsOv81IqWwaSuunXmnGNtbe3ap5QeFU3s+\/v79Hq9bsgHN3IePu7s8GV3d77QFsDW1pYB\/9T29vbMzExVb+PO4F\/dOKlugjP5\/4KfVyfPmdQicy11y5bFInO55wp0R3\/Pt3tJ5p2EX+RIvAhRHI1AFEcUoRZH5YSkymkM2E3i5ThUUuXajKEZGUgC1YKLWuhN\/cYHYtPSVhWtq6hFECA4x680ZLDaUkJic1hYdx7FuFFlqMpFyQxU2RD3fDX0OkbadoW28lyXzIsQGKoSXcUPTayvb6Cq\/Pzd43NcxczIalyWzCvXcpZu2AwNIjL3u+YfG+jyZMafUtAxv8iZG1MuS+EkZ8LxGSLClcFxSmxUFQNVFOiXQjbjevxfNA9\/AVXov5JBOanJAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris4-post_big.png\u0022 alt=\u0022Screenshot of the top of the post page in version 4\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-post-end_big.png\u0022 alt=\u0022Screenshot of the bottom of the post page in version 4\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACx0lEQVRIiZ2Wy44cNRSGv2Mfu6qnJ5NZgMSKp2CDkAJIvBALFrwZy4gFQnkMFihCBLVGM9NdrrIPC1dXVV8nyS+V\/Nv++9x8a\/nm2x\/MABEwY4YABqoeEQGgH4Y6bHX+LAfaNlKK4ZzDSgHAqzIMA8MwUEoVLn2KQM75re6N7QeXePPmOzabDe\/f\/0MumQ8f\/jvRLblzgqrSNi2qSoyBGCNBlaZtUFWCKiHU8SbG2ja1VdVBf\/rx+ypojgUNzchTSoQQaJuG2ERUlVW7IgQlhkC7amnbFvUe7z3OecQJVgoiDucdr+\/upkoftwsMUvb1u4KHhwdKKYgIZrUSIJgVEMHMcAsntlh7o67lzc0NMUbMbArkDP9NXwoGYLfrKFawYtXFIjPvHEPOeO9ZtSuKGewdyVglIIQwBXoQ8BHXM2U7watXt+ScAZliKcWIMeC9n4zGGA+MX6nERa57Y8elXvZDCDjncM6RcwEBRQhB60kadaWUgyqc+5aaY5iZ0+fn54uCl5DzcNBfbtZj7r2f+KVVeXp6anW9Xl90mFLCjyfnU\/E5S2Zm6DXRcyr8u3nkqy\/vEXGkDI2Hbmz7Ak7AOxgyIIY6IQ2gHroBVlpPWp8heLgJ1xO5euy3A6QM6xeMfAq8my\/iYzw+Pv5+9ZR5mYMZCvz9AHdtzfyLdZ3fz6mbf\/fXBm4bKFbH79t5LpdaqUu4eg+5Razq4Ov7U81mV9+jbNCqcRtl1I2PHIdcZObHWwWYj\/05nDy4Z7DMfnZ+ym3kQr0e9thzMyPnXAM6vkHP3RulFFJKdCmRutp2XTeNdV0ipW5sa79L3aRNk2buz19PSoldt0N\/\/uXXyfB+IvU9KfX1zuh7tjmTS67ZjBU3myt4uA3H\/y0GeUziY5HzgP7x57uLghgCIYbJqXee1Pd47\/De0fcDpRRUFRFhu91+tPNzEJHyP7\/y5So+jfmBAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris4-post-end_big.png\u0022 alt=\u0022Screenshot of the bottom of the post page in version 4\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022464.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["programming","php","javascript","webpack","symfony","symfony encore","doctrine","orm","cms","esse","columnist","version","new year","blog","design","redesign"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris4_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris4_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4_mini.png\u0022 alt=\u0022Screenshot of the new version of the blog\u0022 width=\u0022240\u0022 height=\u0022117.03937007874\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EIt wasn\u2019t really supposed for the New Year, but I\u2019ve had plenty of free time on my hands\nduring the holiday break, so here it is already: a brand new version of my blog \ud83e\udd73\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris4_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4_mini.png\u0022 alt=\u0022Screenshot of the new version of the blog\u0022 width=\u0022240\u0022 height=\u0022117.03937007874\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EIt wasn\u2019t really supposed for the New Year, but I\u2019ve had plenty of free time on my hands\nduring the holiday break, so here it is already: a brand new version of my blog \ud83e\udd73\u003C\/p\u003E\n\u003Ch3\u003EWhy?\u003C\/h3\u003E\n\u003Cp\u003EWhere to start? I got increasingly annoyed by the old version.\nThe design started seeming boring and blunt.\nThe code started growing unstable.\nIt was using the \u003Ca href=\u0022\/projects\/micrus-v4.lite\u0022\u003E Micrus framework\u003C\/a\u003E,\nwhich I \u003Ca href=\u0022\/blog\/how-and-why-i-ve-built-and-killed-a-php-framework.lite\u0022\u003E stopped supporting\u003C\/a\u003E a year ago.\nIt was using \u003Ca href=\u0022https:\/\/gitlab.com\/Avris\/Micrus-Assetic\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Micrus Assetic\u003C\/a\u003E\n\u2013 an outdated method of asset management,\nwhich highly depended on the server configuration and binaries installed.\nThe deployments were \u201ciffy\u201d, to say the least.\nI was afraid of sending even small changes to the server, for fear something might blow up again.\nSome small things I didn\u2019t even notice were wrong: like still warning about Google Analytics tracking,\neven though I migrated to \u003Ca href=\u0022https:\/\/matomo.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Matomo\u003C\/a\u003E ages ago.\u003C\/p\u003E\n\u003Ch3\u003EWhat changed?\u003C\/h3\u003E\n\u003Cp\u003EI decided to keep it simple. To go with a \u201cless is more\u201d approach.\nI\u2019ve removed soooo many features that just weren\u2019t useful enough.\u003C\/p\u003E\n\u003Cp\u003EI used to have an admin panel: except I didn\u2019t need any fancy editor,\nthe main content was written in an (enhanced) \u003Ca href=\u0022https:\/\/www.markdownguide.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Markdown\u003C\/a\u003E anyway.\nSo for this version, I\u2019ve decided for a filesystem-based approach: I\u2019ve ditched the database completely,\nI don\u2019t need any ORM, I don\u2019t need an admin panel. I just have a bunch of \u003Ca href=\u0022https:\/\/gitlab.com\/Avris\/SUML\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E SUML\u003C\/a\u003E files\ntracked by GIT. Using \u003Ca href=\u0022https:\/\/gitlab.com\/Avris\/Esse\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Esse CMS\u003C\/a\u003E.\u003C\/p\u003E\n\u003Cp\u003EI used to have three language versions of the UI \u2013 but why?\nI don\u2019t think anyone really cared about not seeing the names of the categories in Polish,\nwhen most of recent my posts were in English anyway.\nSo now the posts are still available in the language(s) they were written in,\nbut I\u2019m no longer maintaining the multiple versions of the UI, or the features to filter content by language.\nWhat a relief!\u003C\/p\u003E\n\u003Cp\u003EI used to have a \u201crandom post\u201d box. I used to have tag clouds. I used to have an embedded Twitter timeline.\nI used to have the latest posts on the \u201cabout me\u201d page (really?). I used to have a link shortening feature\n(\u201davris.it\/l\/\u0026lt;whatever\u0026gt;\u201d) that I\u2019ve never used, not a single time.\nI used to have a separate controller and database fields to support redirects for legacy URLs from the even earlier version.\nI used to have database fixtures. I used to have a console command to export legacy comments to \u003Ca href=\u0022https:\/\/disqus.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Disqus\u003C\/a\u003E.\nI used to have tooltips and popovers.\nAll gone now! \ud83d\ude0d\u003C\/p\u003E\n\u003Cp\u003EI replaced the infinite scroll with... just loading the whole page at once \ud83e\udd37\u200d\nIt\u2019s not that big for today\u2019s standards anyway \u2013 the heaviest list of posts is 113 KB of compressed HTML.\nInstead, I now lazy load images using the \u003Ccode\u003EIntersectionObserver\u003C\/code\u003E API.\nWay simpler than infinite scroll or pagination \u2013 which aren\u2019t really necessary with this amount of data.\u003C\/p\u003E\n\u003Cp\u003EOh, and the website is now using a fast an sleek \u003Ca href=\u0022https:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Symfony\u003C\/a\u003E setup with an HTTP cache \ud83e\udd70\u003C\/p\u003E\n\u003Cp\u003EDesign-wise, I use the default \u003Ca href=\u0022https:\/\/getbootstrap.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Bootstrap\u003C\/a\u003E theme,\nwith only minor changes of variables and minimal additional styling.\nThe main feature, obviously, are the tilted elements. That\u2019s enough to add some personality to the website,\nwithout making it overcomplicated.\nCompared to the previous version, it\u2019s way more contrasting, better tailored for wider screens and clearer.\u003C\/p\u003E\n\u003Cp\u003EOverall, 41090 (!) lines of code were removed:\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-3-4-diff_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-3-4-diff_mini.png\u0022 alt=\u0022424 files changed, 6711 insertions (+), 47801 deletions (-)\u0022 width=\u0022240\u0022 height=\u002238.836363636364\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThere\u2019s nothing else in the (programming) world I love more than removing useless code!\u003C\/p\u003E\n\u003Cp\u003EDeployments are now stable, simple, reproducible and revertible, thanks to\n\u003Ca href=\u0022https:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Symfony\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/webpack.js.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Webpack\u003C\/a\u003E,\nlack of database (no db = no migrations),\nand most importantly my recent child: \u003Ca href=\u0022\/projects\/avris-deployer.lite\u0022\u003E Avris Deployer\u003C\/a\u003E.\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris4-deployer_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-deployer_mini.png\u0022 alt=\u0022Console output of deployment\u0022 width=\u0022240\u0022 height=\u002267.075471698113\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EVersion 1 (\u201dSilva Idearum\u201d)\u003C\/h3\u003E\n\u003Cp\u003ESo, to finish this post, let me just quickly show you the four versions I\u2019ve been through so far:\u003C\/p\u003E\n\u003Cp\u003EMy first blog was called \u201cSilva Idearum\u201d (Latin for \u201cForest of ideas\u201d).\nIt was anonymous, spirituality-oriented, Polish-speaking, Joomla-running and with a \u201ctoo much\u201d design.\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris1-post_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris1-post_mini.png\u0022 alt=\u0022Screenshot of the post page in version 1\u0022 width=\u0022240\u0022 height=\u0022116.125\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EVersion 2\u003C\/h3\u003E\n\u003Cp\u003EAfter coming out and becoming independent from my parents,\nI was finally able to put my name on my posts. New domain and a redesign was also in place.\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris2-home_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris2-home_mini.png\u0022 alt=\u0022Screenshot of the homepage in version 2\u0022 width=\u0022240\u0022 height=\u0022116.125\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EVersion 3\u003C\/h3\u003E\n\u003Cp\u003EAs announced in \u003Ca href=\u0022\/blog\/brand-new-blog.lite\u0022\u003E Brand new blog\u003C\/a\u003E\u2026\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris3-home_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris3-home_mini.png\u0022 alt=\u0022Screenshot of the homepage in version 3\u0022 width=\u0022240\u0022 height=\u0022400.875\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris3-posts_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris3-posts_mini.png\u0022 alt=\u0022Screenshot of the list of posts in version 3\u0022 width=\u0022240\u0022 height=\u0022116.125\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris3-post_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris3-post_mini.png\u0022 alt=\u0022Screenshot of the top of the post page in version 3\u0022 width=\u0022240\u0022 height=\u0022116.125\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris3-post-end_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris3-post-end_mini.png\u0022 alt=\u0022Screenshot of the bottom of the post page in version 3\u0022 width=\u0022240\u0022 height=\u0022116.125\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EVersion 4 (current)\u003C\/h3\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris4-home_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-home_mini.png\u0022 alt=\u0022Screenshot of the homepage in version 4\u0022 width=\u0022240\u0022 height=\u0022416.5\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris4-posts_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-posts_mini.png\u0022 alt=\u0022Screenshot of the list of posts in version 4\u0022 width=\u0022240\u0022 height=\u0022116.125\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris4-post_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-post_mini.png\u0022 alt=\u0022Screenshot of the top of the post page in version 4\u0022 width=\u0022240\u0022 height=\u0022116.125\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris4-post-end_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris4-post-end_mini.png\u0022 alt=\u0022Screenshot of the bottom of the post page in version 4\u0022 width=\u0022240\u0022 height=\u0022116.125\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E","words":694,"readTime":3,"lang":"en"}}},"projects\/avris-deployer":{"key":"projects\/avris-deployer","type":"article","published":true,"meta":{"createdAt":"2019-09-26T20:23:40+02:00","publishedAt":"2019-09-26T20:21:00+02:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Deployer","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-deployer"},"content":{"en":{"slug":"avris-deployer","title":"Avris Deployer","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-deployer_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHoUlEQVRIiX2X248lVRXGf+uyd9Xp0+d09zDTMAzMiBcUEQlEIwkxIAmCMWrUeE+IPuiDxgf\/BN9988FofNYn4wtqotF4SSRRNF4AcSI6wwADMpe+nD59+lTtvXyoM4NR407tqkpqX779fXvt9ZWcft2dEaUiBFErRMXcqaUQBCqCq2GqJHeWyyNqrRABEQggIghDERFEBAiulYhY1Xr9SVREQEUwd0yEphnhEUJEUGpBAgQopSAMA6vqAFQUaiWp0q8mQGTVjuvtRQAEVXltcrn2LkQtiBoRr\/UzhKSG1oJf6yAoQSUCNATRYTJCUHVA6EvFEBQhRDHVFRvDQpDh2+pa3Qx3Y7nsEIGIYYEiiq76mipuRlLFQxRqWRE8MEIEEoKq4aaYCCaKmyJRqbVSaxkGcB9WXyuqdl0qFSFqJQg2NjfZuXIVqBCCOEQtqAoEmCnJjOTXAAmgAnXFloCIrmQwFMXMyJZYaxqInvnhIY07TXKyJ2qtlNLTlwLCsF9qBeDkjdsc7c+AOpAWgWpegVJMB4mzKS6aiOiQMJCKXt+kgpoN1FpCRVExVJxxM2ZrtMHV2S4aSpSg8YR6hiiECH3fDQwJnDl9mpfPPY9IIAGmghBAQiRWkinJFNeU6YNVxNi1WAHVgR01TI3sTraEYogkmpy47cQ6B\/MZR13HjTdtszycM9vbx0TIrtSo3P\/eB3jumWeZ5oYghsgCVAdRVEEEkinZDQ9NSFrJhVADTBRVQ0Vxc1yN7BkXIZmTJJG04dixDT7y2Y9xbGuTn3\/vR1w4fwFr1kk+RNNkY537H3qAp3\/xazbbEVCIiAGMCipgJpgOrLkLTm6Q3qk1VvoCKKKGmiNiuDmqCTdb1cSD73s3n\/rSJzj75F94\/OvfJVG4\/eTNRK3sH8zwbHzmq1\/hye\/\/kFumU5alo0ZPRBmk01V0pWEP5aSYgks7pnYFqXU4hwIQQ8RRSyRzVI1kTpsya+587osf5ZEPP8js6j4\/\/ObjZB2xtdbyhjfezMHuJXbnCx768qdJKtTzL\/CWk9vMDucs+iWl9pTagVaSG2aCu5JcSMlwWZsiyx5KJWDYIygiCRVDPJHVaVNiLSU+9djDPPLhBwH406+eItEwWZ+wvtawf+WQM+94O+\/9wP142\/DE177N9vENNDp294K+T5QYAIVUVIOUHXchJcWz4DKaQO6hgIRCKIKCOGjC1Enm5JS4++7b+ODH33M9Jey9MuOGG09w6sw2Z+68lVvvfT3j41Miggs\/\/iXjUabUddqmJzcVpFKjo++XRO1QA9XA3MiN40lwJhvQFyJkABNDdBmOa0JxZHUAPvaF910\/mQEe\/fzD\/GeJCF785e9YvnqJE6+7gaPdTHeww1Q7xCpiQa0dtT9C6FETzJWUM940uE4mpMKQZ1A8lISRxclhZDUyyr13n+bUqWP\/BeDfSy2F537yW\/bPPkc7VtqNNU7ccQv97pi9i4LKEqEDyURJiNQhwpJjOWPNCJ9sTigFWDFkCB5OK0aL0mI0VbjvXW\/8v2Bmr+7yzA9+zfLyq4waQKE7nDO\/3OGyZG2joXZlOHsMVBKykkvdsdyizQi\/6diIvg9qgRqKhqIYKYSE0YbSFOH21x\/\/n0AO9+b89VdP8eLvnyVLx6itQ+qoEB3UI4NGySMlkmISK5kSaoqljOYGSQ3kEf627Yb5omN5VFj2lVIqpVYkBImKFcFEOL61dh3EweyQ82df5Nwf\/86LT\/+DJjrWvBCp4FSyVkIDLGA55EpJQfLATTATNCUkt5AMcobcgLf4B7Ze4mBRmc069g4qsz7YP4L5EhYdLHuh9PCtb3yHw4NDdi7tcXBlj1wKiUKi0mplZD3jFEzbynKtwiRIIbQ22AwpHeqBZcNq0HWGMaE7MrRN7F5do6NFHrzzeEzbxGIxWCW3TIhTcUISBadqpq8Qq0S7vX0Tly9dwm3Icce3NlkczGgUxhkmGU6Mg5Nbhe3jxngsPH9xh9nhISV3bN58il\/89Decu3jEtK1sb0846oInnjlA1jePhaaMqGEp4U2LqOG5wVPGUoOqkZsW88SxzS3ece89bLaJg\/05KTdkzyznS6ZtYrG\/zx1vOs3sn5eYX7zADWNhawxJe5L1bI57br97jZ2LM169uMdkBC49J+57mPmff4bf8aFP0qxP6Su04wmeGlQdMSd5QsQQhrwjITQqbNyyzcF8wX6dc9dtJ3np5ctoC9JkxtMTXNhZImVMtKc4SEpORpsq1SqLptLZJsfeDDuzv5NGQuM9\/dU56299BC8Ys\/khWGK5v4\/IAaCDF4rBgA9eCKRUKJWXnj9HXSygFs4++zTRVyQCj8BD0FpYS5lJcibubDSJtSS4FtYyzBYd77xrygtXlXqlZzqCyc5LvOnRe\/Cr5\/\/GsutQz9RS8WYEr\/1DIICnhKoiEUQNNAKJQGtF+v66wa\/dES7GzivP0+YWF2FjfcoN0008KvP5HlvTCX941nn5\/IhXdhecu7xglINGl7y\/fwE5\/oY7opZCv1xSuyPSeIKEUJYLNDVDOqg9nluAwXMDWgNFkLjmoQcvrhEIFRPBVXABpZLNUA36vkMobE+cjda4cjjY3MbhvjPwL+zZPiPSuvOOAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-deployer_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EI got tired of creating deployment scripts for my project, so I finally put together a simple, language-agnostic deployment script based on Makefile and symlinks.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-deployer_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHoUlEQVRIiX2X248lVRXGf+uyd9Xp0+d09zDTMAzMiBcUEQlEIwkxIAmCMWrUeE+IPuiDxgf\/BN9988FofNYn4wtqotF4SSRRNF4AcSI6wwADMpe+nD59+lTtvXyoM4NR407tqkpqX779fXvt9ZWcft2dEaUiBFErRMXcqaUQBCqCq2GqJHeWyyNqrRABEQggIghDERFEBAiulYhY1Xr9SVREQEUwd0yEphnhEUJEUGpBAgQopSAMA6vqAFQUaiWp0q8mQGTVjuvtRQAEVXltcrn2LkQtiBoRr\/UzhKSG1oJf6yAoQSUCNATRYTJCUHVA6EvFEBQhRDHVFRvDQpDh2+pa3Qx3Y7nsEIGIYYEiiq76mipuRlLFQxRqWRE8MEIEEoKq4aaYCCaKmyJRqbVSaxkGcB9WXyuqdl0qFSFqJQg2NjfZuXIVqBCCOEQtqAoEmCnJjOTXAAmgAnXFloCIrmQwFMXMyJZYaxqInvnhIY07TXKyJ2qtlNLTlwLCsF9qBeDkjdsc7c+AOpAWgWpegVJMB4mzKS6aiOiQMJCKXt+kgpoN1FpCRVExVJxxM2ZrtMHV2S4aSpSg8YR6hiiECH3fDQwJnDl9mpfPPY9IIAGmghBAQiRWkinJFNeU6YNVxNi1WAHVgR01TI3sTraEYogkmpy47cQ6B\/MZR13HjTdtszycM9vbx0TIrtSo3P\/eB3jumWeZ5oYghsgCVAdRVEEEkinZDQ9NSFrJhVADTBRVQ0Vxc1yN7BkXIZmTJJG04dixDT7y2Y9xbGuTn3\/vR1w4fwFr1kk+RNNkY537H3qAp3\/xazbbEVCIiAGMCipgJpgOrLkLTm6Q3qk1VvoCKKKGmiNiuDmqCTdb1cSD73s3n\/rSJzj75F94\/OvfJVG4\/eTNRK3sH8zwbHzmq1\/hye\/\/kFumU5alo0ZPRBmk01V0pWEP5aSYgks7pnYFqXU4hwIQQ8RRSyRzVI1kTpsya+587osf5ZEPP8js6j4\/\/ObjZB2xtdbyhjfezMHuJXbnCx768qdJKtTzL\/CWk9vMDucs+iWl9pTagVaSG2aCu5JcSMlwWZsiyx5KJWDYIygiCRVDPJHVaVNiLSU+9djDPPLhBwH406+eItEwWZ+wvtawf+WQM+94O+\/9wP142\/DE177N9vENNDp294K+T5QYAIVUVIOUHXchJcWz4DKaQO6hgIRCKIKCOGjC1Enm5JS4++7b+ODH33M9Jey9MuOGG09w6sw2Z+68lVvvfT3j41Miggs\/\/iXjUabUddqmJzcVpFKjo++XRO1QA9XA3MiN40lwJhvQFyJkABNDdBmOa0JxZHUAPvaF910\/mQEe\/fzD\/GeJCF785e9YvnqJE6+7gaPdTHeww1Q7xCpiQa0dtT9C6FETzJWUM940uE4mpMKQZ1A8lISRxclhZDUyyr13n+bUqWP\/BeDfSy2F537yW\/bPPkc7VtqNNU7ccQv97pi9i4LKEqEDyURJiNQhwpJjOWPNCJ9sTigFWDFkCB5OK0aL0mI0VbjvXW\/8v2Bmr+7yzA9+zfLyq4waQKE7nDO\/3OGyZG2joXZlOHsMVBKykkvdsdyizQi\/6diIvg9qgRqKhqIYKYSE0YbSFOH21x\/\/n0AO9+b89VdP8eLvnyVLx6itQ+qoEB3UI4NGySMlkmISK5kSaoqljOYGSQ3kEf627Yb5omN5VFj2lVIqpVYkBImKFcFEOL61dh3EweyQ82df5Nwf\/86LT\/+DJjrWvBCp4FSyVkIDLGA55EpJQfLATTATNCUkt5AMcobcgLf4B7Ze4mBRmc069g4qsz7YP4L5EhYdLHuh9PCtb3yHw4NDdi7tcXBlj1wKiUKi0mplZD3jFEzbynKtwiRIIbQ22AwpHeqBZcNq0HWGMaE7MrRN7F5do6NFHrzzeEzbxGIxWCW3TIhTcUISBadqpq8Qq0S7vX0Tly9dwm3Icce3NlkczGgUxhkmGU6Mg5Nbhe3jxngsPH9xh9nhISV3bN58il\/89Decu3jEtK1sb0846oInnjlA1jePhaaMqGEp4U2LqOG5wVPGUoOqkZsW88SxzS3ece89bLaJg\/05KTdkzyznS6ZtYrG\/zx1vOs3sn5eYX7zADWNhawxJe5L1bI57br97jZ2LM169uMdkBC49J+57mPmff4bf8aFP0qxP6Su04wmeGlQdMSd5QsQQhrwjITQqbNyyzcF8wX6dc9dtJ3np5ctoC9JkxtMTXNhZImVMtKc4SEpORpsq1SqLptLZJsfeDDuzv5NGQuM9\/dU56299BC8Ys\/khWGK5v4\/IAaCDF4rBgA9eCKRUKJWXnj9HXSygFs4++zTRVyQCj8BD0FpYS5lJcibubDSJtSS4FtYyzBYd77xrygtXlXqlZzqCyc5LvOnRe\/Cr5\/\/GsutQz9RS8WYEr\/1DIICnhKoiEUQNNAKJQGtF+v66wa\/dES7GzivP0+YWF2FjfcoN0008KvP5HlvTCX941nn5\/IhXdhecu7xglINGl7y\/fwE5\/oY7opZCv1xSuyPSeIKEUJYLNDVDOqg9nluAwXMDWgNFkLjmoQcvrhEIFRPBVXABpZLNUA36vkMobE+cjda4cjjY3MbhvjPwL+zZPiPSuvOOAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-deployer_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EI got tired of creating deployment scripts for my project, so I finally put together a simple, language-agnostic deployment script based on Makefile and symlinks.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["deploy","deployment","make","makefile","php","symlink"],"hasMore":false,"image":"https:\/\/avris.it\/image\/avris-deployer_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-deployer_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-deployer_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EI got tired of creating deployment scripts for my project, so I finally put together a simple, language-agnostic deployment script based on Makefile and symlinks.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-deployer_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-deployer_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EI got tired of creating deployment scripts for my project, so I finally put together a simple, language-agnostic deployment script based on Makefile and symlinks.\u003C\/p\u003E","words":25,"readTime":null,"lang":"en"}}},"blog\/technology\/cleaning-up-outdated-subdomains":{"key":"blog\/technology\/cleaning-up-outdated-subdomains","type":"article","published":true,"meta":{"createdAt":"2019-08-28T14:40:42+02:00","publishedAt":"2019-08-28T18:39:00+02:00","group":null,"category":"blog","subcategory":"technology","slug":"cleaning-up-outdated-subdomains"},"content":{"en":{"slug":"cleaning-up-outdated-subdomains","title":"Cleaning up outdated subdomains","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/askara-redirect_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022184.02266288952\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAANCAYAAADBo8xmAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABb0lEQVQ4jc2UTU8bMRCGn7E9m0AI0A\/1V8CF8v8R50rlzK23HqoiEbJBu2vP9BASbZKlqqKA+lw8tuzxOx+23P\/46eC4g6pSL2qqqiJIAGA0HgNQSmY2m3E0PqK\/P6liJTOmYzKZUMxIMXF8coyZ8ev3Iw+zOTEmqqoiqa79mTn6Ml8hbdu6quLuywWRvez+COxtSy7F+Y8QX4X6Qu46bm5uCSly\/fWKu2\/f+fD5nFiNyE2L46gmLi4u30aQme1kqJ7P0dGISpVFvcDciDHSNg2IEEJgOp0uHfTKdwjS9oK7kc0oz8+0TUvTNKSUyF1HCJHTs9ODi+izW7KceXqaE1OkdB2mE4yAsGw8c4hByOYY8OUkkOJOXK8272usm3pbkLu\/WfRDiMjGCx0sWV0vcDfMQAQESEkppQDLLJWcCcD5p48bwez77NcCtzM0xNDB\/j90CEErOwxd8jdhQ3sOWeK1oL7T9+6jDUH\/kpX35A+PhxaIl\/hdhgAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/askara-redirect_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022184.02266288952\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFor quite a while my VPS was misconfigured \u2013 any HTTP requests it got but couldn\u2019t assign to a vhost, it redirected to the main website, \u003Ca href=\u0022https:\/\/avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E avris.it\u003C\/a\u003E. I didn\u2019t think it would be a big deal, until I recently found out that my post \u003Ca href=\u0022\/blog\/ungoogling\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-computer-speaker\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Ungoogling\u003C\/a\u003E is indexed by Google under \u003Ca href=\u0022https:\/\/askara.avris.it\/blog\/ungoogling\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E \u003Ca href=\u0022https:\/\/askara.avris.it\/blog\/ungoogling\u0022\u003Ehttps:\/\/askara.avris.it\/blog\/ungoogling\u003C\/a\u003E\u003C\/a\u003E\u003C\/p\u003E\n\u003Cp\u003EThis subdomain hadn\u2019t existed for a long time already, my server doesn\u2019t serve a certificate for it anymore, but it requires \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/HTTP_Strict_Transport_Security\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E HSTS\u003C\/a\u003E, so browsers end up showing users a scary error message.\u003C\/p\u003E\n\u003Cp\u003EI had to do something about it.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/askara-redirect_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022368.04532577904\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAANCAYAAADBo8xmAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABb0lEQVQ4jc2UTU8bMRCGn7E9m0AI0A\/1V8CF8v8R50rlzK23HqoiEbJBu2vP9BASbZKlqqKA+lw8tuzxOx+23P\/46eC4g6pSL2qqqiJIAGA0HgNQSmY2m3E0PqK\/P6liJTOmYzKZUMxIMXF8coyZ8ev3Iw+zOTEmqqoiqa79mTn6Ml8hbdu6quLuywWRvez+COxtSy7F+Y8QX4X6Qu46bm5uCSly\/fWKu2\/f+fD5nFiNyE2L46gmLi4u30aQme1kqJ7P0dGISpVFvcDciDHSNg2IEEJgOp0uHfTKdwjS9oK7kc0oz8+0TUvTNKSUyF1HCJHTs9ODi+izW7KceXqaE1OkdB2mE4yAsGw8c4hByOYY8OUkkOJOXK8272usm3pbkLu\/WfRDiMjGCx0sWV0vcDfMQAQESEkppQDLLJWcCcD5p48bwez77NcCtzM0xNDB\/j90CEErOwxd8jdhQ3sOWeK1oL7T9+6jDUH\/kpX35A+PhxaIl\/hdhgAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/askara-redirect_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022368.04532577904\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFor quite a while my VPS was misconfigured \u2013 any HTTP requests it got but couldn\u2019t assign to a vhost, it redirected to the main website, \u003Ca href=\u0022https:\/\/avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E avris.it\u003C\/a\u003E. I didn\u2019t think it would be a big deal, until I recently found out that my post \u003Ca href=\u0022\/blog\/ungoogling\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-computer-speaker\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Ungoogling\u003C\/a\u003E is indexed by Google under \u003Ca href=\u0022https:\/\/askara.avris.it\/blog\/ungoogling\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E \u003Ca href=\u0022https:\/\/askara.avris.it\/blog\/ungoogling\u0022\u003Ehttps:\/\/askara.avris.it\/blog\/ungoogling\u003C\/a\u003E\u003C\/a\u003E\u003C\/p\u003E\n\u003Cp\u003EThis subdomain hadn\u2019t existed for a long time already, my server doesn\u2019t serve a certificate for it anymore, but it requires \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/HTTP_Strict_Transport_Security\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E HSTS\u003C\/a\u003E, so browsers end up showing users a scary error message.\u003C\/p\u003E\n\u003Cp\u003EI had to do something about it.\u003C\/p\u003E\n\u003Ch3\u003ECertificate\u003C\/h3\u003E\n\u003Cp\u003EFirst of all, users have to see \u003Cem\u003Eanything\u003C\/em\u003E other than a security warning. I need a wildcard certificate.\u003C\/p\u003E\n\u003Cp\u003EFortunately, Let\u2019s Encrypt offers them now, and it\u2019s totally free. I just followed \u003Ca href=\u0022https:\/\/medium.com\/@saurabh6790\/generate-wildcard-ssl-certificate-using-lets-encrypt-certbot-273e432794d7\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E an instruction\u003C\/a\u003E to obtain one, and then configured Apache to serve the \u003Ccode\u003E\/www\/default\u003C\/code\u003E directory with the \u003Ccode\u003E*.avris.it\u003C\/code\u003E certificate for all requests that don\u2019t fit to any vhost.\u003C\/p\u003E\n\u003Ch3\u003ERedirect for users\u003C\/h3\u003E\n\u003Cp\u003EOnce the users can see the website, I can show them things. Ideally, just a 404 with an information that it\u2019s not a valid domain, and a suggestion where they might have wanted to go (same request string, but with the base domain). Easy.\u003C\/p\u003E\n\u003Cp\u003EBtw, I used \u003Ca href=\u0022https:\/\/watercss.netlify.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Water.css\u003C\/a\u003E, a ridiculously simple CSS framework \u2013 I just added two lines, no classes, and the page already looks way better!\u003C\/p\u003E\n\u003Ch3\u003ERedirect for bots\u003C\/h3\u003E\n\u003Cp\u003EBut that doesn\u2019t solve the root cause: bots are confused about which domains they should be using. They don\u2019t care whether my certificate is working or not, they don\u2019t understand the message I left there for the users.\u003C\/p\u003E\n\u003Cp\u003EThey need a proper \u003Ccode\u003EHTTP 301 -- Moved permanently\u003C\/code\u003E. So I had to add a simple recognition whether I\u2019m serving a bot or a user, and adjust the response for each of them.\u003C\/p\u003E\n\u003Ch3\u003ESource\u003C\/h3\u003E\n\u003Cp\u003ESo, here\u2019s what I ended up with:\u003C\/p\u003E\n\u003Ch4\u003E.htaccess\u003C\/h4\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs border\u0022\u003EDirectoryIndex index.php\n\n\u0026lt;IfModule mod_rewrite.c\u0026gt;\n    RewriteEngine On\n\n    RewriteCond %{REQUEST_URI}::$1 ^(\/.+)\/(.*)::\\2$\n    RewriteRule ^(.*) - [E=BASE:%1]\n\n    RewriteCond %{ENV:REDIRECT_STATUS} ^$\n    RewriteRule ^index\\.php(\/(.*)|$) %{ENV:BASE}\/$2 [R=301,L]\n\n    RewriteCond %{REQUEST_FILENAME} -f\n    RewriteRule .? - [L]\n\n    RewriteRule .? %{ENV:BASE}\/index.php [L]\n\u0026lt;\/IfModule\u0026gt;\n\n\u0026lt;IfModule !mod_rewrite.c\u0026gt;\n    \u0026lt;IfModule mod_alias.c\u0026gt;\n        RedirectMatch 302 ^\/$ \/index.php\/\n    \u0026lt;\/IfModule\u0026gt;\n\u0026lt;\/IfModule\u0026gt;\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch4\u003Eindex.php\u003C\/h4\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\n\u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EisBrowser\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($ua)\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Ebool\u003C\/span\u003E\n\u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (!$ua) {\n                \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E;\n        }\n        $isProbablyBot = (bool) preg_match(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027#bot|crawler|baiduspider|80legs|ia_archiver|voyager|curl|wget|yahoo! slurp|mediapartners-google|facebookexternalhit|twitterbot|whatsapp|php|python#i\u0027\u003C\/span\u003E, mb_strtolower($ua));\n        $isProbablyBrowser = (bool) preg_match(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027#mozilla|msie|gecko|firefox|edge|opera|safari|netscape|konqueror|android#i\u0027\u003C\/span\u003E, mb_strtolower($ua));\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $isProbablyBrowser || !$isProbablyBot;\n}\n\n$url = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027https:\/\/avris.it\u0027\u003C\/span\u003E . $_SERVER[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027REQUEST_URI\u0027\u003C\/span\u003E];\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (!isBrowser($_SERVER[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027HTTP_USER_AGENT\u0027\u003C\/span\u003E] ?? \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E)) {\n    http_response_code(\u003Cspan class=\u0022hljs-number\u0022\u003E301\u003C\/span\u003E);\n    header(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027Location: \u0027\u003C\/span\u003E . $url);\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Edie\u003C\/span\u003E;\n}\n\nhttp_response_code(\u003Cspan class=\u0022hljs-number\u0022\u003E404\u003C\/span\u003E);\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Eecho\u003C\/span\u003E \u003Cspan class=\u0022hljs-string\u0022\u003E\u0026lt;\u0026lt;\u0026lt;HTML\n\n\u0026lt;!DOCTYPE html\u0026gt;\n\u0026lt;html lang=\u0022en\u0022\u0026gt;\n\u0026lt;head\u0026gt;\n    \u0026lt;meta charset=\u0022UTF-8\u0022\u0026gt;\n    \u0026lt;title\u0026gt;404 \u2013 Not found\u0026lt;\/title\u0026gt;\n    \u0026lt;meta name=\u0022viewport\u0022 content=\u0022width=device-width, initial-scale=1\u0022\u0026gt;\n\n    \u0026lt;link rel=\u0022shortcut icon\u0022 href=\u0022https:\/\/avris.it\/assetic\/gfx\/favicon.png\u0022 \/\u0026gt;\n    \u0026lt;link rel=\u0022stylesheet\u0022 href=\u0022https:\/\/cdn.jsdelivr.net\/gh\/kognise\/water.css@latest\/dist\/light.min.css\u0022\u0026gt;\n\u0026lt;\/head\u0026gt;\n\u0026lt;body\u0026gt;\n \u0026lt;h1\u0026gt;\n    404 \u2013 Not found\n\u0026lt;\/h1\u0026gt;\n\u0026lt;p\u0026gt;\n    This is not a valid subdomain.\n\u0026lt;\/p\u0026gt;\n\u0026lt;p\u0026gt;\n    Did you mean \u0026lt;a href=\u0022\u003Cspan class=\u0022hljs-subst\u0022\u003E$url\u003C\/span\u003E\u0022\u0026gt;\u003Cspan class=\u0022hljs-subst\u0022\u003E$url\u003C\/span\u003E\u0026lt;\/a\u0026gt; ?\n\u0026lt;\/p\u0026gt;\n\u0026lt;hr\/\u0026gt;\n\u0026lt;p\u0026gt;\n    \u0026lt;small\u0026gt;\n        You\u0027ll be redirected there in \u0026lt;span id=\u0022countdown\u0022\u0026gt;15\u0026lt;\/span\u0026gt; seconds anyway... \ud83e\udd37\n    \u0026lt;\/small\u0026gt;\n\u0026lt;p\u0026gt;\n\u0026lt;script\u0026gt;\n    const \\$el = document.getElementById(\u0027countdown\u0027);\n    seconds = 15;\n    setInterval(_ =\u0026gt; {\n        if (seconds === 0) {\n            window.location.href = \u0027\u003Cspan class=\u0022hljs-subst\u0022\u003E$url\u003C\/span\u003E\u0027;\n            return;\n        }\n        seconds--;\n        \\$el.innerHTML = seconds;\n    }, 1000);\n\u0026lt;\/script\u0026gt;\n\u0026lt;\/body\u0026gt;\n\u0026lt;\/html\u0026gt;\nHTML;\u003C\/span\u003E\n\u003C\/code\u003E\u003C\/pre\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["apache","bots","certbot","certificate","domain","https","letsencrypt","php","subdomain","wildcard"],"hasMore":true,"image":"https:\/\/avris.it\/image\/askara-redirect_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/askara-redirect_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/askara-redirect_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u002292.011331444759\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFor quite a while my VPS was misconfigured \u2013 any HTTP requests it got but couldn\u2019t assign to a vhost, it redirected to the main website, \u003Ca href=\u0022https:\/\/avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E avris.it\u003C\/a\u003E. I didn\u2019t think it would be a big deal, until I recently found out that my post \u003Ca href=\u0022\/blog\/ungoogling.lite\u0022\u003E Ungoogling\u003C\/a\u003E is indexed by Google under \u003Ca href=\u0022https:\/\/askara.avris.it\/blog\/ungoogling\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E \u003Ca href=\u0022https:\/\/askara.avris.it\/blog\/ungoogling\u0022\u003Ehttps:\/\/askara.avris.it\/blog\/ungoogling\u003C\/a\u003E\u003C\/a\u003E\u003C\/p\u003E\n\u003Cp\u003EThis subdomain hadn\u2019t existed for a long time already, my server doesn\u2019t serve a certificate for it anymore, but it requires \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/HTTP_Strict_Transport_Security\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E HSTS\u003C\/a\u003E, so browsers end up showing users a scary error message.\u003C\/p\u003E\n\u003Cp\u003EI had to do something about it.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/askara-redirect_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/askara-redirect_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u002292.011331444759\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFor quite a while my VPS was misconfigured \u2013 any HTTP requests it got but couldn\u2019t assign to a vhost, it redirected to the main website, \u003Ca href=\u0022https:\/\/avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E avris.it\u003C\/a\u003E. I didn\u2019t think it would be a big deal, until I recently found out that my post \u003Ca href=\u0022\/blog\/ungoogling.lite\u0022\u003E Ungoogling\u003C\/a\u003E is indexed by Google under \u003Ca href=\u0022https:\/\/askara.avris.it\/blog\/ungoogling\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E \u003Ca href=\u0022https:\/\/askara.avris.it\/blog\/ungoogling\u0022\u003Ehttps:\/\/askara.avris.it\/blog\/ungoogling\u003C\/a\u003E\u003C\/a\u003E\u003C\/p\u003E\n\u003Cp\u003EThis subdomain hadn\u2019t existed for a long time already, my server doesn\u2019t serve a certificate for it anymore, but it requires \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/HTTP_Strict_Transport_Security\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E HSTS\u003C\/a\u003E, so browsers end up showing users a scary error message.\u003C\/p\u003E\n\u003Cp\u003EI had to do something about it.\u003C\/p\u003E\n\u003Ch3\u003ECertificate\u003C\/h3\u003E\n\u003Cp\u003EFirst of all, users have to see \u003Cem\u003Eanything\u003C\/em\u003E other than a security warning. I need a wildcard certificate.\u003C\/p\u003E\n\u003Cp\u003EFortunately, Let\u2019s Encrypt offers them now, and it\u2019s totally free. I just followed \u003Ca href=\u0022https:\/\/medium.com\/@saurabh6790\/generate-wildcard-ssl-certificate-using-lets-encrypt-certbot-273e432794d7\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E an instruction\u003C\/a\u003E to obtain one, and then configured Apache to serve the \u003Ccode\u003E\/www\/default\u003C\/code\u003E directory with the \u003Ccode\u003E*.avris.it\u003C\/code\u003E certificate for all requests that don\u2019t fit to any vhost.\u003C\/p\u003E\n\u003Ch3\u003ERedirect for users\u003C\/h3\u003E\n\u003Cp\u003EOnce the users can see the website, I can show them things. Ideally, just a 404 with an information that it\u2019s not a valid domain, and a suggestion where they might have wanted to go (same request string, but with the base domain). Easy.\u003C\/p\u003E\n\u003Cp\u003EBtw, I used \u003Ca href=\u0022https:\/\/watercss.netlify.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Water.css\u003C\/a\u003E, a ridiculously simple CSS framework \u2013 I just added two lines, no classes, and the page already looks way better!\u003C\/p\u003E\n\u003Ch3\u003ERedirect for bots\u003C\/h3\u003E\n\u003Cp\u003EBut that doesn\u2019t solve the root cause: bots are confused about which domains they should be using. They don\u2019t care whether my certificate is working or not, they don\u2019t understand the message I left there for the users.\u003C\/p\u003E\n\u003Cp\u003EThey need a proper \u003Ccode\u003EHTTP 301 -- Moved permanently\u003C\/code\u003E. So I had to add a simple recognition whether I\u2019m serving a bot or a user, and adjust the response for each of them.\u003C\/p\u003E\n\u003Ch3\u003ESource\u003C\/h3\u003E\n\u003Cp\u003ESo, here\u2019s what I ended up with:\u003C\/p\u003E\n\u003Ch4\u003E.htaccess\u003C\/h4\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-htaccess\u0022\u003EDirectoryIndex index.php\n\n\u0026lt;IfModule mod_rewrite.c\u0026gt;\n    RewriteEngine On\n\n    RewriteCond %{REQUEST_URI}::$1 ^(\/.+)\/(.*)::\\2$\n    RewriteRule ^(.*) - [E=BASE:%1]\n\n    RewriteCond %{ENV:REDIRECT_STATUS} ^$\n    RewriteRule ^index\\.php(\/(.*)|$) %{ENV:BASE}\/$2 [R=301,L]\n\n    RewriteCond %{REQUEST_FILENAME} -f\n    RewriteRule .? - [L]\n\n    RewriteRule .? %{ENV:BASE}\/index.php [L]\n\u0026lt;\/IfModule\u0026gt;\n\n\u0026lt;IfModule !mod_rewrite.c\u0026gt;\n    \u0026lt;IfModule mod_alias.c\u0026gt;\n        RedirectMatch 302 ^\/$ \/index.php\/\n    \u0026lt;\/IfModule\u0026gt;\n\u0026lt;\/IfModule\u0026gt;\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch4\u003Eindex.php\u003C\/h4\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\n\nfunction isBrowser($ua): bool\n{\n        if (!$ua) {\n                return false;\n        }\n        $isProbablyBot = (bool) preg_match(\u0027#bot|crawler|baiduspider|80legs|ia_archiver|voyager|curl|wget|yahoo! slurp|mediapartners-google|facebookexternalhit|twitterbot|whatsapp|php|python#i\u0027, mb_strtolower($ua));\n        $isProbablyBrowser = (bool) preg_match(\u0027#mozilla|msie|gecko|firefox|edge|opera|safari|netscape|konqueror|android#i\u0027, mb_strtolower($ua));\n\n        return $isProbablyBrowser || !$isProbablyBot;\n}\n\n$url = \u0027https:\/\/avris.it\u0027 . $_SERVER[\u0027REQUEST_URI\u0027];\n\nif (!isBrowser($_SERVER[\u0027HTTP_USER_AGENT\u0027] ?? null)) {\n    http_response_code(301);\n    header(\u0027Location: \u0027 . $url);\n    die;\n}\n\nhttp_response_code(404);\n\necho \u0026lt;\u0026lt;\u0026lt;HTML\n\n\u0026lt;!DOCTYPE html\u0026gt;\n\u0026lt;html lang=\u0022en\u0022\u0026gt;\n\u0026lt;head\u0026gt;\n    \u0026lt;meta charset=\u0022UTF-8\u0022\u0026gt;\n    \u0026lt;title\u0026gt;404 \u2013 Not found\u0026lt;\/title\u0026gt;\n    \u0026lt;meta name=\u0022viewport\u0022 content=\u0022width=device-width, initial-scale=1\u0022\u0026gt;\n\n    \u0026lt;link rel=\u0022shortcut icon\u0022 href=\u0022https:\/\/avris.it\/assetic\/gfx\/favicon.png\u0022 \/\u0026gt;\n    \u0026lt;link rel=\u0022stylesheet\u0022 href=\u0022https:\/\/cdn.jsdelivr.net\/gh\/kognise\/water.css@latest\/dist\/light.min.css\u0022\u0026gt;\n\u0026lt;\/head\u0026gt;\n\u0026lt;body\u0026gt;\n \u0026lt;h1\u0026gt;\n    404 \u2013 Not found\n\u0026lt;\/h1\u0026gt;\n\u0026lt;p\u0026gt;\n    This is not a valid subdomain.\n\u0026lt;\/p\u0026gt;\n\u0026lt;p\u0026gt;\n    Did you mean \u0026lt;a href=\u0022$url\u0022\u0026gt;$url\u0026lt;\/a\u0026gt; ?\n\u0026lt;\/p\u0026gt;\n\u0026lt;hr\/\u0026gt;\n\u0026lt;p\u0026gt;\n    \u0026lt;small\u0026gt;\n        You\u0027ll be redirected there in \u0026lt;span id=\u0022countdown\u0022\u0026gt;15\u0026lt;\/span\u0026gt; seconds anyway... \ud83e\udd37\n    \u0026lt;\/small\u0026gt;\n\u0026lt;p\u0026gt;\n\u0026lt;script\u0026gt;\n    const \\$el = document.getElementById(\u0027countdown\u0027);\n    seconds = 15;\n    setInterval(_ =\u0026gt; {\n        if (seconds === 0) {\n            window.location.href = \u0027$url\u0027;\n            return;\n        }\n        seconds--;\n        \\$el.innerHTML = seconds;\n    }, 1000);\n\u0026lt;\/script\u0026gt;\n\u0026lt;\/body\u0026gt;\n\u0026lt;\/html\u0026gt;\nHTML;\u003C\/code\u003E\u003C\/pre\u003E","words":651,"readTime":3,"lang":"en"}}},"projects\/showr":{"key":"projects\/showr","type":"article","published":true,"meta":{"createdAt":"2019-04-30T19:58:22+02:00","publishedAt":"2019-04-30T19:56:00+02:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/showr.link","displayUrl":null}],"category":"projects","subcategory":null,"slug":"showr"},"content":{"en":{"slug":"showr","title":"showr","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/showr_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022474\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAjCAYAAAD8BaggAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADqElEQVRYhdWYzW7jNhDHf0NSlu04yXqB7qEP0D5Ae+leupee9r37Du0DFEgLpHb8JVEkpwfZihJLsrMrFO0fEDASRzNDcjgflB8\/flIUEM7Q\/qwKIsP0GHDQbQzANM+ZzWbc3t7ivefh4aFmb\/GPaQyA04HBoiwpypK\/V6txtQ4ZNDQ4n8+ZTaeklHDWUnrPZJKx3x+w1jKdTlFNqIKqslqtUAXrLO\/u70kp1u\/WoqqURcF2vx80yKDQ\/ygiBuscYgx5nhNjqoWX\/rhfgio451i+f89icYOq4pzDGIexFjnu6+zmBiMyoO\/CCqWUSCmSjgpSjIjUhu33ezQpKSUQIcQIqmy3W6y1hBCIMVKWJQAigqpijCGl2KtTfvjp05AbPTNKfaK+dPxaOOHV8W7R+WxK5lyj0BhDqALb3f6c\/5Ux0vr0FtqdJHdOThVnDYigSVmvn57\/vgD9QrrxIelgKsqy8YGe2PkCY4Qk8d737nyMEWvtCGquh8uyDFVtjmab3mw2rJ825JOM2WwGwHK57BXWJ+cttGiNTqYYIzGlxrHHwpBBrs3wmg4hIMY0sWbIR3a7HcaYZiW70KzCQAIcnHqIiT8f\/kIQFos5RoT5bIZzDmtNSxGIGHwVMNYjCIoiCAhkWYY5Bk9BMMb06hTV\/nAWQmC73XJ3dzc4qxcCjxG5i66Nv8KH+oQfysi2iEydMmkFCEVBT0pOAUHJJpM6V30F3IA9WGvIMoMKJIFpdlngkLxrICmlETLQeOj3rg6s10\/dKWZE\/L9X6GvQjj9DtDsdza6j+Nsfyq+\/t4uEc2QWJhnEBDFCFbsKmef\/P34H33\/bb\/hZpG5j4uB+DkN53BjFmpojpvoZwuRCFhqMQ2\/FGMl1VB+6NpoP4V9z6msx6paNAfn5l8\/\/KYPcbrfrH3SOxWKBL0uMNWTOEWKkLH1ditzMOewPdQOZEpoSVQh1QScQQmSSZSh1v3bNXrhLuaDa7UnWQFJKX9c61hpUoSgKkLoZsMbU9Y4IVagoCw9AcSjeskDDlw0pJQ4kNNRcqtrMsjZKm+zenr1I3d+nlM7+u2jQ0GCe5yyX71AFY4TD4QAIm80G5xz393cYY\/De432FMYaqqvDe8+HDN1RVwDnXfBMR1qsVQ9nzYvVelr5ph06lZ57nRyP88WYDMucoygLVujk4HApEhBgjIQRCqAghvvSQjtbVnXryrhux\/f5wXJVunsdHf\/b9hMfHx+a93fe\/uOw6t4d\/AEsqQIGxhlTjAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/showr_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022474\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EEasily share content...\u003C\/h3\u003E\n\u003Cp\u003ENeed to send someone a nicely formatted text accessible via a simple link? Look no further. Simply add text, headers, images, tables, videos, quotes, code, etc. \u2013 and publish it with one click.\u003C\/p\u003E\n\u003Ch3\u003EAnonymously...\u003C\/h3\u003E\n\u003Cp\u003ENo need to run a blog or have a social media account \u2013 using showr you can put content on the Internet without registering, and just post the link wherever and however you want.\u003C\/p\u003E\n\u003Ch3\u003EWith a self-destruct...\u003C\/h3\u003E\n\u003Cp\u003ESometimes you want things to be more volatile. Showr lets you set a time limit and limit of unique visitors \u2013 after either of them is reached, your content will just self-destruct.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/showr_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022948\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAjCAYAAAD8BaggAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADqElEQVRYhdWYzW7jNhDHf0NSlu04yXqB7qEP0D5Ae+leupee9r37Du0DFEgLpHb8JVEkpwfZihJLsrMrFO0fEDASRzNDcjgflB8\/flIUEM7Q\/qwKIsP0GHDQbQzANM+ZzWbc3t7ivefh4aFmb\/GPaQyA04HBoiwpypK\/V6txtQ4ZNDQ4n8+ZTaeklHDWUnrPZJKx3x+w1jKdTlFNqIKqslqtUAXrLO\/u70kp1u\/WoqqURcF2vx80yKDQ\/ygiBuscYgx5nhNjqoWX\/rhfgio451i+f89icYOq4pzDGIexFjnu6+zmBiMyoO\/CCqWUSCmSjgpSjIjUhu33ezQpKSUQIcQIqmy3W6y1hBCIMVKWJQAigqpijCGl2KtTfvjp05AbPTNKfaK+dPxaOOHV8W7R+WxK5lyj0BhDqALb3f6c\/5Ux0vr0FtqdJHdOThVnDYigSVmvn57\/vgD9QrrxIelgKsqy8YGe2PkCY4Qk8d737nyMEWvtCGquh8uyDFVtjmab3mw2rJ825JOM2WwGwHK57BXWJ+cttGiNTqYYIzGlxrHHwpBBrs3wmg4hIMY0sWbIR3a7HcaYZiW70KzCQAIcnHqIiT8f\/kIQFos5RoT5bIZzDmtNSxGIGHwVMNYjCIoiCAhkWYY5Bk9BMMb06hTV\/nAWQmC73XJ3dzc4qxcCjxG5i66Nv8KH+oQfysi2iEydMmkFCEVBT0pOAUHJJpM6V30F3IA9WGvIMoMKJIFpdlngkLxrICmlETLQeOj3rg6s10\/dKWZE\/L9X6GvQjj9DtDsdza6j+Nsfyq+\/t4uEc2QWJhnEBDFCFbsKmef\/P34H33\/bb\/hZpG5j4uB+DkN53BjFmpojpvoZwuRCFhqMQ2\/FGMl1VB+6NpoP4V9z6msx6paNAfn5l8\/\/KYPcbrfrH3SOxWKBL0uMNWTOEWKkLH1ditzMOewPdQOZEpoSVQh1QScQQmSSZSh1v3bNXrhLuaDa7UnWQFJKX9c61hpUoSgKkLoZsMbU9Y4IVagoCw9AcSjeskDDlw0pJQ4kNNRcqtrMsjZKm+zenr1I3d+nlM7+u2jQ0GCe5yyX71AFY4TD4QAIm80G5xz393cYY\/De432FMYaqqvDe8+HDN1RVwDnXfBMR1qsVQ9nzYvVelr5ph06lZ57nRyP88WYDMucoygLVujk4HApEhBgjIQRCqAghvvSQjtbVnXryrhux\/f5wXJVunsdHf\/b9hMfHx+a93fe\/uOw6t4d\/AEsqQIGxhlTjAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/showr_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022948\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EEasily share content...\u003C\/h3\u003E\n\u003Cp\u003ENeed to send someone a nicely formatted text accessible via a simple link? Look no further. Simply add text, headers, images, tables, videos, quotes, code, etc. \u2013 and publish it with one click.\u003C\/p\u003E\n\u003Ch3\u003EAnonymously...\u003C\/h3\u003E\n\u003Cp\u003ENo need to run a blog or have a social media account \u2013 using showr you can put content on the Internet without registering, and just post the link wherever and however you want.\u003C\/p\u003E\n\u003Ch3\u003EWith a self-destruct...\u003C\/h3\u003E\n\u003Cp\u003ESometimes you want things to be more volatile. Showr lets you set a time limit and limit of unique visitors \u2013 after either of them is reached, your content will just self-destruct.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["clean code","content","formatted","images","link","php","self-destruct","share","symfony","text","volatile"],"hasMore":false,"image":"https:\/\/avris.it\/image\/showr_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/showr_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/showr_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022237\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EEasily share content...\u003C\/h3\u003E\n\u003Cp\u003ENeed to send someone a nicely formatted text accessible via a simple link? Look no further. Simply add text, headers, images, tables, videos, quotes, code, etc. \u2013 and publish it with one click.\u003C\/p\u003E\n\u003Ch3\u003EAnonymously...\u003C\/h3\u003E\n\u003Cp\u003ENo need to run a blog or have a social media account \u2013 using showr you can put content on the Internet without registering, and just post the link wherever and however you want.\u003C\/p\u003E\n\u003Ch3\u003EWith a self-destruct...\u003C\/h3\u003E\n\u003Cp\u003ESometimes you want things to be more volatile. Showr lets you set a time limit and limit of unique visitors \u2013 after either of them is reached, your content will just self-destruct.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/showr_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/showr_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022237\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Ch3\u003EEasily share content...\u003C\/h3\u003E\n\u003Cp\u003ENeed to send someone a nicely formatted text accessible via a simple link? Look no further. Simply add text, headers, images, tables, videos, quotes, code, etc. \u2013 and publish it with one click.\u003C\/p\u003E\n\u003Ch3\u003EAnonymously...\u003C\/h3\u003E\n\u003Cp\u003ENo need to run a blog or have a social media account \u2013 using showr you can put content on the Internet without registering, and just post the link wherever and however you want.\u003C\/p\u003E\n\u003Ch3\u003EWith a self-destruct...\u003C\/h3\u003E\n\u003Cp\u003ESometimes you want things to be more volatile. Showr lets you set a time limit and limit of unique visitors \u2013 after either of them is reached, your content will just self-destruct.\u003C\/p\u003E","words":103,"readTime":null,"lang":"en"}}},"projects\/avris-graphqlbundle":{"key":"projects\/avris-graphqlbundle","type":"article","published":true,"meta":{"createdAt":"2018-09-27T23:32:04+02:00","publishedAt":"2018-09-27T23:30:00+02:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/GraphqlBundle","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-graphqlbundle"},"content":{"en":{"slug":"avris-graphqlbundle","title":"Avris GraphqlBundle","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-graphql-bundle_small.png\u0022 alt=\u0022Avris GraphqlBundle\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHsklEQVRIiX2X268lRRXGf+tS1fty9j4XZs4AwzCIipJBiUQiCTEgCSoxmKBRoyZeHvRB44N\/gu+++WA0PuuT8QUx0Wi8JJooeEFAJIIzwDhOBmbmnLPP3mfv7qrlQ+8ZjBorXd2ddF2++r5atb6WW287E1EqQhC1QlTMnVoKQaAiuBqmSnJntVpSa4UIiEAAEUHoi4ggIkBwrUTEutbrT6IiAiqCuWMiNM0QjxAiglILEiBAKQWhH1hVe6CiUCtJlW49ASLrdlxvLwIgqMobk8u1dyFqQdSIeKOfISQ1tBb8WgdBCSoRoCGI9pMRgqoDQlcqhqAIIYqprtnoF4L039bX+ma4G6tViwhE9AsUUXTd11RxM5IqHqJQy5rgnhEikBBUDTfFRDBR3BSJSq2VWks\/gHu\/+lpRtetSqQhRK0GwubXF1ctXgAohiEPUgqpAgJmSzEh+DZAAKlDXbAmI6FoGQ1HMjGyJUdNAdMwXCxp3muRkT9RaKaWjKwWEfr\/UCsBNJ3ZZHsyA2pMWgWpeg1JMe4mzKS6aiGiRMJCKXt+kgpr11FpCRVExVJxxM2Z7uMmV2R4aSpSg8YR6hiiECF3X9gwJnL71Vv559mVEAgkwFYQAEiKxlkxJprimTBesI8auxQqo9uyoYWpkd7IlFEMk0eTEm45vcDifsWxbTty4y2oxZ7Z\/gImQXalRuf\/9D\/Dic88zzQ1B9JEFqPaiqIIIJFOyGx6akLSWC6EGmCiqhori5rga2TMuQjInSSJpw87OJh\/53MfY2d7i59\/\/Ea+cewVrNkjeR9Nkc4P7H3qAZ3\/xa7YGQ6AQET0YFVTATDDtWXMXnNwgnVNrrPUFUEQNNUfEcHNUE262rokHH3kvn\/zyJ3jhyb\/w+De+R6Jwx003E7VycDjDs\/Hpr32VJ3\/wBLdMp6xKS42OiNJLp+voSv0eykkxBZfBmNoWpNb+HApADBFHLZHMUTWSOYOUGbnz+S99lA889iCzKwc88a3HyTpkezTgzW+5mcO919ibH\/HQVz5FUqGee5W337TLbDHnqFtRakepLWgluWEmuCvJhZQMl9EUWXVQKgH9HkERSagY4omsziAlRinxyc88zAceexCAp3\/1DImGycaEjVHDweUFp9\/9Tt7\/6P34oOE3X\/8Ou8c20WjZ2w+6LlGiBxRSUQ1SdtyFlBTPgstwArmDAhIKoQgK4qAJUyeZk1Pi7rvfxIc\/\/r7rKWH\/4owbThzn5OldTp85xal7bmd8bEpE8MqPf8l4mCl1g0HTkZsKUqnR0nUroraogWpgbuTG8SQ4k03oChHSg4k+ugzHNaE4sj4AP\/PFR66fzAAf\/MLD\/GeJCM7\/8ilWl17j+G03sNzLtIdXmWqLWEUsqLWldkuEDjXBXEk5402D62RCKvR5BsVDSRhZnBxGViOj3HP3rZw8ufNfAP691FJ48Se\/4+CFFxmMlcHmiON33kK3N2b\/gqCyQmhBMlESIrWPsORYzlgzxCdbE0oB1gwZgoczEGOAMsBoqnDfe97yf8HMLu3x3A9\/zer1SwwbQKFdzJm\/3uKyYrTZUNvSnz0GKglZy6XuWB6gzRC\/cWdI1wW1QA1FQ1GMFELCGITSFOGO24\/9TyCL\/Tl\/\/dUznP\/982RpGQ5qnzoqRAt1adAoeahEUkxiLVNCTbGU0dwgqYE8xO\/abZgftayWhVVXKaVSakVCkKhYEUyEY9uj6yAOZwvOvXCes396ifPP\/p0mWkZeiFRwKlkroQEWsOpzpaQgeeAmmAmaEpIHkAxyhtyAD\/BHt\/\/B4VFlNmvZP6zMuuBgCfMVHLWw6oTSwbe\/+V0WhwuuvrbP4eV9cikkConKQCtD6xinYDqorEYVJkEKYWC9zZDSoh5YNqwGbWsYE9qloYPE3pURLQPkwTPHYjpIHB31VsktE+JUnJBEwamamW7vcPnKHiF9wnU1jt2ww3x2SDZj99g2SSplvs8kw\/FxcNN24a53nuJw7wIvX7jKbLGg5Jatm0\/yi5\/+lrMXlkwHld3dCcs2+M1zh8jG1k5oyogalhLeDBA1PDd4ylhquO2Wk5w8dRopLYFyfGvCfBXcuLvL4mBOVLjj9tvYHA95+rd\/4MadKdvjhlSXvO2ut7K4eBYtC5aXzrE17rjj7hFXL8y4dGGfyRBcOo7f9zDzP\/8MufezX4pmY0pXYTCe4KlB1RFzkidEjGFOnDl1gvlRS+kKt5\/Y5vzFK+zvH3Jic8w4ZxbzJYlgeXjE9qgh5kdsD5ydyYBGO+rhHtLO2RxV7jyzRTOGl556iclQaLxjeOIEebqJF4zZfAGWWB0cIHIIaO+FojfgKsqFC+eRUqFUnoqgWyygFrIZFhClYrWQ6L3xKGUmyZm4s9kkRklwLYwyzI5a7n3HlFevKPVyx3QIk6v\/4K0ffBd+5dzfWLUt6plaKt4M4Y1\/CATwlFBVJIKogUYgEWitSNddN\/i1XeJiXL34MoM8wEXY3Jhyw3QLj8p8vs\/2dMIfn3f+eW7Ixb0jzr5+xDAHja74UPcqcuzNd0YthW61orZL0niChFBWR2hq+nRQOzwPAHrPDWgNFEHimofuvbhGIFRMBFfBBZRKNkM16LoWobA7cTYHxuVFb3Mbh\/tOw78Ak6pNn\/7nMikAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-graphql-bundle_small.png\u0022 alt=\u0022Avris GraphqlBundle\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003ESimplify Graphql configuration using plain methods with typehints and annotations.\u003C\/p\u003E\n\u003Cp\u003EThis bundle is a wrapper on \u003Ccode\u003Ewebonyx\/graphql-php\u003C\/code\u003E.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-graphql-bundle_big.png\u0022 alt=\u0022Avris GraphqlBundle\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHsklEQVRIiX2X268lRRXGf+tS1fty9j4XZs4AwzCIipJBiUQiCTEgCSoxmKBRoyZeHvRB44N\/gu+++WA0PuuT8QUx0Wi8JJooeEFAJIIzwDhOBmbmnLPP3mfv7qrlQ+8ZjBorXd2ddF2++r5atb6WW287E1EqQhC1QlTMnVoKQaAiuBqmSnJntVpSa4UIiEAAEUHoi4ggIkBwrUTEutbrT6IiAiqCuWMiNM0QjxAiglILEiBAKQWhH1hVe6CiUCtJlW49ASLrdlxvLwIgqMobk8u1dyFqQdSIeKOfISQ1tBb8WgdBCSoRoCGI9pMRgqoDQlcqhqAIIYqprtnoF4L039bX+ma4G6tViwhE9AsUUXTd11RxM5IqHqJQy5rgnhEikBBUDTfFRDBR3BSJSq2VWks\/gHu\/+lpRtetSqQhRK0GwubXF1ctXgAohiEPUgqpAgJmSzEh+DZAAKlDXbAmI6FoGQ1HMjGyJUdNAdMwXCxp3muRkT9RaKaWjKwWEfr\/UCsBNJ3ZZHsyA2pMWgWpeg1JMe4mzKS6aiGiRMJCKXt+kgpr11FpCRVExVJxxM2Z7uMmV2R4aSpSg8YR6hiiECF3X9gwJnL71Vv559mVEAgkwFYQAEiKxlkxJprimTBesI8auxQqo9uyoYWpkd7IlFEMk0eTEm45vcDifsWxbTty4y2oxZ7Z\/gImQXalRuf\/9D\/Dic88zzQ1B9JEFqPaiqIIIJFOyGx6akLSWC6EGmCiqhori5rga2TMuQjInSSJpw87OJh\/53MfY2d7i59\/\/Ea+cewVrNkjeR9Nkc4P7H3qAZ3\/xa7YGQ6AQET0YFVTATDDtWXMXnNwgnVNrrPUFUEQNNUfEcHNUE262rokHH3kvn\/zyJ3jhyb\/w+De+R6Jwx003E7VycDjDs\/Hpr32VJ3\/wBLdMp6xKS42OiNJLp+voSv0eykkxBZfBmNoWpNb+HApADBFHLZHMUTWSOYOUGbnz+S99lA889iCzKwc88a3HyTpkezTgzW+5mcO919ibH\/HQVz5FUqGee5W337TLbDHnqFtRakepLWgluWEmuCvJhZQMl9EUWXVQKgH9HkERSagY4omsziAlRinxyc88zAceexCAp3\/1DImGycaEjVHDweUFp9\/9Tt7\/6P34oOE3X\/8Ou8c20WjZ2w+6LlGiBxRSUQ1SdtyFlBTPgstwArmDAhIKoQgK4qAJUyeZk1Pi7rvfxIc\/\/r7rKWH\/4owbThzn5OldTp85xal7bmd8bEpE8MqPf8l4mCl1g0HTkZsKUqnR0nUroraogWpgbuTG8SQ4k03oChHSg4k+ugzHNaE4sj4AP\/PFR66fzAAf\/MLD\/GeJCM7\/8ilWl17j+G03sNzLtIdXmWqLWEUsqLWldkuEDjXBXEk5402D62RCKvR5BsVDSRhZnBxGViOj3HP3rZw8ufNfAP691FJ48Se\/4+CFFxmMlcHmiON33kK3N2b\/gqCyQmhBMlESIrWPsORYzlgzxCdbE0oB1gwZgoczEGOAMsBoqnDfe97yf8HMLu3x3A9\/zer1SwwbQKFdzJm\/3uKyYrTZUNvSnz0GKglZy6XuWB6gzRC\/cWdI1wW1QA1FQ1GMFELCGITSFOGO24\/9TyCL\/Tl\/\/dUznP\/982RpGQ5qnzoqRAt1adAoeahEUkxiLVNCTbGU0dwgqYE8xO\/abZgftayWhVVXKaVSakVCkKhYEUyEY9uj6yAOZwvOvXCes396ifPP\/p0mWkZeiFRwKlkroQEWsOpzpaQgeeAmmAmaEpIHkAxyhtyAD\/BHt\/\/B4VFlNmvZP6zMuuBgCfMVHLWw6oTSwbe\/+V0WhwuuvrbP4eV9cikkConKQCtD6xinYDqorEYVJkEKYWC9zZDSoh5YNqwGbWsYE9qloYPE3pURLQPkwTPHYjpIHB31VsktE+JUnJBEwamamW7vcPnKHiF9wnU1jt2ww3x2SDZj99g2SSplvs8kw\/FxcNN24a53nuJw7wIvX7jKbLGg5Jatm0\/yi5\/+lrMXlkwHld3dCcs2+M1zh8jG1k5oyogalhLeDBA1PDd4ylhquO2Wk5w8dRopLYFyfGvCfBXcuLvL4mBOVLjj9tvYHA95+rd\/4MadKdvjhlSXvO2ut7K4eBYtC5aXzrE17rjj7hFXL8y4dGGfyRBcOo7f9zDzP\/8MufezX4pmY0pXYTCe4KlB1RFzkidEjGFOnDl1gvlRS+kKt5\/Y5vzFK+zvH3Jic8w4ZxbzJYlgeXjE9qgh5kdsD5ydyYBGO+rhHtLO2RxV7jyzRTOGl556iclQaLxjeOIEebqJF4zZfAGWWB0cIHIIaO+FojfgKsqFC+eRUqFUnoqgWyygFrIZFhClYrWQ6L3xKGUmyZm4s9kkRklwLYwyzI5a7n3HlFevKPVyx3QIk6v\/4K0ffBd+5dzfWLUt6plaKt4M4Y1\/CATwlFBVJIKogUYgEWitSNddN\/i1XeJiXL34MoM8wEXY3Jhyw3QLj8p8vs\/2dMIfn3f+eW7Ixb0jzr5+xDAHja74UPcqcuzNd0YthW61orZL0niChFBWR2hq+nRQOzwPAHrPDWgNFEHimofuvbhGIFRMBFfBBZRKNkM16LoWobA7cTYHxuVFb3Mbh\/tOw78Ak6pNn\/7nMikAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-graphql-bundle_big.png\u0022 alt=\u0022Avris GraphqlBundle\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003ESimplify Graphql configuration using plain methods with typehints and annotations.\u003C\/p\u003E\n\u003Cp\u003EThis bundle is a wrapper on \u003Ccode\u003Ewebonyx\/graphql-php\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003EDisclaimer\u003C\/h3\u003E\n\u003Cp\u003EI\u2019m new to GraphQL, currently writing my first project using it.\nI\u2019m sure that many things are missing from the bundle and many assumptions I made are wrong,\nso please be understanding, and if possible help out with a pull request.\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs bash border\u0022\u003E$ composer require avris\/graphql-bundle\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EThen add a route:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs yaml border\u0022\u003E\u003Cspan class=\u0022hljs-attr\u0022\u003Eavris_graphql_main:\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-attr\u0022\u003Epath:\u003C\/span\u003E \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/graph\u0027\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-attr\u0022\u003Econtroller:\u003C\/span\u003E \u003Cspan class=\u0022hljs-string\u0022\u003EAvris\\GraphqlBundle\\Controller\\GraphqlController::main\u003C\/span\u003E\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EExample\u003C\/h3\u003E\n\u003Cp\u003ELet\u2019s say you have the following type in your app:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EEntity\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Efinal\u003C\/span\u003E \u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EUser\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ properties, constructor, etc.\u003C\/span\u003E\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Estatic\u003C\/span\u003E $type = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Efinal\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Estatic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Etype\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003EObjectType\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Estatic\u003C\/span\u003E::$type ?: \u003Cspan class=\u0022hljs-keyword\u0022\u003Estatic\u003C\/span\u003E::$type = \u003Cspan class=\u0022hljs-keyword\u0022\u003Estatic\u003C\/span\u003E::buildType();\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Estatic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EbuildType\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003EObjectType\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E ObjectType([\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027name\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027User\u0027\u003C\/span\u003E,\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027fields\u0027\u003C\/span\u003E =\u0026gt; [\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027id\u0027\u003C\/span\u003E =\u0026gt; [\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027type\u0027\u003C\/span\u003E =\u0026gt; Type::id(),\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027resolve\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E(User $user)\u003C\/span\u003E \u003C\/span\u003E{\n                        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $user-\u0026gt;getId();\n                    },\n                ],\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027email\u0027\u003C\/span\u003E =\u0026gt; [\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027type\u0027\u003C\/span\u003E =\u0026gt; Type::string(),\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027resolve\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E(User $user)\u003C\/span\u003E \u003C\/span\u003E{\n                        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $user-\u0026gt;getEmail();\n                    },\n                ],\n            ],\n        ]);\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetId\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Estring\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;id;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetEmail\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Estring\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;email;\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAnd the following GraphQL schema:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Schema([\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027query\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E ObjectType([\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027name\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027Query\u0027\u003C\/span\u003E,\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027fields\u0027\u003C\/span\u003E =\u0026gt; [\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027hello\u0027\u003C\/span\u003E =\u0026gt; [\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027type\u0027\u003C\/span\u003E =\u0026gt; Type::string(),\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027resolve\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E \u003C\/span\u003E{\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027GraphQL API\u0027\u003C\/span\u003E;\n                }\n            ],\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027user\u0027\u003C\/span\u003E =\u0026gt; [\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027type\u0027\u003C\/span\u003E =\u0026gt; User::type(),\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027args\u0027\u003C\/span\u003E =\u0026gt; [\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027id\u0027\u003C\/span\u003E =\u0026gt; [\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027type\u0027\u003C\/span\u003E =\u0026gt; Type::nonNull(Type::id())],\n                ],\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027resolve\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;accessControl-\u0026gt;guard(\u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E($root, $args)\u003C\/span\u003E \u003C\/span\u003E{\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;repository-\u0026gt;find($args[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027id\u0027\u003C\/span\u003E]);\n                }, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027ROLE_ADMIN\u0027\u003C\/span\u003E),\n            ],\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027currentUser\u0027\u003C\/span\u003E =\u0026gt; [\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027type\u0027\u003C\/span\u003E =\u0026gt; User::type(),\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027resolve\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E($root, $args)\u003C\/span\u003E \u003C\/span\u003E{\n                    $user = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;tokenStorage-\u0026gt;getToken()-\u0026gt;getUser();\n\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $user \u003Cspan class=\u0022hljs-keyword\u0022\u003Einstanceof\u003C\/span\u003E User ? $user : \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E;\n                },\n            ],\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027jwt\u0027\u003C\/span\u003E =\u0026gt; [\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027type\u0027\u003C\/span\u003E =\u0026gt; Type::string(),\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027args\u0027\u003C\/span\u003E =\u0026gt; [\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027login\u0027\u003C\/span\u003E =\u0026gt; Type::nonNull(Type::string()),\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027password\u0027\u003C\/span\u003E =\u0026gt; Type::nonNull(Type::string()),\n                ],\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027resolve\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E($root, $args)\u003C\/span\u003E \u003C\/span\u003E{\n                    $user = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;repository-\u0026gt;findOneBy([\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027email\u0027\u003C\/span\u003E =\u0026gt; $args[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027login\u0027\u003C\/span\u003E]]);\n\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (!$user || !\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;encoder-\u0026gt;isPasswordValid($user, $args[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027password\u0027\u003C\/span\u003E])) {\n                        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E;\n                    }\n\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E (string) \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;jwtManager-\u0026gt;issue($user);\n                },\n            ]\n        ],\n    ]),\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027mutation\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E ObjectType([\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027name\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027Mutation\u0027\u003C\/span\u003E,\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027fields\u0027\u003C\/span\u003E =\u0026gt; [\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027userRegistered\u0027\u003C\/span\u003E =\u0026gt; [\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027type\u0027\u003C\/span\u003E =\u0026gt; User::type(),\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027args\u0027\u003C\/span\u003E =\u0026gt; [\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027email\u0027\u003C\/span\u003E =\u0026gt; Type::nonNull(Type::string()),\n                    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027password\u0027\u003C\/span\u003E =\u0026gt; Type::nonNull(Type::string()),\n                ],\n                \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027resolve\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E($root, $args)\u003C\/span\u003E \u003C\/span\u003E{\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;eventDispatcher-\u0026gt;dispatch(\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E UserRegisteredEvent($args[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027email\u0027\u003C\/span\u003E], $args[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027password\u0027\u003C\/span\u003E]));\n                },\n            ]\n        ],\n    ]),\n]);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EUsing this bundle, you can rewrite the entity\/type like this:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EEntity\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Euse\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EAvris\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EGraphqlBundle\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EAnnotation\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Eas\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EGraphql\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/**\n * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Type\n *\/\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Efinal\u003C\/span\u003E \u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EUser\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ properties, constructor, etc.\u003C\/span\u003E\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/**\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Query\n     *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetId\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Estring\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;id;\n    }\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/**\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Query\n     *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetEmail\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Estring\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;email;\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAnd your schema\/controllers like this:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EController\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Euse\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EAvris\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EGraphqlBundle\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EAnnotation\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Eas\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EGraphql\u003C\/span\u003E;\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Euse\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003ESymfony\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EBundle\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EFrameworkBundle\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EController\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EAbstractController\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Efinal\u003C\/span\u003E \u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EHomeController\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EAbstractController\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/**\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Query\n     *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Ehello\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Estring\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027GraphQL API\u0027\u003C\/span\u003E;\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eand:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EController\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Euse\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EAvris\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EGraphqlBundle\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EAnnotation\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Eas\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EGraphql\u003C\/span\u003E;\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Euse\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EEntity\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EUser\u003C\/span\u003E;\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Euse\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EEvents\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EUserRegistered\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Efinal\u003C\/span\u003E \u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EUserController\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EBaseController\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ dependecies...\u003C\/span\u003E\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/**\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Query\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Security(\u0022ROLE_ADMIN\u0022)\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\ParamType(\u0022id\u0022, var=\u0022id\u0022)\n     *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Euser\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E(string $id)\u003C\/span\u003E: ?\u003Cspan class=\u0022hljs-title\u0022\u003EUser\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;repository-\u0026gt;find($id);\n    }\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/**\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Query\n     *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EcurrentUser\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: ?\u003Cspan class=\u0022hljs-title\u0022\u003EUser\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $user = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;tokenStorage-\u0026gt;getToken()-\u0026gt;getUser();\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $user \u003Cspan class=\u0022hljs-keyword\u0022\u003Einstanceof\u003C\/span\u003E User ? $user : \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E;\n    }\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/**\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Query\n     *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Ejwt\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E(string $login, string $password)\u003C\/span\u003E: ?\u003Cspan class=\u0022hljs-title\u0022\u003Estring\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $user = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;repository-\u0026gt;findOneBy([\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027email\u0027\u003C\/span\u003E =\u0026gt; $login]);\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (!$user || !\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;encoder-\u0026gt;isPasswordValid($user, $password)) {\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E;\n        }\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E (string) \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;jwtManager-\u0026gt;issue($user);\n    }\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/**\n     * \u003Cspan class=\u0022hljs-doctag\u0022\u003E@Graphql\u003C\/span\u003E\\Query(\u0022mutation\u0022)\n     *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EuserRegistered\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E(string $email, string $password)\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003EUser\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;eventDispatcher-\u0026gt;dispatch(\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E UserRegisteredEvent($email, $password));\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EThe bundle will parse all the classes in \u003Ccode\u003Esrc\/Controller\u003C\/code\u003E and \u003Ccode\u003Esrc\/Entity\u003C\/code\u003E\n(that list can be configured in the config key \u003Ccode\u003Eavris_graphql.load\u003C\/code\u003E)\nlooking for the \u003Ccode\u003EGraphql\u003C\/code\u003E annotations.\u003C\/p\u003E\n\u003Cp\u003EAll parameters of a query-method, as well as its return type have to be typehinted.\nSupported built-in types are \u003Ccode\u003Estring\u003C\/code\u003E, \u003Ccode\u003Ebool\u003C\/code\u003E, \u003Ccode\u003Eint\u003C\/code\u003E and \u003Ccode\u003Efloat\u003C\/code\u003E.\nYou can also typehint any type you registered with \u003Ccode\u003E@Graphql\\Type\u003C\/code\u003E.\u003C\/p\u003E\n\u003Cp\u003EAdditionally, you can use \u003Ccode\u003Eid\u003C\/code\u003E, list (\u003Ccode\u003Eint[]\u003C\/code\u003E) and union (\u003Ccode\u003Eint|float\u003C\/code\u003E),\nbut for that you need an \u003Ccode\u003E@Graph\\ParamType\u003C\/code\u003E or \u003Ccode\u003E@Graph\\ReturnType\u003C\/code\u003E annotation for that.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["bundle","graphql","php","symfony"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-graphql-bundle_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-graphql-bundle_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-graphql-bundle_mini.png\u0022 alt=\u0022Avris GraphqlBundle\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003ESimplify Graphql configuration using plain methods with typehints and annotations.\u003C\/p\u003E\n\u003Cp\u003EThis bundle is a wrapper on \u003Ccode\u003Ewebonyx\/graphql-php\u003C\/code\u003E.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-graphql-bundle_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-graphql-bundle_mini.png\u0022 alt=\u0022Avris GraphqlBundle\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003ESimplify Graphql configuration using plain methods with typehints and annotations.\u003C\/p\u003E\n\u003Cp\u003EThis bundle is a wrapper on \u003Ccode\u003Ewebonyx\/graphql-php\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003EDisclaimer\u003C\/h3\u003E\n\u003Cp\u003EI\u2019m new to GraphQL, currently writing my first project using it.\nI\u2019m sure that many things are missing from the bundle and many assumptions I made are wrong,\nso please be understanding, and if possible help out with a pull request.\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003E$ composer require avris\/graphql-bundle\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EThen add a route:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-yaml\u0022\u003Eavris_graphql_main:\n    path: \u0027\/graph\u0027\n    controller: Avris\\GraphqlBundle\\Controller\\GraphqlController::main\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EExample\u003C\/h3\u003E\n\u003Cp\u003ELet\u2019s say you have the following type in your app:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\n\nnamespace App\\Entity;\n\nfinal class User\n{\n    \/\/ properties, constructor, etc.\n\n    private static $type = null;\n\n    final public static function type(): ObjectType\n    {\n        return static::$type ?: static::$type = static::buildType();\n    }\n\n    private static function buildType(): ObjectType\n    {\n        return new ObjectType([\n            \u0027name\u0027 =\u0026gt; \u0027User\u0027,\n            \u0027fields\u0027 =\u0026gt; [\n                \u0027id\u0027 =\u0026gt; [\n                    \u0027type\u0027 =\u0026gt; Type::id(),\n                    \u0027resolve\u0027 =\u0026gt; function (User $user) {\n                        return $user-\u0026gt;getId();\n                    },\n                ],\n                \u0027email\u0027 =\u0026gt; [\n                    \u0027type\u0027 =\u0026gt; Type::string(),\n                    \u0027resolve\u0027 =\u0026gt; function (User $user) {\n                        return $user-\u0026gt;getEmail();\n                    },\n                ],\n            ],\n        ]);\n    }\n\n    public function getId(): string\n    {\n        return $this-\u0026gt;id;\n    }\n\n    public function getEmail(): string\n    {\n        return $this-\u0026gt;email;\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAnd the following GraphQL schema:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003Enew Schema([\n    \u0027query\u0027 =\u0026gt; new ObjectType([\n        \u0027name\u0027 =\u0026gt; \u0027Query\u0027,\n        \u0027fields\u0027 =\u0026gt; [\n            \u0027hello\u0027 =\u0026gt; [\n                \u0027type\u0027 =\u0026gt; Type::string(),\n                \u0027resolve\u0027 =\u0026gt; function () {\n                    return \u0027GraphQL API\u0027;\n                }\n            ],\n            \u0027user\u0027 =\u0026gt; [\n                \u0027type\u0027 =\u0026gt; User::type(),\n                \u0027args\u0027 =\u0026gt; [\n                    \u0027id\u0027 =\u0026gt; [\u0027type\u0027 =\u0026gt; Type::nonNull(Type::id())],\n                ],\n                \u0027resolve\u0027 =\u0026gt; $this-\u0026gt;accessControl-\u0026gt;guard(function ($root, $args) {\n                    return $this-\u0026gt;repository-\u0026gt;find($args[\u0027id\u0027]);\n                }, \u0027ROLE_ADMIN\u0027),\n            ],\n            \u0027currentUser\u0027 =\u0026gt; [\n                \u0027type\u0027 =\u0026gt; User::type(),\n                \u0027resolve\u0027 =\u0026gt; function ($root, $args) {\n                    $user = $this-\u0026gt;tokenStorage-\u0026gt;getToken()-\u0026gt;getUser();\n\n                    return $user instanceof User ? $user : null;\n                },\n            ],\n            \u0027jwt\u0027 =\u0026gt; [\n                \u0027type\u0027 =\u0026gt; Type::string(),\n                \u0027args\u0027 =\u0026gt; [\n                    \u0027login\u0027 =\u0026gt; Type::nonNull(Type::string()),\n                    \u0027password\u0027 =\u0026gt; Type::nonNull(Type::string()),\n                ],\n                \u0027resolve\u0027 =\u0026gt; function ($root, $args) {\n                    $user = $this-\u0026gt;repository-\u0026gt;findOneBy([\u0027email\u0027 =\u0026gt; $args[\u0027login\u0027]]);\n\n                    if (!$user || !$this-\u0026gt;encoder-\u0026gt;isPasswordValid($user, $args[\u0027password\u0027])) {\n                        return null;\n                    }\n\n                    return (string) $this-\u0026gt;jwtManager-\u0026gt;issue($user);\n                },\n            ]\n        ],\n    ]),\n    \u0027mutation\u0027 =\u0026gt; new ObjectType([\n        \u0027name\u0027 =\u0026gt; \u0027Mutation\u0027,\n        \u0027fields\u0027 =\u0026gt; [\n            \u0027userRegistered\u0027 =\u0026gt; [\n                \u0027type\u0027 =\u0026gt; User::type(),\n                \u0027args\u0027 =\u0026gt; [\n                    \u0027email\u0027 =\u0026gt; Type::nonNull(Type::string()),\n                    \u0027password\u0027 =\u0026gt; Type::nonNull(Type::string()),\n                ],\n                \u0027resolve\u0027 =\u0026gt; function ($root, $args) {\n                    return $this-\u0026gt;eventDispatcher-\u0026gt;dispatch(new UserRegisteredEvent($args[\u0027email\u0027], $args[\u0027password\u0027]));\n                },\n            ]\n        ],\n    ]),\n]);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EUsing this bundle, you can rewrite the entity\/type like this:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\n\nnamespace App\\Entity;\n\nuse Avris\\GraphqlBundle\\Annotation as Graphql;\n\n\/**\n * @Graphql\\Type\n *\/\nfinal class User\n{\n    \/\/ properties, constructor, etc.\n\n    \/**\n     * @Graphql\\Query\n     *\/\n    public function getId(): string\n    {\n        return $this-\u0026gt;id;\n    }\n\n    \/**\n     * @Graphql\\Query\n     *\/\n    public function getEmail(): string\n    {\n        return $this-\u0026gt;email;\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAnd your schema\/controllers like this:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\n\nnamespace App\\Controller;\n\nuse Avris\\GraphqlBundle\\Annotation as Graphql;\nuse Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController;\n\nfinal class HomeController extends AbstractController\n{\n    \/**\n     * @Graphql\\Query\n     *\/\n    public function hello(): string\n    {\n        return \u0027GraphQL API\u0027;\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eand:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\n\nnamespace App\\Controller;\n\nuse Avris\\GraphqlBundle\\Annotation as Graphql;\nuse App\\Entity\\User;\nuse App\\Events\\UserRegistered;\n\nfinal class UserController extends BaseController\n{\n    \/\/ dependecies...\n\n    \/**\n     * @Graphql\\Query\n     * @Graphql\\Security(\u0022ROLE_ADMIN\u0022)\n     * @Graphql\\ParamType(\u0022id\u0022, var=\u0022id\u0022)\n     *\/\n    public function user(string $id): ?User\n    {\n        return $this-\u0026gt;repository-\u0026gt;find($id);\n    }\n\n    \/**\n     * @Graphql\\Query\n     *\/\n    public function currentUser(): ?User\n    {\n        $user = $this-\u0026gt;tokenStorage-\u0026gt;getToken()-\u0026gt;getUser();\n\n        return $user instanceof User ? $user : null;\n    }\n\n    \/**\n     * @Graphql\\Query\n     *\/\n    public function jwt(string $login, string $password): ?string\n    {\n        $user = $this-\u0026gt;repository-\u0026gt;findOneBy([\u0027email\u0027 =\u0026gt; $login]);\n\n        if (!$user || !$this-\u0026gt;encoder-\u0026gt;isPasswordValid($user, $password)) {\n            return null;\n        }\n\n        return (string) $this-\u0026gt;jwtManager-\u0026gt;issue($user);\n    }\n\n    \/**\n     * @Graphql\\Query(\u0022mutation\u0022)\n     *\/\n    public function userRegistered(string $email, string $password): User\n    {\n        return $this-\u0026gt;eventDispatcher-\u0026gt;dispatch(new UserRegisteredEvent($email, $password));\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EThe bundle will parse all the classes in \u003Ccode\u003Esrc\/Controller\u003C\/code\u003E and \u003Ccode\u003Esrc\/Entity\u003C\/code\u003E\n(that list can be configured in the config key \u003Ccode\u003Eavris_graphql.load\u003C\/code\u003E)\nlooking for the \u003Ccode\u003EGraphql\u003C\/code\u003E annotations.\u003C\/p\u003E\n\u003Cp\u003EAll parameters of a query-method, as well as its return type have to be typehinted.\nSupported built-in types are \u003Ccode\u003Estring\u003C\/code\u003E, \u003Ccode\u003Ebool\u003C\/code\u003E, \u003Ccode\u003Eint\u003C\/code\u003E and \u003Ccode\u003Efloat\u003C\/code\u003E.\nYou can also typehint any type you registered with \u003Ccode\u003E@Graphql\\Type\u003C\/code\u003E.\u003C\/p\u003E\n\u003Cp\u003EAdditionally, you can use \u003Ccode\u003Eid\u003C\/code\u003E, list (\u003Ccode\u003Eint[]\u003C\/code\u003E) and union (\u003Ccode\u003Eint|float\u003C\/code\u003E),\nbut for that you need an \u003Ccode\u003E@Graph\\ParamType\u003C\/code\u003E or \u003Ccode\u003E@Graph\\ReturnType\u003C\/code\u003E annotation for that.\u003C\/p\u003E","words":680,"readTime":3,"lang":"en"}}},"blog\/technology\/how-and-why-i-ve-built-and-killed-a-php-framework":{"key":"blog\/technology\/how-and-why-i-ve-built-and-killed-a-php-framework","type":"article","published":true,"meta":{"createdAt":"2018-09-22T14:26:34+02:00","publishedAt":"2018-09-22T14:24:00+02:00","group":null,"category":"blog","subcategory":"technology","slug":"how-and-why-i-ve-built-and-killed-a-php-framework"},"content":{"en":{"slug":"how-and-why-i-ve-built-and-killed-a-php-framework","title":"How and why I\u0027ve built and killed a PHP framework","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus-rip_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022235.46694648478\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEeElEQVRIiZ2WvW8UVxDAf\/P27d7tnT\/uDP4I4AQsDMSJkLBS0EWKRJEqFTQpQhPlP6ADKRUl\/4ClpIuUzojIKYmUpLBpEqBIMAihGNsnWyf77Fvf7r43KfZ8xjbYSkZavX1fM7+ZN292xTmnvEOSJCFNU0AQAREAwXuPtZY0TYnjmHK5\/Nb93jlar5aJqzHtPCGqDSEixHH8LpPIzNxTVRGk6HaNAm+M9Qa16KKHfdg3pMXS\/fP6xpz2+qoKqoVqAbuxleJ7EF2oLlDRyO4IRgSvigh4LSi0Byh7EF0Dh4APQqhSvBZ9I2Brsd0D2vVgL0w9xZfG63x8ts5qMyEuW35+9ArV7nzXkIgQlwLCwOC8YkzhSruT00lzvO86ql3du051wYyAHeu37HhIHVRCqFjIFUIDzUQphwHlyPDRmX58lvPBcJXmZpvps3UazW1UYXPHs9LKUZTQCrE1hBbiUHj57Cluu8FgxbK4uMjSapNzU1c5e\/EKcWTInMdAEXkUufX9I7XWIAjbqSPJHCKCESF1ijWGi8MWgzJeC3mvPyDpOKRUxncSfnmZ8dd6hndKaA3lKOgdWXX9D778bIqFhQVGR0eZmpqiWq3y4MEDXjR2WK99QlQqMTFYRC00ILe+m1cFktzjFTwgKhgjtDs57dTTzhznx\/qpV0IAIhuw3GyTpI6OUzLnEDFE1hAaQ5I5tteX+fpqH8l2C2MM4+PjjI6OIiLkec7c3Bw\/PnFEwxfwqnivvH6+iL3Qn\/L3WoYl4MMzg9Rjw\/O1jBdrCZ3MYVQJcDxbaqKq2CCgFAaUQkOae5bXWySdlCAwlKOQWl+ZkVqVK+f7mbo0ydraGiMjI7TbbZxzdDodKpUKfX19fHrBs9DYYfWfFRqrK2xvb2F\/erQMUiRlq7nJxJk67586wdhQhdwrae7ZyTxOwfvd2yNkztHJHOdHqxgprrL3RZIbhFO1iEajwZ07d5ienmZkZISJiQlu377NvXv3CMOQwVLKgM0x9TL1eAQ4if3h2y96tUFEUO226F4d+h\/y5PGfjI2Nce3aNdI0pV6vc\/nyZa5fv87k5CSNRoNzYwN889XniHTLhir2KKXGGJ4vLlKv1wms5eSJE3vAb1z1g6KqJEkCwI0bN\/atv3nzJqqKcw7n\/aG9RwIBnD59mizL8N7vr67A5uYmeZ5TLpepVCq9Pd57BgYGjtTrvT9czgFzHJAxgogQBAHNZpM0zXpRybKM3HnEloqKfuA5SqIo4m1ExwJpN5nzPMcYQ6u1WXgH2LgfjfrYTt1xat4q\/i1HdixQlmUkSZs0TXv5k2UZAJIn6M4G2VaT10tL3T8Dup+Cd\/5EFHtFaCcJzuX\/DSiKIuI4Jq5UEBGGhoYolUpdwxAYQxAE2NDSarVIkoT5+Xnm5uZYXl5mY2ODlZUVtra2AJidne2te\/L4Mffvz+6zd2xS7344vXP0HUjUIAiw1uK9JwgiEo0YKpcxxnD37l1mZmYYHh7uRW5gYICHDx9Sq9XI8yIyv\/72+z6d\/wKYTk2khRKRbgAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/micrus-rip_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022235.46694648478\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThe PHP ecosystem is full of frameworks: \u003Ca href=\u0022https:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Symfony\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/laravel.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Laravel\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/www.yiiframework.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Yii\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/framework.zend.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Zend\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/phalconphp.com\/en\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Phalcon\u003C\/a\u003E, and so many, many, many more... All of them built by professionals and supported by big communities. So why on earth would a junior developer, who has just started his first job, try his hand in building yet another one?\u003C\/p\u003E\n\u003Cp\u003EWell, here\u2019s why:\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus-rip_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022470.93389296957\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEeElEQVRIiZ2WvW8UVxDAf\/P27d7tnT\/uDP4I4AQsDMSJkLBS0EWKRJEqFTQpQhPlP6ADKRUl\/4ClpIuUzojIKYmUpLBpEqBIMAihGNsnWyf77Fvf7r43KfZ8xjbYSkZavX1fM7+ZN292xTmnvEOSJCFNU0AQAREAwXuPtZY0TYnjmHK5\/Nb93jlar5aJqzHtPCGqDSEixHH8LpPIzNxTVRGk6HaNAm+M9Qa16KKHfdg3pMXS\/fP6xpz2+qoKqoVqAbuxleJ7EF2oLlDRyO4IRgSvigh4LSi0Byh7EF0Dh4APQqhSvBZ9I2Brsd0D2vVgL0w9xZfG63x8ts5qMyEuW35+9ArV7nzXkIgQlwLCwOC8YkzhSruT00lzvO86ql3du051wYyAHeu37HhIHVRCqFjIFUIDzUQphwHlyPDRmX58lvPBcJXmZpvps3UazW1UYXPHs9LKUZTQCrE1hBbiUHj57Cluu8FgxbK4uMjSapNzU1c5e\/EKcWTInMdAEXkUufX9I7XWIAjbqSPJHCKCESF1ijWGi8MWgzJeC3mvPyDpOKRUxncSfnmZ8dd6hndKaA3lKOgdWXX9D778bIqFhQVGR0eZmpqiWq3y4MEDXjR2WK99QlQqMTFYRC00ILe+m1cFktzjFTwgKhgjtDs57dTTzhznx\/qpV0IAIhuw3GyTpI6OUzLnEDFE1hAaQ5I5tteX+fpqH8l2C2MM4+PjjI6OIiLkec7c3Bw\/PnFEwxfwqnivvH6+iL3Qn\/L3WoYl4MMzg9Rjw\/O1jBdrCZ3MYVQJcDxbaqKq2CCgFAaUQkOae5bXWySdlCAwlKOQWl+ZkVqVK+f7mbo0ydraGiMjI7TbbZxzdDodKpUKfX19fHrBs9DYYfWfFRqrK2xvb2F\/erQMUiRlq7nJxJk67586wdhQhdwrae7ZyTxOwfvd2yNkztHJHOdHqxgprrL3RZIbhFO1iEajwZ07d5ienmZkZISJiQlu377NvXv3CMOQwVLKgM0x9TL1eAQ4if3h2y96tUFEUO226F4d+h\/y5PGfjI2Nce3aNdI0pV6vc\/nyZa5fv87k5CSNRoNzYwN889XniHTLhir2KKXGGJ4vLlKv1wms5eSJE3vAb1z1g6KqJEkCwI0bN\/atv3nzJqqKcw7n\/aG9RwIBnD59mizL8N7vr67A5uYmeZ5TLpepVCq9Pd57BgYGjtTrvT9czgFzHJAxgogQBAHNZpM0zXpRybKM3HnEloqKfuA5SqIo4m1ExwJpN5nzPMcYQ6u1WXgH2LgfjfrYTt1xat4q\/i1HdixQlmUkSZs0TXv5k2UZAJIn6M4G2VaT10tL3T8Dup+Cd\/5EFHtFaCcJzuX\/DSiKIuI4Jq5UEBGGhoYolUpdwxAYQxAE2NDSarVIkoT5+Xnm5uZYXl5mY2ODlZUVtra2AJidne2te\/L4Mffvz+6zd2xS7344vXP0HUjUIAiw1uK9JwgiEo0YKpcxxnD37l1mZmYYHh7uRW5gYICHDx9Sq9XI8yIyv\/72+z6d\/wKYTk2khRKRbgAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/micrus-rip_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022470.93389296957\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThe PHP ecosystem is full of frameworks: \u003Ca href=\u0022https:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Symfony\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/laravel.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Laravel\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/www.yiiframework.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Yii\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/framework.zend.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Zend\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/phalconphp.com\/en\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Phalcon\u003C\/a\u003E, and so many, many, many more... All of them built by professionals and supported by big communities. So why on earth would a junior developer, who has just started his first job, try his hand in building yet another one?\u003C\/p\u003E\n\u003Cp\u003EWell, here\u2019s why:\u003C\/p\u003E\n\u003Cp\u003EWhen I learned PHP, the URLs I knew how to build and use looked like this: \u003Ccode\u003E\/index.php?module=user\u0026amp;action=show\u0026amp;id=123\u003C\/code\u003E. I had no idea, how to make them nice like this: \u003Ccode\u003E\/user\/show\/123\u003C\/code\u003E. Symfony knew though, and I could just use it.\u003C\/p\u003E\n\u003Cp\u003EBut I was used to coding things from scratch and understanding how they work internally, I didn\u2019t like the magic of \u201cit just works\u201d. I know, stupid, if I were still doing that, I\u2019d never finish any project. At the time it did make sense, though.\u003C\/p\u003E\n\u003Cp\u003EI was curious if I can replicate the behaviour. I stole the \u003Ccode\u003E.htaccess\u003C\/code\u003E config from Symfony, I\u2019ve expanded my knowledge about regular expressions, and eventually, I did it! It was buggy and ugly, but it worked and it was mine, and I was so proud of it!\u003C\/p\u003E\n\u003Cp\u003EAnd then I couldn\u2019t stop. Every time I had had to dig into Symfony\u2019s code and had gotten overwhelmed by its complexity, I realised how simple the overall logic actually is, even though it\u2019s overblown by trying to keep it generic and open for modification\/extension. So just like I did with the router, I also rewrote from scratch the event dispatcher, the security layer, the DI container and a couple of other components, eventually ending up with a fully usable framework.\u003C\/p\u003E\n\u003Cp\u003EI don\u2019t think I have access to its source code anymore, but I think it was only 10 files or so. It was crappy, didn\u2019t comply with PSR-4, wasn\u2019t published as a Composer package, and probably was full of bugs that I\u2019ve never found.\u003C\/p\u003E\n\u003Cp\u003EIt didn\u2019t really make much sense to further develop it. Nobody\u2019s gonna use it anyway. It\u2019s not gonna be better than any of the frameworks that have plenty of people working on them. I\u2019ve already learned a lot, isn\u2019t it enough to call it a day?\u003C\/p\u003E\n\u003Cp\u003EBut I took some criticism for the version 0.0 from people who knew better how to code, so I had to address their advice and to make Micrus better. And it turned out to be a great decision because I still had \u003Cem\u003Ea lot\u003C\/em\u003E more to learn.\u003C\/p\u003E\n\u003Cp\u003EThe biggest challenge was managing dependencies. I couldn\u2019t put all the modules like \u201cMailer\u201d, \u201cTwig\u201d, \u201cSocial login\u201d, \u201cCRUD\u201d etc. in one huge package and still call it \u201cMicrus\u201d. But I was developing all of them at once! I had to learn more about the internals of Composer in order to treat local directories as if they were packages, without pushing or tagging them (only later I found out about \u003Ca href=\u0022https:\/\/getcomposer.org\/doc\/05-repositories.md#path\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Repository of type \u201cpath\u201d\u003C\/a\u003E \u2013 really useful!). I had to learn how to organise the code better so that adding or removing entire modules could be as seamless as possible. I had to keep them consistent, simple, powerful, extendable. I spent hours and hours, and days and days, on figuring out their architecture...\u003C\/p\u003E\n\u003Cp\u003EThe second biggest challenge was the forms. They\u2019re just awful. The form abstraction layer has to move data around between the actual object that holds data, a form and it\u2019s HTML representation. It needs to map the data both ways, it needs to validate it, it\u2019s just a mess. At work at \u003Ca href=\u0022https:\/\/www.rocket-internet.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Rocket Internet\u003C\/a\u003E, even though we were using a Phalcon-based framework, many ventures decided to use the standalone \u003Ca href=\u0022https:\/\/symfony.com\/doc\/current\/components\/form.html\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Symfony Form\u003C\/a\u003E component instead of Phalcon forms, because that was the least troublesome one.\u003C\/p\u003E\n\u003Cp\u003EStill, they weren\u2019t simple enough for me. Sure, they\u2019re way, way better than handling the forms without any framework, but whenever I needed to do anything even slightly non-standard, I really missed the option of just writing down some HTML. Instead, I had to learn about the internals of the framework. I didn\u2019t like it, so, I\u2019ve spent ages trying to prove to myself, that one can make the best of two worlds.\u003C\/p\u003E\n\u003Cp\u003EThe same with the security layer. I really hate how Symfony implements it. It\u2019s a piece of cake to configure the most simple cases, they hide everything behind a veil of magic. But as soon as you need something non-standard, you need to go through their documentation, get into their way of thinking, understand their ideas... You can\u2019t just write down the simple logic you need, you need wrappers, voters, providers, listeners, etc. etc. I decided to try to implement it my way.\u003C\/p\u003E\n\u003Cp\u003EAnd it worked for me. I\u2019ve released \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus \u2022 Tiny, yet powerful, PHP framework\u003C\/a\u003E that later become  \u003Ca href=\u0022\/projects\/micrus-v4\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus v4 \u2022 Beauty of simplicity\u003C\/a\u003E. I\u2019ve built a couple of projects based on Micrus (listed at \u003Ca href=\u0022https:\/\/micrus.avris.it\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E micrus.avris.it\u003C\/a\u003E). I had fun, seeing how easy it was for me to build apps with Micrus.\u003C\/p\u003E\n\u003Cp\u003EWhenever I would encounter something worth improving, I did so. How exhausting was that! On one hand disappointing (I wasn\u2019t expecting anyone else to use it), on the other really rewarding (it is an accomplishment after all). When I discovered \u003Ca href=\u0022\/blog\/the-non-magic-of-autowiring\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-computer-speaker\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E The non-magic of autowiring\u003C\/a\u003E, I implemented that as well. I covered 100% of my code with unit tests (which was quite a challenging type of code to cover).\u003C\/p\u003E\n\u003Cp\u003EBut then, Symfony has released its version four. And it\u2019s amazing. It has \u003Ca href=\u0022https:\/\/symfony.com\/doc\/current\/setup\/flex.html\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Flex\u003C\/a\u003E, it\u2019s flexible, it\u2019s fast... Maybe it\u2019s not as simple as I would wish it to be, but it\u2019s undeniably way better than anything I could ever create myself. Also, I simply got tired of building Micrus already. It grew bigger than I thought it would, it started resembling Symfony more and more, while I understood more and more of Symfony\u2019s design... \u003C\/p\u003E\n\u003Cp\u003ESo there I am: having learned an awful lot about standards, modularisation, architecture, framework internals, autoloading, autowiring, testing, managing dependencies, and much much more. Having created something irrelevant, but quite impressive nevertheless.\u003C\/p\u003E\n\u003Cp\u003EI\u2019m still gonna keep putting Micrus in my CV, I\u2019m still gonna keep most of my Micrus-based-projects running it, but I\u2019m not gonna improve it anymore or start any new projects on it.\u003C\/p\u003E\n\u003Cp\u003EIt\u2019s been a wonderful adventure. Now it\u2019s time to move on \ud83d\ude0a\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["framework","micrus","php","programming"],"hasMore":true,"image":"https:\/\/avris.it\/image\/micrus-rip_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/micrus-rip_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus-rip_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022117.73347324239\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThe PHP ecosystem is full of frameworks: \u003Ca href=\u0022https:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Symfony\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/laravel.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Laravel\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/www.yiiframework.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Yii\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/framework.zend.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Zend\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/phalconphp.com\/en\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Phalcon\u003C\/a\u003E, and so many, many, many more... All of them built by professionals and supported by big communities. So why on earth would a junior developer, who has just started his first job, try his hand in building yet another one?\u003C\/p\u003E\n\u003Cp\u003EWell, here\u2019s why:\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/micrus-rip_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus-rip_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022117.73347324239\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThe PHP ecosystem is full of frameworks: \u003Ca href=\u0022https:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Symfony\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/laravel.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Laravel\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/www.yiiframework.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Yii\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/framework.zend.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Zend\u003C\/a\u003E, \u003Ca href=\u0022https:\/\/phalconphp.com\/en\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Phalcon\u003C\/a\u003E, and so many, many, many more... All of them built by professionals and supported by big communities. So why on earth would a junior developer, who has just started his first job, try his hand in building yet another one?\u003C\/p\u003E\n\u003Cp\u003EWell, here\u2019s why:\u003C\/p\u003E\n\u003Cp\u003EWhen I learned PHP, the URLs I knew how to build and use looked like this: \u003Ccode\u003E\/index.php?module=user\u0026amp;action=show\u0026amp;id=123\u003C\/code\u003E. I had no idea, how to make them nice like this: \u003Ccode\u003E\/user\/show\/123\u003C\/code\u003E. Symfony knew though, and I could just use it.\u003C\/p\u003E\n\u003Cp\u003EBut I was used to coding things from scratch and understanding how they work internally, I didn\u2019t like the magic of \u201cit just works\u201d. I know, stupid, if I were still doing that, I\u2019d never finish any project. At the time it did make sense, though.\u003C\/p\u003E\n\u003Cp\u003EI was curious if I can replicate the behaviour. I stole the \u003Ccode\u003E.htaccess\u003C\/code\u003E config from Symfony, I\u2019ve expanded my knowledge about regular expressions, and eventually, I did it! It was buggy and ugly, but it worked and it was mine, and I was so proud of it!\u003C\/p\u003E\n\u003Cp\u003EAnd then I couldn\u2019t stop. Every time I had had to dig into Symfony\u2019s code and had gotten overwhelmed by its complexity, I realised how simple the overall logic actually is, even though it\u2019s overblown by trying to keep it generic and open for modification\/extension. So just like I did with the router, I also rewrote from scratch the event dispatcher, the security layer, the DI container and a couple of other components, eventually ending up with a fully usable framework.\u003C\/p\u003E\n\u003Cp\u003EI don\u2019t think I have access to its source code anymore, but I think it was only 10 files or so. It was crappy, didn\u2019t comply with PSR-4, wasn\u2019t published as a Composer package, and probably was full of bugs that I\u2019ve never found.\u003C\/p\u003E\n\u003Cp\u003EIt didn\u2019t really make much sense to further develop it. Nobody\u2019s gonna use it anyway. It\u2019s not gonna be better than any of the frameworks that have plenty of people working on them. I\u2019ve already learned a lot, isn\u2019t it enough to call it a day?\u003C\/p\u003E\n\u003Cp\u003EBut I took some criticism for the version 0.0 from people who knew better how to code, so I had to address their advice and to make Micrus better. And it turned out to be a great decision because I still had \u003Cem\u003Ea lot\u003C\/em\u003E more to learn.\u003C\/p\u003E\n\u003Cp\u003EThe biggest challenge was managing dependencies. I couldn\u2019t put all the modules like \u201cMailer\u201d, \u201cTwig\u201d, \u201cSocial login\u201d, \u201cCRUD\u201d etc. in one huge package and still call it \u201cMicrus\u201d. But I was developing all of them at once! I had to learn more about the internals of Composer in order to treat local directories as if they were packages, without pushing or tagging them (only later I found out about \u003Ca href=\u0022https:\/\/getcomposer.org\/doc\/05-repositories.md#path\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Repository of type \u201cpath\u201d\u003C\/a\u003E \u2013 really useful!). I had to learn how to organise the code better so that adding or removing entire modules could be as seamless as possible. I had to keep them consistent, simple, powerful, extendable. I spent hours and hours, and days and days, on figuring out their architecture...\u003C\/p\u003E\n\u003Cp\u003EThe second biggest challenge was the forms. They\u2019re just awful. The form abstraction layer has to move data around between the actual object that holds data, a form and it\u2019s HTML representation. It needs to map the data both ways, it needs to validate it, it\u2019s just a mess. At work at \u003Ca href=\u0022https:\/\/www.rocket-internet.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Rocket Internet\u003C\/a\u003E, even though we were using a Phalcon-based framework, many ventures decided to use the standalone \u003Ca href=\u0022https:\/\/symfony.com\/doc\/current\/components\/form.html\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Symfony Form\u003C\/a\u003E component instead of Phalcon forms, because that was the least troublesome one.\u003C\/p\u003E\n\u003Cp\u003EStill, they weren\u2019t simple enough for me. Sure, they\u2019re way, way better than handling the forms without any framework, but whenever I needed to do anything even slightly non-standard, I really missed the option of just writing down some HTML. Instead, I had to learn about the internals of the framework. I didn\u2019t like it, so, I\u2019ve spent ages trying to prove to myself, that one can make the best of two worlds.\u003C\/p\u003E\n\u003Cp\u003EThe same with the security layer. I really hate how Symfony implements it. It\u2019s a piece of cake to configure the most simple cases, they hide everything behind a veil of magic. But as soon as you need something non-standard, you need to go through their documentation, get into their way of thinking, understand their ideas... You can\u2019t just write down the simple logic you need, you need wrappers, voters, providers, listeners, etc. etc. I decided to try to implement it my way.\u003C\/p\u003E\n\u003Cp\u003EAnd it worked for me. I\u2019ve released \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework.lite\u0022\u003E Micrus \u2022 Tiny, yet powerful, PHP framework\u003C\/a\u003E that later become  \u003Ca href=\u0022\/projects\/micrus-v4.lite\u0022\u003E Micrus v4 \u2022 Beauty of simplicity\u003C\/a\u003E. I\u2019ve built a couple of projects based on Micrus (listed at \u003Ca href=\u0022https:\/\/micrus.avris.it\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E micrus.avris.it\u003C\/a\u003E). I had fun, seeing how easy it was for me to build apps with Micrus.\u003C\/p\u003E\n\u003Cp\u003EWhenever I would encounter something worth improving, I did so. How exhausting was that! On one hand disappointing (I wasn\u2019t expecting anyone else to use it), on the other really rewarding (it is an accomplishment after all). When I discovered \u003Ca href=\u0022\/blog\/the-non-magic-of-autowiring.lite\u0022\u003E The non-magic of autowiring\u003C\/a\u003E, I implemented that as well. I covered 100% of my code with unit tests (which was quite a challenging type of code to cover).\u003C\/p\u003E\n\u003Cp\u003EBut then, Symfony has released its version four. And it\u2019s amazing. It has \u003Ca href=\u0022https:\/\/symfony.com\/doc\/current\/setup\/flex.html\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Flex\u003C\/a\u003E, it\u2019s flexible, it\u2019s fast... Maybe it\u2019s not as simple as I would wish it to be, but it\u2019s undeniably way better than anything I could ever create myself. Also, I simply got tired of building Micrus already. It grew bigger than I thought it would, it started resembling Symfony more and more, while I understood more and more of Symfony\u2019s design... \u003C\/p\u003E\n\u003Cp\u003ESo there I am: having learned an awful lot about standards, modularisation, architecture, framework internals, autoloading, autowiring, testing, managing dependencies, and much much more. Having created something irrelevant, but quite impressive nevertheless.\u003C\/p\u003E\n\u003Cp\u003EI\u2019m still gonna keep putting Micrus in my CV, I\u2019m still gonna keep most of my Micrus-based-projects running it, but I\u2019m not gonna improve it anymore or start any new projects on it.\u003C\/p\u003E\n\u003Cp\u003EIt\u2019s been a wonderful adventure. Now it\u2019s time to move on \ud83d\ude0a\u003C\/p\u003E","words":1080,"readTime":5,"lang":"en"}}},"projects\/micrus-v4":{"key":"projects\/micrus-v4","type":"article","published":true,"meta":{"createdAt":"2018-01-28T12:45:41+01:00","publishedAt":"2018-01-28T12:40:00+01:00","group":"micrus","links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/micrus.avris.it","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Micrus","displayUrl":null}],"category":"projects","subcategory":null,"slug":"micrus-v4"},"content":{"en":{"slug":"micrus-v4","title":"Micrus v4 \u2022 Beauty of simplicity","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus-demo_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022279.53757225434\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEgklEQVRIicWWy44bRRSGv1NdfbE9Y0+scTJBIQosgGwQICEWvAAb3oGX4hV4CRYsWCAuEkgIERCCEEKSGWcSz3hst12Xw6K6fZkJGyTgqOVqd50+9Z\/z\/12nRJMBoKocHx9js4yyqphOz+l293BuRZZllGXJ+fkZZdkhhgAC+\/0+ZVGwbSKCqq5HgOn9x2CUjsk5mzyEm7fpdDo45zg4OFj7ydc\/nTR365\/d4AiKrmc2znrFtzVtp7bChRAQbSKIIMZceikN9tNvHzVZtRGkvV4IcAeG6g423XFSaJJ50Zyy\/Z4imkZrshZMm5SuqyVyeaUX1UV3cekmEb2CtPVpqJRNXkgarTXbgJoZEaTxfve1Ef1uzlc\/j\/ngnVt8\/sMTnjxfbBaj1ckmKxE47HfwMRKjEiMU1hBUmdeeehV2C9mMqmC7uUkwRLbKmji7ddjj\/bvXAXjtpT5lnvHhe7f55LNfWPmwlfbm3cIaepUlaMQKRAMYwaAYIwy6lk4uzGq\/RaYkyhTszf0ilasJGJvggvDmnSHf3Dum3yt45eaAr348ZtArePuVIb8+Pmv8dxWcW6HKM0JstCFCjBEQoiohRHLJsC2l2nLWAHLBUUdDP1dqDw\/PA93c8PqhZfL0Kb52nM5gfjbB157xDLpVzlEvBfAq3Bs7vn9c40NEEbql4VrHMqgyEBhUBh8Ua2C+ijyfJ9qu7+csHIwvPMNuxqtDi3z08RdqAMQkLWkqcSZw93rJG7eukZtIgWe\/EDCG356teKlyfPfI8eWfnplLWhGgU2YMewXj6RIUjEDtGnpjREXxARZL3yqW3BgQMIDNTSvEVF4UFivP09pztnB8+fuMw\/2CvdJiM4OqMl04AKbLgI9KbpQoSbz1MvKwdolKTV9giMrKe0IjchCMQGYSzT5GUJLu3nq5h\/Pw5HzJn88WTOYOVYiAc46lD5yeC3lmyDJDlVvKPCMzwnLlGE8umNcritxSFJZeVXLQK7GZTV+ZQoxKCIaoEGLER8UAEUWi4pvqirHYUancO3c4Hxn2CoZ7JSjUPuBaZ92IPjOCaspouXL0OgXdKifGiI9wUS+Zzmuu7XW4PdonzwwhJBAtIEEQ02zDknqBUXDBI4t6+fc94H8wK+tdMfFts+w\/BaCqW1uHYtPAmpPT06c8ePCAo6MbIIaqLBmNRuvu3Qb5J\/fbz9ZgoqaTA01rXNSrLYVAWeT\/SiX+zmKMOB9ou6Hd9ONkIQTOziY450GVvCgYDodXAi0Wc+7f\/x3JLEVnnzu3jjCXDgchBMbjE55Pa4y1HFwbcuOgt3NO2vTA9N+mRtrSlh4aY9a9zTnHyckJVVUxGAzWi4kYBoMBe3t7O89baxfM84JBT4nVAf1etaZsG9Q2jetT0lZrJUZFYyTGyGqZTosXFxdMJpPNipIxz\/qcectv4xlLH7ey3nKr9lnmA0SETm6uzF9K46qoE7i0e9osw+YZIQSMMcxms3U1DJFunJI5IUZlcnpBCJHR6JA83+jQhprKXWCC4Y9TJSjcGfVecPRLBP0FlaGMv+a4vYsAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/micrus-demo_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022279.53757225434\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA framework doesn\u2019t have to be overly complex! Micrus provides you with a quick, easy and comfortable way of creating neatly structured, modular MVC websites, which can be easily extended and configured.\u003C\/p\u003E\n\u003Cp\u003EOur goal is to keep the framework as simple as possible, while offering all the most important features.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus-demo_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022559.07514450867\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEgklEQVRIicWWy44bRRSGv1NdfbE9Y0+scTJBIQosgGwQICEWvAAb3oGX4hV4CRYsWCAuEkgIERCCEEKSGWcSz3hst12Xw6K6fZkJGyTgqOVqd50+9Z\/z\/12nRJMBoKocHx9js4yyqphOz+l293BuRZZllGXJ+fkZZdkhhgAC+\/0+ZVGwbSKCqq5HgOn9x2CUjsk5mzyEm7fpdDo45zg4OFj7ydc\/nTR365\/d4AiKrmc2znrFtzVtp7bChRAQbSKIIMZceikN9tNvHzVZtRGkvV4IcAeG6g423XFSaJJ50Zyy\/Z4imkZrshZMm5SuqyVyeaUX1UV3cekmEb2CtPVpqJRNXkgarTXbgJoZEaTxfve1Ef1uzlc\/j\/ngnVt8\/sMTnjxfbBaj1ckmKxE47HfwMRKjEiMU1hBUmdeeehV2C9mMqmC7uUkwRLbKmji7ddjj\/bvXAXjtpT5lnvHhe7f55LNfWPmwlfbm3cIaepUlaMQKRAMYwaAYIwy6lk4uzGq\/RaYkyhTszf0ilasJGJvggvDmnSHf3Dum3yt45eaAr348ZtArePuVIb8+Pmv8dxWcW6HKM0JstCFCjBEQoiohRHLJsC2l2nLWAHLBUUdDP1dqDw\/PA93c8PqhZfL0Kb52nM5gfjbB157xDLpVzlEvBfAq3Bs7vn9c40NEEbql4VrHMqgyEBhUBh8Ua2C+ijyfJ9qu7+csHIwvPMNuxqtDi3z08RdqAMQkLWkqcSZw93rJG7eukZtIgWe\/EDCG356teKlyfPfI8eWfnplLWhGgU2YMewXj6RIUjEDtGnpjREXxARZL3yqW3BgQMIDNTSvEVF4UFivP09pztnB8+fuMw\/2CvdJiM4OqMl04AKbLgI9KbpQoSbz1MvKwdolKTV9giMrKe0IjchCMQGYSzT5GUJLu3nq5h\/Pw5HzJn88WTOYOVYiAc46lD5yeC3lmyDJDlVvKPCMzwnLlGE8umNcritxSFJZeVXLQK7GZTV+ZQoxKCIaoEGLER8UAEUWi4pvqirHYUancO3c4Hxn2CoZ7JSjUPuBaZ92IPjOCaspouXL0OgXdKifGiI9wUS+Zzmuu7XW4PdonzwwhJBAtIEEQ02zDknqBUXDBI4t6+fc94H8wK+tdMfFts+w\/BaCqW1uHYtPAmpPT06c8ePCAo6MbIIaqLBmNRuvu3Qb5J\/fbz9ZgoqaTA01rXNSrLYVAWeT\/SiX+zmKMOB9ou6Hd9ONkIQTOziY450GVvCgYDodXAi0Wc+7f\/x3JLEVnnzu3jjCXDgchBMbjE55Pa4y1HFwbcuOgt3NO2vTA9N+mRtrSlh4aY9a9zTnHyckJVVUxGAzWi4kYBoMBe3t7O89baxfM84JBT4nVAf1etaZsG9Q2jetT0lZrJUZFYyTGyGqZTosXFxdMJpPNipIxz\/qcectv4xlLH7ey3nKr9lnmA0SETm6uzF9K46qoE7i0e9osw+YZIQSMMcxms3U1DJFunJI5IUZlcnpBCJHR6JA83+jQhprKXWCC4Y9TJSjcGfVecPRLBP0FlaGMv+a4vYsAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/micrus-demo_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022559.07514450867\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA framework doesn\u2019t have to be overly complex! Micrus provides you with a quick, easy and comfortable way of creating neatly structured, modular MVC websites, which can be easily extended and configured.\u003C\/p\u003E\n\u003Cp\u003EOur goal is to keep the framework as simple as possible, while offering all the most important features.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["container","dependency injection","doctrine","event dispatcher","framework","full-stack","micrus","mvc","php","routing","twig"],"hasMore":false,"image":"https:\/\/avris.it\/image\/micrus-demo_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/micrus-demo_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus-demo_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022139.76878612717\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA framework doesn\u2019t have to be overly complex! Micrus provides you with a quick, easy and comfortable way of creating neatly structured, modular MVC websites, which can be easily extended and configured.\u003C\/p\u003E\n\u003Cp\u003EOur goal is to keep the framework as simple as possible, while offering all the most important features.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/micrus-demo_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus-demo_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022139.76878612717\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA framework doesn\u2019t have to be overly complex! Micrus provides you with a quick, easy and comfortable way of creating neatly structured, modular MVC websites, which can be easily extended and configured.\u003C\/p\u003E\n\u003Cp\u003EOur goal is to keep the framework as simple as possible, while offering all the most important features.\u003C\/p\u003E","words":51,"readTime":null,"lang":"en"}}},"projects\/avris-forms":{"key":"projects\/avris-forms","type":"article","published":true,"meta":{"createdAt":"2018-01-26T20:28:10+01:00","publishedAt":"2018-01-26T20:27:00+01:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Forms","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-forms"},"content":{"en":{"slug":"avris-forms","title":"Avris Forms","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-forms_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHk0lEQVRIiX2X268lRRXGf+tS1b33PnufM8PMgWGA4SaKqEQikYQQkASBGDRovKAJ0Qd90Pjgn+C7bz4Yjc\/6ZHxBTDQagQQSReMFEIngzHAZEOZyztnnsnd31fKh9wxGjZWu7k66Ll99X61aX8s1194SUSpCELVCVMydWgpBoCK4GqZKcme5XFBrhQiIQAARQRiKiCAiQHCxRMSq1ktPoiICKoK5YyI0zQiPECKCUgsSIEApBWEYWFUHoKJQK0mVfjUBIqt2XGovAiCoyruTy8V3IWpB1Ih4t58hJDW0FvxiB0EJKhGgIYgOkxGCqgNCXyqGoAghiqmu2BgWggzfVtfqZrgby2WHCEQMCxRRdNXXVHEzkioeolDLiuCBESKQEFQNN8VEMFHcFIlKrZVayzCA+7D6WlG1S1KpCFErQbC+scGFc+eBCiGIQ9SCqkCAmZLMSH4RkAAqUFdsCYjoSgZDUcyMbIlx00D07O3v07jTJCd7otZKKT19KSAM+6VWAI5dvsliZw7UgbQIVPMKlGI6SJxNcdFERIeEgVT00iYV1Gyg1hIqioqh4kyaCYdG65yfb6GhRAkaT6hniEKI0PfdwJDAiWuu4c2TpxEJJMBUEAJIiMRKMiWZ4poyfbCKGLsYK6A6sKOGqZHdyZZQDJFEkxPXHV1jd2\/Oouu4\/IpNlvt7zLd3MBGyKzUqd378bl5+4UVmuSGIIbIA1UEUVRCBZEp2w0MTklZyIdQAE0XVUFHcHFcje8ZFSOYkSSRtOHx4nU9\/+bMcPrTBb37yc1499SrWrJF8iKbp+hp33ns3zz\/xNBvtCChExABGBRUwE0wH1twFJzdI79QaK30BFFFDzREx3BzVhJutauKeB+\/ikW98npee\/SuPfffHJAo3HbuSqJWd3TmejS99+1s8+9PHuWo2Y1k6avRElEE6XUVXGvZQToopuLQTaleQWodzKAAxRBy1RDJH1UjmtCkzducrX\/8M9z98D\/PzOzz+\/cfIOuLQuOWGG69kd+sdtvYOuPebXySpUE+9xvuObTLf3+OgX1JqT6kdaCW5YSa4K8mFlAyX8QxZ9lAqAcMeQRFJqBjiiaxOmxLjlHjk0fu4\/+F7APjzU8+RaJiuTVkbN+yc2+fERz7Exx+6E28bnvnOD9k8so5Gx9Z20PeJEgOgkIpqkLLjLqSkeBZcRlPIPRSQUAhFUBAHTZg6yZycErfeeh2f\/NzHLqWE7bfmXHb5UY6f2OTELVdz9W3XMzkyIyJ49RdPMhllSl2jbXpyU0EqNTr6fknUDjVQDcyN3DieBGe6Dn0hQgYwMUSX4bgmFEdWB+CjX3vw0skM8MBX7+M\/S0Tw+pO\/Z\/n2Oxy99jIWW5lu9wIz7RCriAW1dtR+gdCjJpgrKWe8aXCdTkmFIc+geCgJI4uTw8hqZJTbbr2G48cP\/xeAfy+1FF7+5e\/Yeell2onSro85evNV9FsTts8IKkuEDiQTJSFShwhLjuWMNSN8ujGlFGDFkCF4OK0YLUqL0VThjo\/e+H\/BzN\/e4oWfPc3y7NuMGkCh299j72yHy5LxekPtynD2GKgkZCWXumO5RZsRfsXhEX0f1AI1FA1FMVIICaMNpSnCTdcf+Z9A9rf3+NtTz\/H6H14kS8eorUPqqBAd1IVBo+SREkkxiZVMCTXFUkZzg6QG8gj\/wGbD3kHHclFY9pVSKqVWJASJihXBRDhyaHwJxO58n1Mvvc7JP73C68\/\/gyY6xl6IVHAqWSuhARawHHKlpCB54CaYCZoSkltIBjlDbsBb\/KFDb7B7UJnPO7Z3K\/M+2FnA3hIOOlj2QunhB9\/7Efu7+1x4Z5vdc9vkUkgUEpVWKyPrmaRg1laW4wrTIIXQ2mAzpHSoB5YNq0HXGcaUbmFom9g6P6ajRe655UjM2sTBwWCV3DIhTsUJSRScqpm+QoiCOiLKxmydyXjMztYW2YxsQqMwyTDNcHQSHDtU2DxiTCbC6TMXmO\/vU3LHxpXHeeJXv+XkmQWztrK5OWXRBc+8sIusbRwOTRlRw1LCmxZRw3ODp4ylBlUjNy3mieQZt8Rdd9xOt79gbTyiWxYmueXoxoTFzpxzp99gTM9GC5etKYcmkLQnWc\/GpOemW8dcODPn7TPbTEfg0nP0jvvY+8uv8Zs\/9QWatRl9hXYyxVODqiPmJE+IGMKQdyQEKRVKYTGbMmePzWNHOHt2i3mFRAttIh8fsdies3BhNyk5GW2qVKscNJXONjj8Xrgwf4U0Ehrv6c\/vsfb++\/GCMd\/bB0ssd3YQ2QV08EIxGPDBC7ECU5Fa+efpA6iFV08aUgKJwGohMXjjccpMkzN1Z71JjJPgWhhnmB903P7BGa+dV+q5ntkIphfe4D0PfBg\/f+rvLLsO9UwtFW9G8O4\/BAJ4SqgqEkHUQGMAoLUifX\/J4NdugYtx4a3TtLnFRVhfm3HZbAOPyt7eNodmU\/74ovPmqRFvbR1w8uwBoxw0uuQT\/WvIkRtujloK\/XJJ7RakyRQJoSwP0NQM6aD2eG4BBs8NaA0UQeKihx68uEYgVEwEV8EFlEo2QzXo+w6hsDl11lvj3P5gcxuHO07AvwDs3DcxrBe3EwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-forms_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EForms are complicated. There are many things you must take into consideration:\nbinding an existing object (if any) to each sparate field of the form,\nvalidating them after user has submited the form,\nif invalid redisplaying it with POST data bound and with validation errors,\nbinding the data back to an object...\u003C\/p\u003E\n\u003Cp\u003EAvris Forms add an abstraction layer that handles all of that.\nYou just need to define the list of fields you need and their cofiguration options.\nYou\u2019ll get an object that will handle everything for you.\nJust handle it in the controller and display it in the view.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-forms_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHk0lEQVRIiX2X268lRRXGf+tS1b33PnufM8PMgWGA4SaKqEQikYQQkASBGDRovKAJ0Qd90Pjgn+C7bz4Yjc\/6ZHxBTDQagQQSReMFEIngzHAZEOZyztnnsnd31fKh9wxGjZWu7k66Ll99X61aX8s1194SUSpCELVCVMydWgpBoCK4GqZKcme5XFBrhQiIQAARQRiKiCAiQHCxRMSq1ktPoiICKoK5YyI0zQiPECKCUgsSIEApBWEYWFUHoKJQK0mVfjUBIqt2XGovAiCoyruTy8V3IWpB1Ih4t58hJDW0FvxiB0EJKhGgIYgOkxGCqgNCXyqGoAghiqmu2BgWggzfVtfqZrgby2WHCEQMCxRRdNXXVHEzkioeolDLiuCBESKQEFQNN8VEMFHcFIlKrZVayzCA+7D6WlG1S1KpCFErQbC+scGFc+eBCiGIQ9SCqkCAmZLMSH4RkAAqUFdsCYjoSgZDUcyMbIlx00D07O3v07jTJCd7otZKKT19KSAM+6VWAI5dvsliZw7UgbQIVPMKlGI6SJxNcdFERIeEgVT00iYV1Gyg1hIqioqh4kyaCYdG65yfb6GhRAkaT6hniEKI0PfdwJDAiWuu4c2TpxEJJMBUEAJIiMRKMiWZ4poyfbCKGLsYK6A6sKOGqZHdyZZQDJFEkxPXHV1jd2\/Oouu4\/IpNlvt7zLd3MBGyKzUqd378bl5+4UVmuSGIIbIA1UEUVRCBZEp2w0MTklZyIdQAE0XVUFHcHFcje8ZFSOYkSSRtOHx4nU9\/+bMcPrTBb37yc1499SrWrJF8iKbp+hp33ns3zz\/xNBvtCChExABGBRUwE0wH1twFJzdI79QaK30BFFFDzREx3BzVhJutauKeB+\/ikW98npee\/SuPfffHJAo3HbuSqJWd3TmejS99+1s8+9PHuWo2Y1k6avRElEE6XUVXGvZQToopuLQTaleQWodzKAAxRBy1RDJH1UjmtCkzducrX\/8M9z98D\/PzOzz+\/cfIOuLQuOWGG69kd+sdtvYOuPebXySpUE+9xvuObTLf3+OgX1JqT6kdaCW5YSa4K8mFlAyX8QxZ9lAqAcMeQRFJqBjiiaxOmxLjlHjk0fu4\/+F7APjzU8+RaJiuTVkbN+yc2+fERz7Exx+6E28bnvnOD9k8so5Gx9Z20PeJEgOgkIpqkLLjLqSkeBZcRlPIPRSQUAhFUBAHTZg6yZycErfeeh2f\/NzHLqWE7bfmXHb5UY6f2OTELVdz9W3XMzkyIyJ49RdPMhllSl2jbXpyU0EqNTr6fknUDjVQDcyN3DieBGe6Dn0hQgYwMUSX4bgmFEdWB+CjX3vw0skM8MBX7+M\/S0Tw+pO\/Z\/n2Oxy99jIWW5lu9wIz7RCriAW1dtR+gdCjJpgrKWe8aXCdTkmFIc+geCgJI4uTw8hqZJTbbr2G48cP\/xeAfy+1FF7+5e\/Yeell2onSro85evNV9FsTts8IKkuEDiQTJSFShwhLjuWMNSN8ujGlFGDFkCF4OK0YLUqL0VThjo\/e+H\/BzN\/e4oWfPc3y7NuMGkCh299j72yHy5LxekPtynD2GKgkZCWXumO5RZsRfsXhEX0f1AI1FA1FMVIICaMNpSnCTdcf+Z9A9rf3+NtTz\/H6H14kS8eorUPqqBAd1IVBo+SREkkxiZVMCTXFUkZzg6QG8gj\/wGbD3kHHclFY9pVSKqVWJASJihXBRDhyaHwJxO58n1Mvvc7JP73C68\/\/gyY6xl6IVHAqWSuhARawHHKlpCB54CaYCZoSkltIBjlDbsBb\/KFDb7B7UJnPO7Z3K\/M+2FnA3hIOOlj2QunhB9\/7Efu7+1x4Z5vdc9vkUkgUEpVWKyPrmaRg1laW4wrTIIXQ2mAzpHSoB5YNq0HXGcaUbmFom9g6P6ajRe655UjM2sTBwWCV3DIhTsUJSRScqpm+QoiCOiLKxmydyXjMztYW2YxsQqMwyTDNcHQSHDtU2DxiTCbC6TMXmO\/vU3LHxpXHeeJXv+XkmQWztrK5OWXRBc+8sIusbRwOTRlRw1LCmxZRw3ODp4ylBlUjNy3mieQZt8Rdd9xOt79gbTyiWxYmueXoxoTFzpxzp99gTM9GC5etKYcmkLQnWc\/GpOemW8dcODPn7TPbTEfg0nP0jvvY+8uv8Zs\/9QWatRl9hXYyxVODqiPmJE+IGMKQdyQEKRVKYTGbMmePzWNHOHt2i3mFRAttIh8fsdies3BhNyk5GW2qVKscNJXONjj8Xrgwf4U0Ehrv6c\/vsfb++\/GCMd\/bB0ssd3YQ2QV08EIxGPDBC7ECU5Fa+efpA6iFV08aUgKJwGohMXjjccpMkzN1Z71JjJPgWhhnmB903P7BGa+dV+q5ntkIphfe4D0PfBg\/f+rvLLsO9UwtFW9G8O4\/BAJ4SqgqEkHUQGMAoLUifX\/J4NdugYtx4a3TtLnFRVhfm3HZbAOPyt7eNodmU\/74ovPmqRFvbR1w8uwBoxw0uuQT\/WvIkRtujloK\/XJJ7RakyRQJoSwP0NQM6aD2eG4BBs8NaA0UQeKihx68uEYgVEwEV8EFlEo2QzXo+w6hsDl11lvj3P5gcxuHO07AvwDs3DcxrBe3EwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-forms_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EForms are complicated. There are many things you must take into consideration:\nbinding an existing object (if any) to each sparate field of the form,\nvalidating them after user has submited the form,\nif invalid redisplaying it with POST data bound and with validation errors,\nbinding the data back to an object...\u003C\/p\u003E\n\u003Cp\u003EAvris Forms add an abstraction layer that handles all of that.\nYou just need to define the list of fields you need and their cofiguration options.\nYou\u2019ll get an object that will handle everything for you.\nJust handle it in the controller and display it in the view.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["abstraction","forms","library","php"],"hasMore":false,"image":"https:\/\/avris.it\/image\/avris-forms_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-forms_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-forms_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EForms are complicated. There are many things you must take into consideration:\nbinding an existing object (if any) to each sparate field of the form,\nvalidating them after user has submited the form,\nif invalid redisplaying it with POST data bound and with validation errors,\nbinding the data back to an object...\u003C\/p\u003E\n\u003Cp\u003EAvris Forms add an abstraction layer that handles all of that.\nYou just need to define the list of fields you need and their cofiguration options.\nYou\u2019ll get an object that will handle everything for you.\nJust handle it in the controller and display it in the view.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-forms_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-forms_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EForms are complicated. There are many things you must take into consideration:\nbinding an existing object (if any) to each sparate field of the form,\nvalidating them after user has submited the form,\nif invalid redisplaying it with POST data bound and with validation errors,\nbinding the data back to an object...\u003C\/p\u003E\n\u003Cp\u003EAvris Forms add an abstraction layer that handles all of that.\nYou just need to define the list of fields you need and their cofiguration options.\nYou\u2019ll get an object that will handle everything for you.\nJust handle it in the controller and display it in the view.\u003C\/p\u003E","words":101,"readTime":null,"lang":"en"}}},"projects\/avris-http":{"key":"projects\/avris-http","type":"article","published":true,"meta":{"createdAt":"2018-01-12T20:41:21+01:00","publishedAt":"2018-01-12T20:40:00+01:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Http","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-http"},"content":{"en":{"slug":"avris-http","title":"Avris Http","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-http_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHiklEQVRIiX2Xy69lRRXGf+tRtfc55577aLovb5qXKKJ0RI0kxIAkvGLQoPGBJkQHOtA48E9w7syB0TjWkXGCmGg0AgkkisYHIBJBGmga0tD3de659569q5aDfboxaqzsV7Krdn31fWvV+rZcc+0tEaUiBFErRMXcqaUQBCqCq2GqJHcWiyNqrRABEQggIghDExFEBAgutIhYnvXinaiIgIpg7pgITTPCI4SIoNSCBAhQSkEYPqyqA1BRqJWkSr+cAJFlPy72FwEQVOXdyeXCsxC1IGpEvDvOEJIaWgt+YYCgBJUI0BBEh8kIQdUBoS8VQ1CEEMVUl2wMC0GGd8tjeTHcjcWiQwQihgWKKLoca6q4GUkVD1GoZUnwwAgRSAiqhptiIpgobopEpdZKrWX4gPuw+lpRtYtSqQhRK0Gwtr7O9vktoEII4hC1oCoQYKYkM5JfACSACtQlWwIiupTBUBQzI1ti3DQQPfODAxp3muRkT9RaKaWnLwWEIV5qBeDySzc52psBdSAtAtW8BKWYDhJnU1w0EdEhYSAVvRikgpoN1FpCRVExVJxJM2FjtMbWbAcNJUrQeEI9QxRChL7vBoYETl5zDW++8ioigQSYCkIACZFYSqYkU1xTpg+WGWMXcgVUB3bUMDWyO9kSiiGSaHLiuhMr7M9nHHUdl162yeJgzmx3DxMhu1Kjcse9d\/LS8y+wmhuCGDILUB1EUQURSKZkNzw0IWkpF0INMFFUDRXFzXE1smdchGROkkTShmPH1vjMVz7HsY11fvvTX\/Da6dewZoXkQzZN11a44+47ee7xp1hvR0AhIgYwKqiAmWA6sOYuOLlBeqfWWOoLoIgaao6I4eaoJtxseSbueuDjPPzNL\/DiM3\/j0e\/9hEThpsuvIGplb3+GZ+PL3\/k2z\/zsMa5aXWVROmr0RJRBOl1mVxpiKCfFFFzaCbUrSK3DPhSAGCKOWiKZo2okc9qUGbvz1W98lvseuovZ1h6P\/eBRso7YGLfccOMV7O+8zc78kLu\/9SWSCvX067zv8k1mB3MO+wWl9pTagVaSG2aCu5JcSMlwGa8iix5KJWCIERSRhIohnsjqtCkxTomHH7mH+x66C4C\/PPksiYbpypSVccPe+QNOfuRW7n3wDrxtePq7P2Lz+BoaHTu7Qd8nSgyAQiqqQcqOu5CS4llwGU0h91BAQiEUQUEcNGHqJHNySpw6dR2f+vwnLpaE3bdmXHLpCa48ucnJW67m6tuuZ3J8lYjgtV8+wWSUKXWFtunJTQWp1Ojo+wVRO9RANTA3cuN4EpzpGvSFCBnAxJBdhuOaUBxZboCPfP2BizszwP1fu4f\/bBHBmSf+wOLc25y49hKOdjLd\/jar2iFWEQtq7aj9EUKPmmCupJzxpsF1OiUVhjqD4qEkjCxODiOrkVFuO3UNV1557L8A\/HurpfDSr37P3osv0U6Udm3MiZuvot+ZsHtWUFkgdCCZKAmROmRYcixnrBnh0\/UppQBLhgzBw2nFaFFajKYKt3\/sxv8LZnZuh+d\/\/hSLd84xagCF7mDO\/J0OlwXjtYbalWHvMVBJyFIudcdyizYj\/LJjI\/o+qAVqKBqKYqQQEkYbSlOEm64\/\/j+BHOzO+fuTz3Lmjy+QpWPU1qF0VIgO6pFBo+SREkkxiaVMCTXFUkZzg6QG8gj\/wGbD\/LBjcVRY9JVSKqVWJASJihXBRDi+Mb4IYn92wOkXz\/DKn1\/mzHP\/pImOsRciFZxK1kpogAUshlopKUgeuAlmgqaE5BaSQc6QG\/AWf3DjDfYPK7NZx+5+ZdYHe0cwX8BhB4teKD388Ps\/5mD\/gO23d9k\/v0suhUQhUWm1MrKeSQpW28piXGEapBBaG2yGlA71wLJhNeg6w5jSHRnaJna2xnS0yF23HI\/VNnF4OFglt0yIU3FCEgWnaqavEKKgjojiakzGY7IZCmQTPAqTDNMMJybB5RuFzePGZCK8enab2cEBJXesX3Elj\/\/6d7xy9ojVtrK5OeWoC55+fh9ZWT8WmjKihqWENy2ihucGTxlLDapGblrME8kzbommGXHHh29lbWUFFSOLsb+9w8HWNoutHTZaZb2FS1aUjQkk7UnWsz7puenUmO2zM86d3WU6ApeeE7ffw\/yvv8Fv\/vQXaVZW6Su0kymeGlQdMSd5QsQQhrojIUipUApSg\/loynxRWBtlLIRLr72e1+RNelnhyJQjF\/aTkpPRpkq1ymFT6WydY++F7dnLpJHQeE+\/NWfl\/ffhBWM2PwBLLPb2ENkHdPBCMRjwwQuxBFORWqmHh7z1aiGbISWQCDwCD0FrYZwy0+RM3VlrEuMkuBbGGWaHHR\/94Cqvbyn1fM\/qCKbbb\/Ce+z+Eb53+B4uuQz1TS8WbEbz7D4EAnhKqikQQNdAYAGitSN9fNPi1O8LF2H7rVdrc4iKsraxyyeo6HpX5fJeN1Sl\/esF58\/SIt3YOeeWdQ0Y5aHTBJ\/vXkeM33By1FPrFgtodkSZTJISyOERTM5SD2uO5BRg8N6A1UASJCx568OIagVAxEVwFF1DqEPwa9H2HUNicOmutcf5gsLmNw+0n4V\/6+DKRylDp2gAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-http_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHTTP Request\/Response abstraction\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-http_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHiklEQVRIiX2Xy69lRRXGf+tRtfc55577aLovb5qXKKJ0RI0kxIAkvGLQoPGBJkQHOtA48E9w7syB0TjWkXGCmGg0AgkkisYHIBJBGmga0tD3de659569q5aDfboxaqzsV7Krdn31fWvV+rZcc+0tEaUiBFErRMXcqaUQBCqCq2GqJHcWiyNqrRABEQggIghDExFEBAgutIhYnvXinaiIgIpg7pgITTPCI4SIoNSCBAhQSkEYPqyqA1BRqJWkSr+cAJFlPy72FwEQVOXdyeXCsxC1IGpEvDvOEJIaWgt+YYCgBJUI0BBEh8kIQdUBoS8VQ1CEEMVUl2wMC0GGd8tjeTHcjcWiQwQihgWKKLoca6q4GUkVD1GoZUnwwAgRSAiqhptiIpgobopEpdZKrWX4gPuw+lpRtYtSqQhRK0Gwtr7O9vktoEII4hC1oCoQYKYkM5JfACSACtQlWwIiupTBUBQzI1ti3DQQPfODAxp3muRkT9RaKaWnLwWEIV5qBeDySzc52psBdSAtAtW8BKWYDhJnU1w0EdEhYSAVvRikgpoN1FpCRVExVJxJM2FjtMbWbAcNJUrQeEI9QxRChL7vBoYETl5zDW++8ioigQSYCkIACZFYSqYkU1xTpg+WGWMXcgVUB3bUMDWyO9kSiiGSaHLiuhMr7M9nHHUdl162yeJgzmx3DxMhu1Kjcse9d\/LS8y+wmhuCGDILUB1EUQURSKZkNzw0IWkpF0INMFFUDRXFzXE1smdchGROkkTShmPH1vjMVz7HsY11fvvTX\/Da6dewZoXkQzZN11a44+47ee7xp1hvR0AhIgYwKqiAmWA6sOYuOLlBeqfWWOoLoIgaao6I4eaoJtxseSbueuDjPPzNL\/DiM3\/j0e\/9hEThpsuvIGplb3+GZ+PL3\/k2z\/zsMa5aXWVROmr0RJRBOl1mVxpiKCfFFFzaCbUrSK3DPhSAGCKOWiKZo2okc9qUGbvz1W98lvseuovZ1h6P\/eBRso7YGLfccOMV7O+8zc78kLu\/9SWSCvX067zv8k1mB3MO+wWl9pTagVaSG2aCu5JcSMlwGa8iix5KJWCIERSRhIohnsjqtCkxTomHH7mH+x66C4C\/PPksiYbpypSVccPe+QNOfuRW7n3wDrxtePq7P2Lz+BoaHTu7Qd8nSgyAQiqqQcqOu5CS4llwGU0h91BAQiEUQUEcNGHqJHNySpw6dR2f+vwnLpaE3bdmXHLpCa48ucnJW67m6tuuZ3J8lYjgtV8+wWSUKXWFtunJTQWp1Ojo+wVRO9RANTA3cuN4EpzpGvSFCBnAxJBdhuOaUBxZboCPfP2BizszwP1fu4f\/bBHBmSf+wOLc25y49hKOdjLd\/jar2iFWEQtq7aj9EUKPmmCupJzxpsF1OiUVhjqD4qEkjCxODiOrkVFuO3UNV1557L8A\/HurpfDSr37P3osv0U6Udm3MiZuvot+ZsHtWUFkgdCCZKAmROmRYcixnrBnh0\/UppQBLhgzBw2nFaFFajKYKt3\/sxv8LZnZuh+d\/\/hSLd84xagCF7mDO\/J0OlwXjtYbalWHvMVBJyFIudcdyizYj\/LJjI\/o+qAVqKBqKYqQQEkYbSlOEm64\/\/j+BHOzO+fuTz3Lmjy+QpWPU1qF0VIgO6pFBo+SREkkxiaVMCTXFUkZzg6QG8gj\/wGbD\/LBjcVRY9JVSKqVWJASJihXBRDi+Mb4IYn92wOkXz\/DKn1\/mzHP\/pImOsRciFZxK1kpogAUshlopKUgeuAlmgqaE5BaSQc6QG\/AWf3DjDfYPK7NZx+5+ZdYHe0cwX8BhB4teKD388Ps\/5mD\/gO23d9k\/v0suhUQhUWm1MrKeSQpW28piXGEapBBaG2yGlA71wLJhNeg6w5jSHRnaJna2xnS0yF23HI\/VNnF4OFglt0yIU3FCEgWnaqavEKKgjojiakzGY7IZCmQTPAqTDNMMJybB5RuFzePGZCK8enab2cEBJXesX3Elj\/\/6d7xy9ojVtrK5OeWoC55+fh9ZWT8WmjKihqWENy2ihucGTxlLDapGblrME8kzbommGXHHh29lbWUFFSOLsb+9w8HWNoutHTZaZb2FS1aUjQkk7UnWsz7puenUmO2zM86d3WU6ApeeE7ffw\/yvv8Fv\/vQXaVZW6Su0kymeGlQdMSd5QsQQhrojIUipUApSg\/loynxRWBtlLIRLr72e1+RNelnhyJQjF\/aTkpPRpkq1ymFT6WydY++F7dnLpJHQeE+\/NWfl\/ffhBWM2PwBLLPb2ENkHdPBCMRjwwQuxBFORWqmHh7z1aiGbISWQCDwCD0FrYZwy0+RM3VlrEuMkuBbGGWaHHR\/94Cqvbyn1fM\/qCKbbb\/Ce+z+Eb53+B4uuQz1TS8WbEbz7D4EAnhKqikQQNdAYAGitSN9fNPi1O8LF2H7rVdrc4iKsraxyyeo6HpX5fJeN1Sl\/esF58\/SIt3YOeeWdQ0Y5aHTBJ\/vXkeM33By1FPrFgtodkSZTJISyOERTM5SD2uO5BRg8N6A1UASJCx568OIagVAxEVwFF1DqEPwa9H2HUNicOmutcf5gsLmNw+0n4V\/6+DKRylDp2gAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-http_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHTTP Request\/Response abstraction\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["abstraction","http","library","php","request","response","session"],"hasMore":false,"image":"https:\/\/avris.it\/image\/avris-http_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-http_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-http_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHTTP Request\/Response abstraction\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-http_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-http_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHTTP Request\/Response abstraction\u003C\/p\u003E","words":4,"readTime":null,"lang":"en"}}},"projects\/avris-polonisator":{"key":"projects\/avris-polonisator","type":"article","published":true,"meta":{"createdAt":"2018-01-10T19:14:14+01:00","publishedAt":"2018-01-10T19:12:00+01:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Polonisator","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-polonisator"},"content":{"en":{"slug":"avris-polonisator","title":"Avris Polonisator","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-polonisator_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHp0lEQVRIiX2XyY8kRxXGf2+JyKyurupletp4DB6DwWAbY2GxGFmAQTIGIUCAWAySBQc4gDjwJ3DnxgGBOMMJcWGRQOwSSGxiszEWNh5vY2s801t1VXdlRjwOWTNGgAhlZKaUsXzxffHifSk33nR7RKkIQdQKUTF3aikEgYrgapgqyZ3l8pRaK0RABAKICMJQRAQRAYKrJSJWtV57EhURUBHMHROhaUZ4hBARlFqQAAFKKQjDwKo6ABWFWkmq9KsJEFm141p7EQBBVV6cXK6+C1ELokbEi\/0MIamhteBXOwhKUIkADUF0mIwQVB0Q+lIxBEUIUUx1xcawEGT4trpWN8PdWC47RCBiWKCIoqu+poqbkVTxEIVaVgQPjBCBhKBquCkmgonipkhUaq3UWoYB3IfV14qqXZNKRYhaCYKNzU32r+wBFUIQh6gFVYEAMyWZkfwqIAFUoK7YEhDRlQyGopgZ2RJrTQPRM18saNxpkpM9UWullJ6+FBCG\/VIrANdft8vp0QyoA2kRqOYVKMV0kDib4qKJiA4JA6notU0qqNlArSVUFBVDxRk3Y7ZGG+zNDtBQogSNJ9QzRCFE6PtuYEjg\/I038twTTyISSICpIASQEImVZEoyxTVl+mAVMXY1VkB1YEcNUyO7ky2hGCKJJidefnad4\/mM067jupfsslzMmR0eYSJkV2pU7nnX23ns4UeY5oYghsgCVAdRVEEEkinZDQ9NSFrJhVADTBRVQ0Vxc1yN7BkXIZmTJJG0YXt7gw996iNsb23ys2\/\/gKcuPIU16yQfommysc4973w7D\/38V2y2I6AQEQMYFVTATDAdWHMXnNwgvVNrrPQFUEQNNUfEcHNUE262qol73\/NWHvj8x3j0d3\/ju1\/5FonCLdefI2rl6HiGZ+OTX\/oiv\/vO93npdMqydNToiSiDdLqKrjTsoZwUU3Bpx9SuILUO51AAYog4aolkjqqRzGlTZs2dT3\/uw9z\/wXuZ7R3x\/a99l6wjttZabn7lOY4PXuBgfsI7v\/AJkgr1wtO85vpdZos5J\/2SUntK7UAryQ0zwV1JLqRkuKxNkWUPpRIw7BEUkYSKIZ7I6rQpsZYSDzx4H\/d\/8F4A\/vzLv5JomKxPWF9rOLqy4PwbXse73ncP3jb8+svfYHdnA42Og8Og7xMlBkAhFdUgZcddSEnxLLiMJpB7KCChEIqgIA6aMHWSOTkl7rzz5bz\/o++4lhIOn59x5rqz3HB+l\/O3v4yX3fUKxjtTIoKnfvgLxqNMqeu0TU9uKkilRkffL4naoQaqgbmRG8eT4Ew2oC9EyAAmhugyHNeE4sjqAHzws++5djIDvPsz9\/GfJSJ45he\/Z3npBc7edIbTg0x3vM9UO8QqYkGtHbU\/RehRE8yVlDPeNLhOJqTCkGdQPJSEkcXJYWQ1Mspdd97IDTds\/xeAfy+1FB770W85evQx2rHSbqxx9taX0h+MObwoqCwROpBMlIRIHSIsOZYz1ozwyeaEUoAVQ4bg4bRitCgtRlOFu9\/8yv8LZnbpgIe\/9yuWly8xagCFbjFnfrnDZcnaRkPtynD2GKgkZCWXumO5RZsR\/pLtEX0f1AI1FA1FMVIICaMNpSnCLa\/Y+Z9AFodz\/v7Lv\/LMHx4hS8eorUPqqBAd1FODRskjJZJiEiuZEmqKpYzmBkkN5BH+2t2G+UnH8rSw7CulVEqtSAgSFSuCibCztXYNxPFswYVHn+GJPz3OMw\/9kyY61rwQqeBUslZCAyxgOeRKSUHywE0wEzQlJLeQDHKG3IC3+Pu2nuX4pDKbdRweV2Z9cHQK8yWcdLDshdLD17\/6TRbHC\/ZfOOT4yiG5FBKFRKXVysh6ximYtpXlWoVJkEJobbAZUjrUA8uG1aDrDGNCd2pomzjYW6OjRe69fSembeLkZLBKbpkQp+KEJApO1UxfYTRex3PD0dExO2fOMDs8WuU4Y3tjnW5+zDjD5kjZbgvXbxV2d4zxWHjy4j6zxYKSOzbP3cDPf\/wbnrh4yrSt7O5OOO2CXz98jKxvboemjKhhKeFNi6jhucFTxlKDqpGblre95U2c2dxib2+P7a0zXLmyz9ZkyvK058x0wv4Le9x0bodxysTJjM1xor\/8LOu6IGlPsp7Ncc8td66xf3HGpYuHTEbg0nP27vuY\/+Un+K0f+DjN+pS+Qjue4KlB1RFzkidEDGHIO7a1wSOX9rnjtpspXWFz4xytG8dXDpnnhp2bz7IUYTE\/YdLusFDjuD9FxkabKtUqJ02ls022Xw37s8dJI6Hxnn5vzvpt9+MFYzZfgCWWR0eIHAM6eKEYDPjgheDyc89Cqfz04lPUkxOohWyGlEAisFpIDN54LWUmyZm4s9Ek1pLgWljLMDvpeOMdU57eU+qVnukIJvvP8qp3vx7fu\/APll2HeqaWijcjePEfAgE8JVQViSBqoDEA0FqRvr9m8Gt3ioux\/\/yTtLnFRdhYn3JmuolHZT4\/ZGs64Y+POM9dGPH8wQlPXD5hlINGl7y3fxrZufnWqKXQL5fU7pQ0niAhlOUJmpohHdQezy3A4LkBrYEiSFz10IMX1wiEiongKriAUslmqAZ93yEUdifORmtcWQw2t3G4+zz8C1qOQnwD4bZcAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-polonisator_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EAn extension to Avris Localisator and Avris Stringer introducing support for the Polish language.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-polonisator_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHp0lEQVRIiX2XyY8kRxXGf2+JyKyurupletp4DB6DwWAbY2GxGFmAQTIGIUCAWAySBQc4gDjwJ3DnxgGBOMMJcWGRQOwSSGxiszEWNh5vY2s801t1VXdlRjwOWTNGgAhlZKaUsXzxffHifSk33nR7RKkIQdQKUTF3aikEgYrgapgqyZ3l8pRaK0RABAKICMJQRAQRAYKrJSJWtV57EhURUBHMHROhaUZ4hBARlFqQAAFKKQjDwKo6ABWFWkmq9KsJEFm141p7EQBBVV6cXK6+C1ELokbEi\/0MIamhteBXOwhKUIkADUF0mIwQVB0Q+lIxBEUIUUx1xcawEGT4trpWN8PdWC47RCBiWKCIoqu+poqbkVTxEIVaVgQPjBCBhKBquCkmgonipkhUaq3UWoYB3IfV14qqXZNKRYhaCYKNzU32r+wBFUIQh6gFVYEAMyWZkfwqIAFUoK7YEhDRlQyGopgZ2RJrTQPRM18saNxpkpM9UWullJ6+FBCG\/VIrANdft8vp0QyoA2kRqOYVKMV0kDib4qKJiA4JA6notU0qqNlArSVUFBVDxRk3Y7ZGG+zNDtBQogSNJ9QzRCFE6PtuYEjg\/I038twTTyISSICpIASQEImVZEoyxTVl+mAVMXY1VkB1YEcNUyO7ky2hGCKJJidefnad4\/mM067jupfsslzMmR0eYSJkV2pU7nnX23ns4UeY5oYghsgCVAdRVEEEkinZDQ9NSFrJhVADTBRVQ0Vxc1yN7BkXIZmTJJG0YXt7gw996iNsb23ys2\/\/gKcuPIU16yQfommysc4973w7D\/38V2y2I6AQEQMYFVTATDAdWHMXnNwgvVNrrPQFUEQNNUfEcHNUE262qol73\/NWHvj8x3j0d3\/ju1\/5FonCLdefI2rl6HiGZ+OTX\/oiv\/vO93npdMqydNToiSiDdLqKrjTsoZwUU3Bpx9SuILUO51AAYog4aolkjqqRzGlTZs2dT3\/uw9z\/wXuZ7R3x\/a99l6wjttZabn7lOY4PXuBgfsI7v\/AJkgr1wtO85vpdZos5J\/2SUntK7UAryQ0zwV1JLqRkuKxNkWUPpRIw7BEUkYSKIZ7I6rQpsZYSDzx4H\/d\/8F4A\/vzLv5JomKxPWF9rOLqy4PwbXse73ncP3jb8+svfYHdnA42Og8Og7xMlBkAhFdUgZcddSEnxLLiMJpB7KCChEIqgIA6aMHWSOTkl7rzz5bz\/o++4lhIOn59x5rqz3HB+l\/O3v4yX3fUKxjtTIoKnfvgLxqNMqeu0TU9uKkilRkffL4naoQaqgbmRG8eT4Ew2oC9EyAAmhugyHNeE4sjqAHzws++5djIDvPsz9\/GfJSJ45he\/Z3npBc7edIbTg0x3vM9UO8QqYkGtHbU\/RehRE8yVlDPeNLhOJqTCkGdQPJSEkcXJYWQ1Mspdd97IDTds\/xeAfy+1FB770W85evQx2rHSbqxx9taX0h+MObwoqCwROpBMlIRIHSIsOZYz1ozwyeaEUoAVQ4bg4bRitCgtRlOFu9\/8yv8LZnbpgIe\/9yuWly8xagCFbjFnfrnDZcnaRkPtynD2GKgkZCWXumO5RZsR\/pLtEX0f1AI1FA1FMVIICaMNpSnCLa\/Y+Z9AFodz\/v7Lv\/LMHx4hS8eorUPqqBAd1FODRskjJZJiEiuZEmqKpYzmBkkN5BH+2t2G+UnH8rSw7CulVEqtSAgSFSuCibCztXYNxPFswYVHn+GJPz3OMw\/9kyY61rwQqeBUslZCAyxgOeRKSUHywE0wEzQlJLeQDHKG3IC3+Pu2nuX4pDKbdRweV2Z9cHQK8yWcdLDshdLD17\/6TRbHC\/ZfOOT4yiG5FBKFRKXVysh6ximYtpXlWoVJkEJobbAZUjrUA8uG1aDrDGNCd2pomzjYW6OjRe69fSembeLkZLBKbpkQp+KEJApO1UxfYTRex3PD0dExO2fOMDs8WuU4Y3tjnW5+zDjD5kjZbgvXbxV2d4zxWHjy4j6zxYKSOzbP3cDPf\/wbnrh4yrSt7O5OOO2CXz98jKxvboemjKhhKeFNi6jhucFTxlKDqpGblre95U2c2dxib2+P7a0zXLmyz9ZkyvK058x0wv4Le9x0bodxysTJjM1xor\/8LOu6IGlPsp7Ncc8td66xf3HGpYuHTEbg0nP27vuY\/+Un+K0f+DjN+pS+Qjue4KlB1RFzkidEDGHIO7a1wSOX9rnjtpspXWFz4xytG8dXDpnnhp2bz7IUYTE\/YdLusFDjuD9FxkabKtUqJ02ls022Xw37s8dJI6Hxnn5vzvpt9+MFYzZfgCWWR0eIHAM6eKEYDPjgheDyc89Cqfz04lPUkxOohWyGlEAisFpIDN54LWUmyZm4s9Ek1pLgWljLMDvpeOMdU57eU+qVnukIJvvP8qp3vx7fu\/APll2HeqaWijcjePEfAgE8JVQViSBqoDEA0FqRvr9m8Gt3ioux\/\/yTtLnFRdhYn3JmuolHZT4\/ZGs64Y+POM9dGPH8wQlPXD5hlINGl7y3fxrZufnWqKXQL5fU7pQ0niAhlOUJmpohHdQezy3A4LkBrYEiSFz10IMX1wiEiongKriAUslmqAZ93yEUdifORmtcWQw2t3G4+zz8C1qOQnwD4bZcAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-polonisator_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EAn extension to Avris Localisator and Avris Stringer introducing support for the Polish language.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["library","locale","localisation","localization","php","polish","translation"],"hasMore":false,"image":"https:\/\/avris.it\/image\/avris-polonisator_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-polonisator_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-polonisator_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EAn extension to Avris Localisator and Avris Stringer introducing support for the Polish language.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-polonisator_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-polonisator_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EAn extension to Avris Localisator and Avris Stringer introducing support for the Polish language.\u003C\/p\u003E","words":14,"readTime":null,"lang":"en"}}},"projects\/avris-stringer":{"key":"projects\/avris-stringer","type":"article","published":true,"meta":{"createdAt":"2018-01-10T19:12:52+01:00","publishedAt":"2018-01-10T19:10:00+01:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Stringer","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-stringer"},"content":{"en":{"slug":"avris-stringer","title":"Avris Stringer","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-stringer_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHlklEQVRIiX2X268lRRXGf+tS1b33PnufM7fDneGuiDoRJZIQA5IgEIMGjRc0IfqgDxof\/BN8980Ho\/FZn4wviIlGI5BAAmi8wIBEYAYYBgLMnMs+t91dtXzoPYNRY6Wru5Ouy1ffV6vW13L1NbdElIoQRK0QFXOnlkIQqAiuhqmS3FksDqi1QgREIICIIAxFRBARILhQImJZ68UnUREBFcHcMRGaZoRHCBFBqQUJEKCUgjAMrKoDUFGolaRKv5wAkWU7LrYXARBU5f3J5cK7ELUgakS8388QkhpaC36hg6AElQjQEESHyQhB1QGhLxVDUIQQxVSXbAwLQYZvy2t5M9yNxaJDBCKGBYoouuxrqrgZSRUPUahlSfDACBFICKqGm2IimChuikSl1kqtZRjAfVh9rajaRalUhKiVIFhdW2Pj3HmgQgjiELWgKhBgpiQzkl8AJIAK1CVbAiK6lMFQFDMjW2LcNBA9u3t7NO40ycmeqLVSSk9fCgjDfqkVgMsuWedgew7UgbQIVPMSlGI6SJxNcdFERIeEgVT04iYV1Gyg1hIqioqh4kyaCYdGq5yfb6KhRAkaT6hniEKI0PfdwJDA8auv5q1TryESSICpIASQEImlZEoyxTVl+mAZMXYhVkB1YEcNUyO7ky2hGCKJJieuPbbCzu6cg67jkkvXWeztMt\/axkTIrtSo3PGZO3n55IvMckMQQ2QBqoMoqiACyZTshocmJC3lQqgBJoqqoaK4Oa5G9oyLkMxJkkjacPjwKl\/4xpc4fGiNP\/7yN7x++nWsWSH5EE3T1RXuuPtOnn\/sSdbaEVCIiAGMCipgJpgOrLkLTm6Q3qk1lvoCKKKGmiNiuDmqCTdb1sRd93+Kh777FV569gUe+dEvSBRuuuxyola2d+Z4Nr7+g+\/z7K8e5crZjEXpqNETUQbpdBldadhDOSmm4NJOqF1Bah3OoQDEEHHUEskcVSOZ06bM2J1vfueL3PvgXczPb\/PoTx4h64hD45brb7icnc132dzd5+7vfY2kQj39Bh+8bJ353i77\/YJSe0rtQCvJDTPBXUkupGS4jGfIoodSCRj2CIpIQsUQT2R12pQYp8RDD9\/DvQ\/eBcDfnniORMN0ZcrKuGH73B7HP\/FRPvPAHXjb8NQPf8b60VU0Oja3gr5PlBgAhVRUg5QddyElxbPgMppC7qGAhEIogoI4aMLUSebklDhx4lo+9+VPX0wJW2\/POXLJMa44vs7xW67iqluvY3J0RkTw+m8fZzLKlLpC2\/TkpoJUanT0\/YKoHWqgGpgbuXE8Cc50FfpChAxgYoguw3FNKI4sD8CHv33\/xZMZ4L5v3cN\/lojgzON\/YvHOuxy75ggHm5luZ4OZdohVxIJaO2p\/gNCjJpgrKWe8aXCdTkmFIc+geCgJI4uTw8hqZJRbT1zNFVcc\/i8A\/15qKbz8u2fYfull2onSro45dvOV9JsTts4KKguEDiQTJSFShwhLjuWMNSN8ujalFGDJkCF4OK0YLUqL0VTh9k\/e8H\/BzN\/Z5OSvn2Tx3juMGkCh29tl970OlwXj1YbaleHsMVBJyFIudcdyizYj\/NLDI\/o+qAVqKBqKYqQQEkYbSlOEm647+j+B7G3t8o8nnuPMn18kS8eorUPqqBAd1AODRskjJZJiEkuZEmqKpYzmBkkN5BH+4fWG3f2OxUFh0VdKqZRakRAkKlYEE+HoofFFEDvzPU6\/dIZTf32FM8+\/ShMdYy9EKjiVrJXQAAtYDLlSUpA8cBPMBE0JyS0kg5whN+At\/sChN9nZr8znHVs7lXkfbB\/A7gL2O1j0Qunhpz\/+OXs7e2y8u8XOuS1yKSQKiUqrlZH1TFIwayuLcYVpkEJobbAZUjrUA8uG1aDrDGNKd2Bom9g8P6ajRe665WjM2sT+\/mCV3DIhTsUJSRScqpm+QoiCOuPxhDY37O\/tsTqdEn2PUWkUJhmmGY5NgssOFdaPGpOJ8NrZDeZ7e5TcsXb5FTz2+6c5dfaAWVtZX59y0AVPndxBVtYOh6aMqGEp4U2LqOG5wVPGUoOqkZsW80TyzO23fZyjq6vsbM\/Jnil9ZeyJ6bghRWHWZjZeO0Pu5hxZUQ5NIGlPsp61Sc9NJ8ZsnJ3zztktpiNw6Tl2+z3s\/v0P+M2f\/yrNyoy+QjuZ4qlB1RFzkidEDGHIOxKClEq\/0lJnK6ysBdtbO2gENA3X3XglJ0++yqKDbVZZG62xk5ScjDZVqlX2m0pnaxz+AGzMXyGNhMZ7+vO7rHzoXrxgzHf3wBKL7W1EdgAdvFAMBnzwQiClQqm8e7bywv4+1EI2Q0ogETz9zNNYFbQWxikzTc7UndUmMU6Ca2GcYb7fcdtHZrxxXqnnemYjmG68yY33fQw\/f\/qfLLoO9UwtFW9G8P4\/BAJ4SqgqEkHUQGMAoLUifX\/R4NfuABdj4+3XaHOLi7C6MuPIbA2Pyu7uFodmU\/7yovPW6RFvb+5z6r19RjlodMFn+zeQo9ffHLUU+sWC2h2QJlMkhLLYR1MzpIPa47kFGDw3oDVQBIkLHnrw4hqBUDERXAUXUCrZDNWg7zuEwvrUWW2Nc3uDzW0cbj8O\/wI5fjvJRZLhiwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-stringer_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA set of useful, string-related helpers:\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-stringer_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHlklEQVRIiX2X268lRRXGf+tS1b33PnufM7fDneGuiDoRJZIQA5IgEIMGjRc0IfqgDxof\/BN8980Ho\/FZn4wviIlGI5BAAmi8wIBEYAYYBgLMnMs+t91dtXzoPYNRY6Wru5Ouy1ffV6vW13L1NbdElIoQRK0QFXOnlkIQqAiuhqmS3FksDqi1QgREIICIIAxFRBARILhQImJZ68UnUREBFcHcMRGaZoRHCBFBqQUJEKCUgjAMrKoDUFGolaRKv5wAkWU7LrYXARBU5f3J5cK7ELUgakS8388QkhpaC36hg6AElQjQEESHyQhB1QGhLxVDUIQQxVSXbAwLQYZvy2t5M9yNxaJDBCKGBYoouuxrqrgZSRUPUahlSfDACBFICKqGm2IimChuikSl1kqtZRjAfVh9rajaRalUhKiVIFhdW2Pj3HmgQgjiELWgKhBgpiQzkl8AJIAK1CVbAiK6lMFQFDMjW2LcNBA9u3t7NO40ycmeqLVSSk9fCgjDfqkVgMsuWedgew7UgbQIVPMSlGI6SJxNcdFERIeEgVT04iYV1Gyg1hIqioqh4kyaCYdGq5yfb6KhRAkaT6hniEKI0PfdwJDA8auv5q1TryESSICpIASQEImlZEoyxTVl+mAZMXYhVkB1YEcNUyO7ky2hGCKJJieuPbbCzu6cg67jkkvXWeztMt\/axkTIrtSo3PGZO3n55IvMckMQQ2QBqoMoqiACyZTshocmJC3lQqgBJoqqoaK4Oa5G9oyLkMxJkkjacPjwKl\/4xpc4fGiNP\/7yN7x++nWsWSH5EE3T1RXuuPtOnn\/sSdbaEVCIiAGMCipgJpgOrLkLTm6Q3qk1lvoCKKKGmiNiuDmqCTdb1sRd93+Kh777FV569gUe+dEvSBRuuuxyola2d+Z4Nr7+g+\/z7K8e5crZjEXpqNETUQbpdBldadhDOSmm4NJOqF1Bah3OoQDEEHHUEskcVSOZ06bM2J1vfueL3PvgXczPb\/PoTx4h64hD45brb7icnc132dzd5+7vfY2kQj39Bh+8bJ353i77\/YJSe0rtQCvJDTPBXUkupGS4jGfIoodSCRj2CIpIQsUQT2R12pQYp8RDD9\/DvQ\/eBcDfnniORMN0ZcrKuGH73B7HP\/FRPvPAHXjb8NQPf8b60VU0Oja3gr5PlBgAhVRUg5QddyElxbPgMppC7qGAhEIogoI4aMLUSebklDhx4lo+9+VPX0wJW2\/POXLJMa44vs7xW67iqluvY3J0RkTw+m8fZzLKlLpC2\/TkpoJUanT0\/YKoHWqgGpgbuXE8Cc50FfpChAxgYoguw3FNKI4sD8CHv33\/xZMZ4L5v3cN\/lojgzON\/YvHOuxy75ggHm5luZ4OZdohVxIJaO2p\/gNCjJpgrKWe8aXCdTkmFIc+geCgJI4uTw8hqZJRbT1zNFVcc\/i8A\/15qKbz8u2fYfull2onSro45dvOV9JsTts4KKguEDiQTJSFShwhLjuWMNSN8ujalFGDJkCF4OK0YLUqL0VTh9k\/e8H\/BzN\/Z5OSvn2Tx3juMGkCh29tl970OlwXj1YbaleHsMVBJyFIudcdyizYj\/NLDI\/o+qAVqKBqKYqQQEkYbSlOEm647+j+B7G3t8o8nnuPMn18kS8eorUPqqBAd1AODRskjJZJiEkuZEmqKpYzmBkkN5BH+4fWG3f2OxUFh0VdKqZRakRAkKlYEE+HoofFFEDvzPU6\/dIZTf32FM8+\/ShMdYy9EKjiVrJXQAAtYDLlSUpA8cBPMBE0JyS0kg5whN+At\/sChN9nZr8znHVs7lXkfbB\/A7gL2O1j0Qunhpz\/+OXs7e2y8u8XOuS1yKSQKiUqrlZH1TFIwayuLcYVpkEJobbAZUjrUA8uG1aDrDGNKd2Bom9g8P6ajRe665WjM2sT+\/mCV3DIhTsUJSRScqpm+QoiCOuPxhDY37O\/tsTqdEn2PUWkUJhmmGY5NgssOFdaPGpOJ8NrZDeZ7e5TcsXb5FTz2+6c5dfaAWVtZX59y0AVPndxBVtYOh6aMqGEp4U2LqOG5wVPGUoOqkZsW80TyzO23fZyjq6vsbM\/Jnil9ZeyJ6bghRWHWZjZeO0Pu5hxZUQ5NIGlPsp61Sc9NJ8ZsnJ3zztktpiNw6Tl2+z3s\/v0P+M2f\/yrNyoy+QjuZ4qlB1RFzkidEDGHIOxKClEq\/0lJnK6ysBdtbO2gENA3X3XglJ0++yqKDbVZZG62xk5ScjDZVqlX2m0pnaxz+AGzMXyGNhMZ7+vO7rHzoXrxgzHf3wBKL7W1EdgAdvFAMBnzwQiClQqm8e7bywv4+1EI2Q0ogETz9zNNYFbQWxikzTc7UndUmMU6Ca2GcYb7fcdtHZrxxXqnnemYjmG68yY33fQw\/f\/qfLLoO9UwtFW9G8P4\/BAJ4SqgqEkHUQGMAoLUifX\/R4NfuABdj4+3XaHOLi7C6MuPIbA2Pyu7uFodmU\/7yovPW6RFvb+5z6r19RjlodMFn+zeQo9ffHLUU+sWC2h2QJlMkhLLYR1MzpIPa47kFGDw3oDVQBIkLHnrw4hqBUDERXAUXUCrZDNWg7zuEwvrUWW2Nc3uDzW0cbj8O\/wI5fjvJRZLhiwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-stringer_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA set of useful, string-related helpers:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EconvertCase\u003C\/li\u003E\n\u003Cli\u003EcleanUrl\u003C\/li\u003E\n\u003Cli\u003EnumberInWords\u003C\/li\u003E\n\u003Cli\u003EtimeInWords\u003C\/li\u003E\n\u003Cli\u003EtimeDiff\u003C\/li\u003E\n\u003Cli\u003Elistify\u003C\/li\u003E\n\u003Cli\u003Ephone\u003C\/li\u003E\n\u003Cli\u003EarabicToRoman\u003C\/li\u003E\n\u003C\/ul\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["helper","language","library","numbers","php","string","time","words"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-stringer_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-stringer_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-stringer_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA set of useful, string-related helpers:\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-stringer_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-stringer_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA set of useful, string-related helpers:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EconvertCase\u003C\/li\u003E\n\u003Cli\u003EcleanUrl\u003C\/li\u003E\n\u003Cli\u003EnumberInWords\u003C\/li\u003E\n\u003Cli\u003EtimeInWords\u003C\/li\u003E\n\u003Cli\u003EtimeDiff\u003C\/li\u003E\n\u003Cli\u003Elistify\u003C\/li\u003E\n\u003Cli\u003Ephone\u003C\/li\u003E\n\u003Cli\u003EarabicToRoman\u003C\/li\u003E\n\u003C\/ul\u003E","words":14,"readTime":null,"lang":"en"}}},"projects\/avris-localisator":{"key":"projects\/avris-localisator","type":"article","published":true,"meta":{"createdAt":"2018-01-10T19:09:58+01:00","publishedAt":"2018-01-10T19:08:00+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Localisator","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-localisator"},"content":{"en":{"slug":"avris-localisator","title":"Avris Localisator","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-localisator_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHmUlEQVRIiX2Xya8dRxXGf2eo6r73vfsGx37GQ+wMEAgBLBCISBFKiJRJCBAgZgnBAhYgFvwJ7NmxQCDWsEJsGCQQiEFKJAiIIQkhIiGOY0xkx2+67973bnfVYdHXDgJEqSepq7q++r4653wt5267J6JUhCBqhaiYO7UUgkBFcDVMleTOYnFErRUiIAIBRARhaCKCiADBjRYRy7PevBMVEVARzB0ToWlGeIQQEZRakAABSikIw4dVdQAqCrWSVOmXEyCy7MfN\/iIAgqq8NrnceBaiFkSNiNfGGUJSQ2vBbwwQlKASARqC6DAZIag6IPSlYgiKEKKY6pKNYSHI8G55LC+Gu7FYdIhAxLBAEUWXY00VNyOp4iEKtSwJHhghAglB1XBTTAQTxU2RqNRaqbUMH3AfVl8rqnZTKhUhaiUI1jc22Lm+DVQIQRyiFlQFAsyUZEbyG4AEUIG6ZEtARJcyGIpiZmRLjJsGomc2n9O40yQne6LWSik9fSkgDPulVgBOndziaH8K1IG0CFTzEpRiOkicTXHRRESHhIFU9OYmFdRsoNYSKoqKoeKsNCtsjtbZnu6ioUQJGk+oZ4hCiND33cCQwPlz5\/jniy8hEkiAqSAEkBCJpWRKMsU1ZfpgGTF2I1ZAdWBHDVMju5MtoRgiiSYnbj+xysFsylHXcfJ1WyzmM6Z7+5gI2ZUalfsevp\/nn3mWtdwQxBBZgOogiiqIQDIlu+GhCUlLuRBqgImiaqgobo6rkT3jIiRzkiSSNhw7ts6HP\/tRjm1u8Ivv\/ZhLFy9hzSrJh2iarK9y34P38\/QvH2ejHQGFiBjAqKACZoLpwJq74OQG6Z1aY6kvgCJqqDkihpujmnCz5Zl44LH38MkvfZznnvwLP\/j6d0kU7jp1mqiV\/YMpno1Pf\/UrPPn9H3F2bY1F6ajRE1EG6XQZXWnYQzkppuDSrlC7gtQ65KEAxBBx1BLJHFUjmdOmzNidz33xIzzyoQeYbu\/zo2\/+gKwjNsctd77+NAe719idHfLglz9FUqFefJk3ndpiOp9x2C8otafUDrSS3DAT3JXkQkqGy3gNWfRQKgHDHkERSagY4omsTpsS45T45Gce4pEPPQDAn379FImGyeqE1XHD\/vU559\/5Nh5+\/3142\/DE177N1vF1NDp294K+T5QYAIVUVIOUHXchJcWz4DKaQO6hgIRCKIKCOGjC1Enm5JS4cOF2PvCx994sCXuvTLnl5AnOnN\/i\/D23cus77mDl+BoRwaWf\/IqVUabUVdqmJzcVpFKjo+8XRO1QA9XA3MiN40lwJuvQFyJkABNDdBmOa0JxZJkAP\/OFx25mZoBHP\/8Q\/9kigsu\/+h2Lq9c4cdstHO1muoMd1rRDrCIW1NpR+yOEHjXBXEk5402D62RCKgx1BsVDSRhZnBxGViOjvOPCOc6cOfZfAP691VJ4\/qe\/Zf+552lXlHZ9zIm7z9LvrrB3RVBZIHQgmSgJkTpEWHIsZ6wZ4ZONCaUAS4YMwcNpxWhRWoymCve++\/X\/F8z06i7P\/PBxFq9eZdQACt18xuzVDpcF4\/WG2pUh9xioJGQpl7pjuUWbEf66YyP6PqgFaigaimKkEBJGG0pThLvuOP4\/gcz3Zvz1109x+ffPkqVj1NahdFSIDuqRQaPkkRJJMYmlTAk1xVJGc4OkBvIIf8tWw+ywY3FUWPSVUiqlViQEiYoVwUQ4vjm+CeJgOufic5d58Y8vcPnpv9NEx9gLkQpOJWslNMACFkOtlBQkD9wEM0FTQnILySBnyA14i79\/8x8cHFam0469g8q0D\/aPYLaAww4WvVB6+NY3vsP8YM7OtT0Oru+RSyFRSFRarYysZyUFa21lMa4wCVIIrQ02Q0qHemDZsBp0nWFM6I4MbRO722M6WuSBe47HWps4PBysklsmxKk4IYmCUzXTV0CN1Izouh7XIWtnd86dPc3OtWskKquNMMlwfFw5tVnYOm6srAgvXdlhOp9TcsfG6TP88me\/4cUrR6y1la2tCUdd8MQzB8jqxrHQlBE1LCW8aRE1PDd4ylhqUDVy07Kysspdd9xOm5xx07K9O+eOs6c4trnJwd4B+9u7vOH8KepszkiD1grl6iU2VyBpT7KejZWeuy6M2bky5eqVPSYjcOk5ce9DzP78c\/zuD36CZnWNvkK7MsFTg6oj5iRPiBjCUHeyGm+89STz+SEuwnpXEIK5GfPGOXHnSa4fLWjaFjOhO5gR6Sw5GW2qVKscNpXONjj2RtiZvkAaCY339NszVt\/8CF4wprM5WGKxv4\/IAaCDF4rBgA9eCKRUrly6iNRKPTyEWshmSAkkAquFxOCNxykzSc7EnfUmMU6Ca2GcYXrY8a63rvHytlKv96yNYLLzD97w6Nvx7Yt\/Y9F1qGdqqXgzgtf+IRDAU0JVkQiiBhoDAK0V6fubBr92R7gYO6+8RJvbgcXVNW5Z28CjMpvtsbk24Q\/POv+8OOKV3UNefPWQUQ4aXfC+\/mXk+J13Ry2FfrGgdkeklQkSQlkcoqkZykHt8dwCDJ4b0BoogsQNDz14cY1AqJgIroILKJVshmrQ9x1CYWvirLfG9flgcxuHe8\/DvwDS\/jrrbBLcdQAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-localisator_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA neat tool to localise messages.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-localisator_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHmUlEQVRIiX2Xya8dRxXGf2eo6r73vfsGx37GQ+wMEAgBLBCISBFKiJRJCBAgZgnBAhYgFvwJ7NmxQCDWsEJsGCQQiEFKJAiIIQkhIiGOY0xkx2+67973bnfVYdHXDgJEqSepq7q++r4653wt5267J6JUhCBqhaiYO7UUgkBFcDVMleTOYnFErRUiIAIBRARhaCKCiADBjRYRy7PevBMVEVARzB0ToWlGeIQQEZRakAABSikIw4dVdQAqCrWSVOmXEyCy7MfN\/iIAgqq8NrnceBaiFkSNiNfGGUJSQ2vBbwwQlKASARqC6DAZIag6IPSlYgiKEKKY6pKNYSHI8G55LC+Gu7FYdIhAxLBAEUWXY00VNyOp4iEKtSwJHhghAglB1XBTTAQTxU2RqNRaqbUMH3AfVl8rqnZTKhUhaiUI1jc22Lm+DVQIQRyiFlQFAsyUZEbyG4AEUIG6ZEtARJcyGIpiZmRLjJsGomc2n9O40yQne6LWSik9fSkgDPulVgBOndziaH8K1IG0CFTzEpRiOkicTXHRRESHhIFU9OYmFdRsoNYSKoqKoeKsNCtsjtbZnu6ioUQJGk+oZ4hCiND33cCQwPlz5\/jniy8hEkiAqSAEkBCJpWRKMsU1ZfpgGTF2I1ZAdWBHDVMju5MtoRgiiSYnbj+xysFsylHXcfJ1WyzmM6Z7+5gI2ZUalfsevp\/nn3mWtdwQxBBZgOogiiqIQDIlu+GhCUlLuRBqgImiaqgobo6rkT3jIiRzkiSSNhw7ts6HP\/tRjm1u8Ivv\/ZhLFy9hzSrJh2iarK9y34P38\/QvH2ejHQGFiBjAqKACZoLpwJq74OQG6Z1aY6kvgCJqqDkihpujmnCz5Zl44LH38MkvfZznnvwLP\/j6d0kU7jp1mqiV\/YMpno1Pf\/UrPPn9H3F2bY1F6ajRE1EG6XQZXWnYQzkppuDSrlC7gtQ65KEAxBBx1BLJHFUjmdOmzNidz33xIzzyoQeYbu\/zo2\/+gKwjNsctd77+NAe719idHfLglz9FUqFefJk3ndpiOp9x2C8otafUDrSS3DAT3JXkQkqGy3gNWfRQKgHDHkERSagY4omsTpsS45T45Gce4pEPPQDAn379FImGyeqE1XHD\/vU559\/5Nh5+\/3142\/DE177N1vF1NDp294K+T5QYAIVUVIOUHXchJcWz4DKaQO6hgIRCKIKCOGjC1Enm5JS4cOF2PvCx994sCXuvTLnl5AnOnN\/i\/D23cus77mDl+BoRwaWf\/IqVUabUVdqmJzcVpFKjo+8XRO1QA9XA3MiN40lwJuvQFyJkABNDdBmOa0JxZJkAP\/OFx25mZoBHP\/8Q\/9kigsu\/+h2Lq9c4cdstHO1muoMd1rRDrCIW1NpR+yOEHjXBXEk5402D62RCKgx1BsVDSRhZnBxGViOjvOPCOc6cOfZfAP691VJ4\/qe\/Zf+552lXlHZ9zIm7z9LvrrB3RVBZIHQgmSgJkTpEWHIsZ6wZ4ZONCaUAS4YMwcNpxWhRWoymCve++\/X\/F8z06i7P\/PBxFq9eZdQACt18xuzVDpcF4\/WG2pUh9xioJGQpl7pjuUWbEf66YyP6PqgFaigaimKkEBJGG0pThLvuOP4\/gcz3Zvz1109x+ffPkqVj1NahdFSIDuqRQaPkkRJJMYmlTAk1xVJGc4OkBvIIf8tWw+ywY3FUWPSVUiqlViQEiYoVwUQ4vjm+CeJgOufic5d58Y8vcPnpv9NEx9gLkQpOJWslNMACFkOtlBQkD9wEM0FTQnILySBnyA14i79\/8x8cHFam0469g8q0D\/aPYLaAww4WvVB6+NY3vsP8YM7OtT0Oru+RSyFRSFRarYysZyUFa21lMa4wCVIIrQ02Q0qHemDZsBp0nWFM6I4MbRO722M6WuSBe47HWps4PBysklsmxKk4IYmCUzXTV0CN1Izouh7XIWtnd86dPc3OtWskKquNMMlwfFw5tVnYOm6srAgvXdlhOp9TcsfG6TP88me\/4cUrR6y1la2tCUdd8MQzB8jqxrHQlBE1LCW8aRE1PDd4ylhqUDVy07Kysspdd9xOm5xx07K9O+eOs6c4trnJwd4B+9u7vOH8KepszkiD1grl6iU2VyBpT7KejZWeuy6M2bky5eqVPSYjcOk5ce9DzP78c\/zuD36CZnWNvkK7MsFTg6oj5iRPiBjCUHeyGm+89STz+SEuwnpXEIK5GfPGOXHnSa4fLWjaFjOhO5gR6Sw5GW2qVKscNpXONjj2RtiZvkAaCY339NszVt\/8CF4wprM5WGKxv4\/IAaCDF4rBgA9eCKRUrly6iNRKPTyEWshmSAkkAquFxOCNxykzSc7EnfUmMU6Ca2GcYXrY8a63rvHytlKv96yNYLLzD97w6Nvx7Yt\/Y9F1qGdqqXgzgtf+IRDAU0JVkQiiBhoDAK0V6fubBr92R7gYO6+8RJvbgcXVNW5Z28CjMpvtsbk24Q\/POv+8OOKV3UNefPWQUQ4aXfC+\/mXk+J13Ry2FfrGgdkeklQkSQlkcoqkZykHt8dwCDJ4b0BoogsQNDz14cY1AqJgIroILKJVshmrQ9x1CYWvirLfG9flgcxuHe8\/DvwDS\/jrrbBLcdQAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-localisator_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA neat tool to localise messages.\u003C\/p\u003E\n\u003Cp\u003ELocalisator uses three elements to translate your messages:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELocaleOrder \u2013 decides which language(s) should be used,\u003C\/li\u003E\n\u003Cli\u003EProviders \u2013 fetches a translation for a specific key from a source (file, database, cache, ...),\u003C\/li\u003E\n\u003Cli\u003ETransformers \u2013 modifies the translation with more advanced logic (pluralisation, fallback, nesting, ...).\u003C\/li\u003E\n\u003C\/ul\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["i18n","language","library","locale","localisation","localization","php","translation"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-localisator_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-localisator_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-localisator_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA neat tool to localise messages.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-localisator_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-localisator_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA neat tool to localise messages.\u003C\/p\u003E\n\u003Cp\u003ELocalisator uses three elements to translate your messages:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELocaleOrder \u2013 decides which language(s) should be used,\u003C\/li\u003E\n\u003Cli\u003EProviders \u2013 fetches a translation for a specific key from a source (file, database, cache, ...),\u003C\/li\u003E\n\u003Cli\u003ETransformers \u2013 modifies the translation with more advanced logic (pluralisation, fallback, nesting, ...).\u003C\/li\u003E\n\u003C\/ul\u003E","words":47,"readTime":null,"lang":"en"}}},"projects\/avris-container":{"key":"projects\/avris-container","type":"article","published":true,"meta":{"createdAt":"2018-01-09T20:22:03+01:00","publishedAt":"2018-01-09T20:21:00+01:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Container","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-container"},"content":{"en":{"slug":"avris-container","title":"Avris Container","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-container_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHlklEQVRIiX2XyY8kRxXGf2+JyKyqruplZnq8zoA3MAYsNmFkIYwlLwgBAsRikBAc4ADiwJ\/AnRsHBOIMJ8TFgAQCgS3ZEptYjbEweLwNlsczvVR3V1dmxOOQNW0EiFBGZkoZyxffFy\/el3LuNXdElIoQRK0QFXOnlkIQqAiuhqmS3Fkuj6m1QgREIICIIAxFRBARILhaImJV68mTqIiAimDumAhNM8IjhIig1IIECFBKQRgGVtUBqCjUSlKlX02AyKodJ+1FAARVeXVyufouRC2IGhGv9jOEpIbWgl\/tIChBJQI0BNFhMkJQdUDoS8UQFCFEMdUVG8NCkOHb6lrdDHdjuewQgYhhgSKKrvqaKm5GUsVDFGpZETwwQgQSgqrhppgIJoqbIlGptVJrGQZwH1ZfK6p2IpWKELUSBOsbG+xcvgJUCEEcohZUBQLMlGRG8quABFCBumJLQERXMhiKYmZkS4ybBqLn8OiIxp0mOdkTtVZK6elLAWHYL7UCcO3ZbY7350AdSItANa9AKaaDxNkUF01EdEgYSEVPNqmgZgO1llBRVAwVZ9JM2Bytc2W+i4YSJWg8oZ4hCiFC33cDQwLnz53jn888i0ggAaaCEEBCJFaSKckU15Tpg1XE2NVYAdWBHTVMjexOtoRiiCSanHjtmTUODuccdx1nr9lmeXTIfG8fEyG7UqNy9\/3v4eknnmSWG4IYIgtQHURRBRFIpmQ3PDQhaSUXQg0wUVQNFcXNcTWyZ1yEZE6SRNKGra11PvLZj7G1ucHPv\/cjnrvwHNaskXyIpun6Gnff+x7+\/IvH2GhHQCEiBjAqqICZYDqw5i44uUF6p9ZY6QugiBpqjojh5qgm3GxVE\/e879089KVP8NSv\/8LDX\/8uicJt115H1Mr+wRzPxqe\/+hV+\/f0fcsNsxrJ01OiJKIN0uoquNOyhnBRTcGkn1K4gtQ7nUABiiDhqiWSOqpHMaVNm7M7nvvhRHvjwPcyv7PPDbz5M1hGb45abb7mOg91L7B4uuPfLnyKpUC88z+uv3WZ+dMiiX1JqT6kdaCW5YSa4K8mFlAyX8QxZ9lAqAcMeQRFJqBjiiaxOmxLjlHjoM\/fxwIfvAeAPj\/6JRMN0bcrauGH\/8hHn3\/5m7v\/A3Xjb8PjXvs326XU0Onb3gr5PlBgAhVRUg5QddyElxbPgMppC7qGAhEIogoI4aMLUSebklLjzztfywY+\/9yQl7L0059TZM1x\/fpvzd9zIjW+9icnpGRHBcz9+hMkoU+oabdOTmwpSqdHR90uidqiBamBu5MbxJDjTdegLETKAiSG6DMc1oTiyOgA\/84X3nZzMAA9+\/j7+s0QELzzyG5YvX+LMa05xvJvpDnaYaYdYRSyotaP2xwg9aoK5knLGmwbX6ZRUGPIMioeSMLI4OYysRkZ5653nuP76rf8C8O+llsLTP\/kV+089TTtR2vUxZ26\/gX53wt5FQWWJ0IFkoiRE6hBhybGcsWaETzemlAKsGDIED6cVo0VpMZoq3PXOW\/4vmPnLuzzxg8dYvvIyowZQ6I4OOXylw2XJeL2hdmU4ewxUErKSS92x3KLNCL9ma0TfB7VADUVDUYwUQsJoQ2mKcNtNp\/8nkKO9Q\/766J944bdPkqVj1NYhdVSIDuqxQaPkkRJJMYmVTAk1xVJGc4OkBvIIf+N2w+GiY3lcWPaVUiqlViQEiYoVwUQ4vTk+AXEwP+LCUy\/wzO\/\/zgt\/\/gdNdIy9EKngVLJWQgMsYDnkSklB8sBNMBM0JSS3kAxyhtyAt\/gHNl\/kYFGZzzv2DirzPtg\/hsMlLDpY9kLp4Vvf+A5HB0fsXNrj4PIeuRQShUSl1crIeiYpmLWV5bjCNEghtDbYDCkd6oFlw2rQdYYxpTs2tE3sXhnT0SL33HE6Zm1isRisklsmxKk4IYmCUzXTVwhR1BObm6fY29nFzWhSovGES2VrfQLHB0wznJkE124Wtk8bk4nw7MUd5kdHlNyxcd31\/OKnv+SZi8fM2sr29pTjLnj8iQNkbWMrNGVEDUsJb1pEDc8NnjKWGlSN3LSYJ971trdw8dIut95wltJVmmZE45mttTGH+wdMsjNrE9Ns7D1\/gTVZsDmBpD3JejYmPbfdOWbn4pyXL+4xHYFLz5m77uPwjz\/Db\/\/QJ2nWZvQV2skUTw2qjpiTPCFiCEPekRB8NuGmc7A1m7C7e0DfdUzHI3p3sAn7xx21KseLYFG3kFbJyWhTpVpl0VQ622DrdbAz\/ztpJDTe0185ZO0ND+AFY354BJZY7u8jcgDo4IViMOCDFwIplUsvVqRW6mIBtZDNkBJIBFYLicEbj1NmmpypO+tNYpwE18I4w3zR8Y43zXj+ilIv98xGMN15kVsffAt+5cLfWHYd6plaKt6M4NV\/CATwlFBVJIKogcYAQGtF+v7E4NfuGBdj56VnaXOLi7C+NuPUbAOPyuHhHpuzKb970vnnhREv7S545pUFoxw0uuT9\/fPI6Ztvj1oK\/XJJ7Y5JkykSQlku0NQM6aD2eG4BBs8NaA0UQeKqhx68uEYgVEwEV8EFlEo2QzXo+w6hsD111lvj8tFgcxuHu87DvwCrEznQnIJIIAAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-container_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA dependency injection container with autowiring\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-container_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHlklEQVRIiX2XyY8kRxXGf2+JyKyqruplZnq8zoA3MAYsNmFkIYwlLwgBAsRikBAc4ADiwJ\/AnRsHBOIMJ8TFgAQCgS3ZEptYjbEweLwNlsczvVR3V1dmxOOQNW0EiFBGZkoZyxffFy\/el3LuNXdElIoQRK0QFXOnlkIQqAiuhqmS3Fkuj6m1QgREIICIIAxFRBARILhaImJV68mTqIiAimDumAhNM8IjhIig1IIECFBKQRgGVtUBqCjUSlKlX02AyKodJ+1FAARVeXVyufouRC2IGhGv9jOEpIbWgl\/tIChBJQI0BNFhMkJQdUDoS8UQFCFEMdUVG8NCkOHb6lrdDHdjuewQgYhhgSKKrvqaKm5GUsVDFGpZETwwQgQSgqrhppgIJoqbIlGptVJrGQZwH1ZfK6p2IpWKELUSBOsbG+xcvgJUCEEcohZUBQLMlGRG8quABFCBumJLQERXMhiKYmZkS4ybBqLn8OiIxp0mOdkTtVZK6elLAWHYL7UCcO3ZbY7350AdSItANa9AKaaDxNkUF01EdEgYSEVPNqmgZgO1llBRVAwVZ9JM2Bytc2W+i4YSJWg8oZ4hCiFC33cDQwLnz53jn888i0ggAaaCEEBCJFaSKckU15Tpg1XE2NVYAdWBHTVMjexOtoRiiCSanHjtmTUODuccdx1nr9lmeXTIfG8fEyG7UqNy9\/3v4eknnmSWG4IYIgtQHURRBRFIpmQ3PDQhaSUXQg0wUVQNFcXNcTWyZ1yEZE6SRNKGra11PvLZj7G1ucHPv\/cjnrvwHNaskXyIpun6Gnff+x7+\/IvH2GhHQCEiBjAqqICZYDqw5i44uUF6p9ZY6QugiBpqjojh5qgm3GxVE\/e879089KVP8NSv\/8LDX\/8uicJt115H1Mr+wRzPxqe\/+hV+\/f0fcsNsxrJ01OiJKIN0uoquNOyhnBRTcGkn1K4gtQ7nUABiiDhqiWSOqpHMaVNm7M7nvvhRHvjwPcyv7PPDbz5M1hGb45abb7mOg91L7B4uuPfLnyKpUC88z+uv3WZ+dMiiX1JqT6kdaCW5YSa4K8mFlAyX8QxZ9lAqAcMeQRFJqBjiiaxOmxLjlHjoM\/fxwIfvAeAPj\/6JRMN0bcrauGH\/8hHn3\/5m7v\/A3Xjb8PjXvs326XU0Onb3gr5PlBgAhVRUg5QddyElxbPgMppC7qGAhEIogoI4aMLUSebklLjzztfywY+\/9yQl7L0059TZM1x\/fpvzd9zIjW+9icnpGRHBcz9+hMkoU+oabdOTmwpSqdHR90uidqiBamBu5MbxJDjTdegLETKAiSG6DMc1oTiyOgA\/84X3nZzMAA9+\/j7+s0QELzzyG5YvX+LMa05xvJvpDnaYaYdYRSyotaP2xwg9aoK5knLGmwbX6ZRUGPIMioeSMLI4OYysRkZ5653nuP76rf8C8O+llsLTP\/kV+089TTtR2vUxZ26\/gX53wt5FQWWJ0IFkoiRE6hBhybGcsWaETzemlAKsGDIED6cVo0VpMZoq3PXOW\/4vmPnLuzzxg8dYvvIyowZQ6I4OOXylw2XJeL2hdmU4ewxUErKSS92x3KLNCL9ma0TfB7VADUVDUYwUQsJoQ2mKcNtNp\/8nkKO9Q\/766J944bdPkqVj1NYhdVSIDuqxQaPkkRJJMYmVTAk1xVJGc4OkBvIIf+N2w+GiY3lcWPaVUiqlViQEiYoVwUQ4vTk+AXEwP+LCUy\/wzO\/\/zgt\/\/gdNdIy9EKngVLJWQgMsYDnkSklB8sBNMBM0JSS3kAxyhtyAt\/gHNl\/kYFGZzzv2DirzPtg\/hsMlLDpY9kLp4Vvf+A5HB0fsXNrj4PIeuRQShUSl1crIeiYpmLWV5bjCNEghtDbYDCkd6oFlw2rQdYYxpTs2tE3sXhnT0SL33HE6Zm1isRisklsmxKk4IYmCUzXTVwhR1BObm6fY29nFzWhSovGES2VrfQLHB0wznJkE124Wtk8bk4nw7MUd5kdHlNyxcd31\/OKnv+SZi8fM2sr29pTjLnj8iQNkbWMrNGVEDUsJb1pEDc8NnjKWGlSN3LSYJ971trdw8dIut95wltJVmmZE45mttTGH+wdMsjNrE9Ns7D1\/gTVZsDmBpD3JejYmPbfdOWbn4pyXL+4xHYFLz5m77uPwjz\/Db\/\/QJ2nWZvQV2skUTw2qjpiTPCFiCEPekRB8NuGmc7A1m7C7e0DfdUzHI3p3sAn7xx21KseLYFG3kFbJyWhTpVpl0VQ622DrdbAz\/ztpJDTe0185ZO0ND+AFY354BJZY7u8jcgDo4IViMOCDFwIplUsvVqRW6mIBtZDNkBJIBFYLicEbj1NmmpypO+tNYpwE18I4w3zR8Y43zXj+ilIv98xGMN15kVsffAt+5cLfWHYd6plaKt6M4NV\/CATwlFBVJIKogcYAQGtF+v7E4NfuGBdj56VnaXOLi7C+NuPUbAOPyuHhHpuzKb970vnnhREv7S545pUFoxw0uuT9\/fPI6Ztvj1oK\/XJJ7Y5JkykSQlku0NQM6aD2eG4BBs8NaA0UQeKqhx68uEYgVEwEV8EFlEo2QzXo+w6hsD111lvj8tFgcxuHu87DvwCrEznQnIJIIAAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-container_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA dependency injection container with autowiring\u003C\/p\u003E\n\u003Ch3\u003EInstallation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs bash border\u0022\u003Ecomposer require avris\/container\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EUsage\u003C\/h3\u003E\n\u003Ch4\u003EBasics\u003C\/h4\u003E\n\u003Cp\u003EContainer resolves dependencies between defined services,\nin order to simplify the development process, avoid duplication of code,\nfacilitate interoperability and improve maintainability and testability.\nSee: \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Dependency_injection\u0022\u003EDependency Injection pattern\u003C\/a\u003E.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$parameterProvider = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E SimpleParameterProvider([\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027ROOT_DIR\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-keyword\u0022\u003E__DIR__\u003C\/span\u003E]);\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ parameter provider is optional\u003C\/span\u003E\n\n$container = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Container($parameterProvider);\n\n$container-\u0026gt;set(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027number\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-number\u0022\u003E4\u003C\/span\u003E);\n$container-\u0026gt;set(Foo::class, \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Foo);\n$container-\u0026gt;setDefinition(Bar::class, [\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027arguments\u0027\u003C\/span\u003E =\u0026gt; [\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027$foo\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027@\u0027\u003C\/span\u003E . Foo::class,\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027$dir\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027%ROOT_DIR%\/bar\u0027\u003C\/span\u003E,\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027$number\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027@number\u0027\u003C\/span\u003E,\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027$float\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-number\u0022\u003E69.123\u003C\/span\u003E,\n    ],\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027public\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E,\n]);\n$container-\u0026gt;setDefinition(BarInterface::class, Bar::class); \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ alias\u003C\/span\u003E\n\n$container-\u0026gt;get(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027number\u0027\u003C\/span\u003E);              \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ 4\u003C\/span\u003E\n$container-\u0026gt;get(Foo::class);            \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ new Foo\u003C\/span\u003E\n$container-\u0026gt;get(BarInterface::class);   \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ new Bar(new Foo, __DIR__ . \u0027\/bar\u0027, 4, 69.123)\u003C\/span\u003E\n$container-\u0026gt;getParameter(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027ROOT_DIR\u0027\u003C\/span\u003E);   \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ __DIR__\u003C\/span\u003E\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch4\u003EOptions\u003C\/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Eclass\u003C\/code\u003E \u2013 the class of the service, will default to the service name if not given.\nIf the given class implements the \u003Ccode\u003EResolver\u003C\/code\u003E interface, it will be instantiated,\nand it\u2019s \u003Ccode\u003Eresolve\u003C\/code\u003E method executed to provide an actual value to be put in the container.\u003C\/p\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Earguments\u003C\/code\u003E \u2013 constructor arguments.\u003C\/p\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Ecalls\u003C\/code\u003E \u2013 method calls to be executed right after constructing the service (setter injection etc.)\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E\u0027calls\u0027 =\u0026gt; [\n    [\u0027setLogger\u0027, [\u0027@logger\u0027]],\n    [\u0027registerListener\u0027, [\u0027@listenerA\u0027]],\n    [\u0027registerListener\u0027, [\u0027@listenerB\u0027]],\n],\u003C\/code\u003E\u003C\/pre\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Etags\u003C\/code\u003E \u2013 an array of string that help group similar services together; tagged services can be injected with \u003Ccode\u003E#tagName\u003C\/code\u003E:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E$container-\u0026gt;setDefinition(HandlerA::class, [\u0027tags\u0027 =\u0026gt; \u0027handler\u0027]);\n$container-\u0026gt;setDefinition(HandlerB::class, [\u0027tags\u0027 =\u0026gt; \u0027handler\u0027]);\n$container-\u0026gt;setDefinition(HandlerC::class, [\u0027tags\u0027 =\u0026gt; \u0027handler\u0027]);\n$container-\u0026gt;setDefinition(Manager::class, [\u0027arguments\u0027 =\u0026gt; [\u0027$handlers\u0027 =\u0026gt; \u0027#handler\u0027]]);\u003C\/code\u003E\u003C\/pre\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Efactory\u003C\/code\u003E -- determines if each \u003Ccode\u003Eget\u003C\/code\u003E should create a new service (\u003Ccode\u003Etrue\u003C\/code\u003E),\nor should one service be reused (\u003Ccode\u003Efalse\u003C\/code\u003E, default).\u003C\/p\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Eresolve\u003C\/code\u003E \u2013 instead of using \u003Ccode\u003Eclass\u003C\/code\u003E + \u003Ccode\u003Earguments\u003C\/code\u003E to construct the service,\nyou can use \u003Ccode\u003Eresolve\u003C\/code\u003E to define how it should be created:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E$container-\u0026gt;setDefinition(\u0027foo\u0027, [\u0027resolve\u0027 =\u0026gt; 4]); \/\/ 4\n$container-\u0026gt;setDefinition(\u0027language\u0027, [\u0027resolve\u0027 =\u0026gt; \u0027@Request.locale.language\u0027]); \/\/ $container-\u0026gt;get(\u0027Request\u0027)-\u0026gt;getLocale()-\u0026gt;getLanguage()\u003C\/code\u003E\u003C\/pre\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Epublic\u003C\/code\u003E \u2013 determines if the service should be accessible directly with \u003Ccode\u003Eget\u003C\/code\u003E,\nor can it only be injected into other services.\u003C\/p\u003E\n\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Ch4\u003EContainerCompiler: autowiring and autoconfiguration\u003C\/h4\u003E\n\u003Cp\u003EUsually it\u2019s obvious, which service should be injected into another.\nFor instance when your service has a constructor argument \u003Ccode\u003EPsr\\Cache\\CacheItemPoolInterface $cache\u003C\/code\u003E,\nand the container does have a service named \u003Ccode\u003EPsr\\Cache\\CacheItemPoolInterface\u003C\/code\u003E,\nthen explicitly writing \u003Ccode\u003E[\u0027arguments\u0027 =\u0026gt; [\u0027$cache\u0027 =\u0026gt; \u0027@Psr\\Cache\\CacheItemPoolInterface\u0027]\u003C\/code\u003E is redundant.\nYou can always specify the dependencies manually, then autowiring won\u0027t overwrite them.\u003C\/p\u003E\n\u003Cp\u003EAutowiring is not magic \u2013 it just follows simple rules to determine,\nwhich service should be injected into the constructor:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eif the argument is a class which is defined in the container, use this service,\u003C\/li\u003E\n\u003Cli\u003Eif the argument is a class which is not defined in the container,\ntry to autowire that class and create a private service out of it,\u003C\/li\u003E\n\u003Cli\u003Eif the argument is an array and its name ends with \u003Ccode\u003Es\u003C\/code\u003E (e.g. \u003Ccode\u003Earray $helpers\u003C\/code\u003E),\ninject an array of services with a specific tag (\u003Ccode\u003E#helper\u003C\/code\u003E).\u003C\/li\u003E\n\u003Cli\u003Eif the argument is of type \u003Ccode\u003EBag\u003C\/code\u003E, inject the config value with its name\n(e.g. \u003Ccode\u003EBag $localisation\u003C\/code\u003E -\u0026gt; \u003Ccode\u003E@config.localisation\u003C\/code\u003E),\u003C\/li\u003E\n\u003Cli\u003Eif its name starts with \u003Ccode\u003Eenv\u003C\/code\u003E, inject a parameter:\n(e.g. \u003Ccode\u003Estring $envCacheDir\u003C\/code\u003E -\u0026gt; \u003Ccode\u003E%CACHE_DIR%\u003C\/code\u003E)\u003C\/li\u003E\n\u003Cli\u003Eif none of the above is true, but there is a default value, just use it,\u003C\/li\u003E\n\u003Cli\u003Eif none of the above is true, throw an exception \u2013 this argument should be defined explicitly.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EAutoconfiguration is another way to make your life simpler.\nFor instance, if you\u2019re using \u003Ca href=\u0022https:\/\/twig.symfony.com\/\u0022\u003ETwig\u003C\/a\u003E,\nyou might want all the classes in your code that extend \u003Ccode\u003ETwig\\Extension\\AbstractExtension\u003C\/code\u003E\nto be automatically registered as twig extension.\nAutoconfiguration lets you define what default config (tags, public etc.) should be added to them.\u003C\/p\u003E\n\u003Cp\u003ETo use autowiring and autoconfiguration, run the \u003Ccode\u003EContainerCompiler\u003C\/code\u003E:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$container = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Container;\n\n$services = [\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027App\\\u0027 =\u0026gt; [\n        \u0027\u003C\/span\u003Edir\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027 =\u0026gt; \u0027\u003C\/span\u003E%MODULE_DIR%\/src\/\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027,\n        \u0027\u003C\/span\u003Eexclude\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027 =\u0026gt; [\u0027\u003C\/span\u003E\u003Cspan class=\u0022hljs-comment\u0022\u003E#^Entity\/#\u0027],\u003C\/span\u003E\n    ],\n\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027App\\Foo\u0027\u003C\/span\u003E =\u0026gt; [\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027arguments\u0027\u003C\/span\u003E =\u0026gt; [\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027$bar\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-number\u0022\u003E5\u003C\/span\u003E,\n        ],\n    ],\n\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027App\\Bar\u0027\u003C\/span\u003E =\u0026gt; [\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027public\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E,\n    ],\n];\n\n$autoconfiguration = [\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027Twig\\Extension\\AbstractExtension\u0027\u003C\/span\u003E =\u0026gt; [\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027tags\u0027\u003C\/span\u003E =\u0026gt; [\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027twigExtension\u0027\u003C\/span\u003E],\n    ],\n];\n\n$definitions = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E ContainerCompiler(\n    $container,\n    $services,\n    $autoconfiguration\n))-\u0026gt;compile();\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/** \u003Cspan class=\u0022hljs-doctag\u0022\u003E@var\u003C\/span\u003E ServiceDefinition $definition *\/\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Eforeach\u003C\/span\u003E ($definitions \u003Cspan class=\u0022hljs-keyword\u0022\u003Eas\u003C\/span\u003E $name =\u0026gt; $definition) {\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (!$container-\u0026gt;has($name)) {\n        $container-\u0026gt;setDefinition($name, $definition);\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIn this example, the whole \u003Ccode\u003E%MODULE_DIR%\/src\/\u003C\/code\u003E except for (the \u003Ccode\u003E\/src\/Entity\u003C\/code\u003E dir) will be scanned for PHP files\nand all the found classes will be autowired as private services. If some of them are not used and not public,\nthey will be removed from the container.\u003C\/p\u003E\n\u003Cp\u003ECompiling the container has \u003Cstrong\u003Eno impact on performance\u003C\/strong\u003E on production environment,\nas long as you \u003Cstrong\u003Ecache the result of \u003Ccode\u003Ecompile()\u003C\/code\u003E\u003C\/strong\u003E.\u003C\/p\u003E\n\u003Ch4\u003EServiceLocator\u003C\/h4\u003E\n\u003Cp\u003EService locator restricts access to services in the container only to a selected list of names:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$container = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Container();\n$container-\u0026gt;set(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027abc\u0027\u003C\/span\u003E);\n$container-\u0026gt;set(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027def\u0027\u003C\/span\u003E);\n$container-\u0026gt;set(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027secret\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027XYZ\u0027\u003C\/span\u003E);\n\n$locator = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E ServiceLocator($container, [\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E]);\n\n$locator-\u0026gt;get(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E);     \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ \u0027abc\u0027\u003C\/span\u003E\n$locator-\u0026gt;get(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E);     \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ \u0027def\u0027\u003C\/span\u003E\n$locator-\u0026gt;get(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027secret\u0027\u003C\/span\u003E);  \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ Exception\u003C\/span\u003E\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EContainerAssistedBuilder\u003C\/h3\u003E\n\u003Cp\u003E\u003Ccode\u003EContainerAssistedBuilder\u003C\/code\u003E can be used to join together a couple of \u003Ccode\u003EContainerBuilderExtension\u003C\/code\u003Es\nwhich encapsulate a set of service definitions that form a library together.\nFor an example, see \u003Ca href=\u0022https:\/\/gitlab.com\/Avris\/Localisator\u0022\u003EAvris Localisator\u003C\/a\u003E.\u003C\/p\u003E\n\u003Ch4\u003EMicrus\u003C\/h4\u003E\n\u003Cp\u003EThis container was originally built as a part of the \u003Ca href=\u0022https:\/\/micrus.avris.it\u0022\u003EMicrus framework\u003C\/a\u003E.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["autoconfiguration","autowiring","container","dependencies","dependency injection","library","php","programming"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-container_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-container_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-container_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA dependency injection container with autowiring\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-container_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-container_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA dependency injection container with autowiring\u003C\/p\u003E\n\u003Ch3\u003EInstallation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Ecomposer require avris\/container\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EUsage\u003C\/h3\u003E\n\u003Ch4\u003EBasics\u003C\/h4\u003E\n\u003Cp\u003EContainer resolves dependencies between defined services,\nin order to simplify the development process, avoid duplication of code,\nfacilitate interoperability and improve maintainability and testability.\nSee: \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Dependency_injection\u0022\u003EDependency Injection pattern\u003C\/a\u003E.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$parameterProvider = new SimpleParameterProvider([\u0027ROOT_DIR\u0027 =\u0026gt; __DIR__]);\n\/\/ parameter provider is optional\n\n$container = new Container($parameterProvider);\n\n$container-\u0026gt;set(\u0027number\u0027, 4);\n$container-\u0026gt;set(Foo::class, new Foo);\n$container-\u0026gt;setDefinition(Bar::class, [\n    \u0027arguments\u0027 =\u0026gt; [\n        \u0027$foo\u0027 =\u0026gt; \u0027@\u0027 . Foo::class,\n        \u0027$dir\u0027 =\u0026gt; \u0027%ROOT_DIR%\/bar\u0027,\n        \u0027$number\u0027 =\u0026gt; \u0027@number\u0027,\n        \u0027$float\u0027 =\u0026gt; 69.123,\n    ],\n    \u0027public\u0027 =\u0026gt; true,\n]);\n$container-\u0026gt;setDefinition(BarInterface::class, Bar::class); \/\/ alias\n\n$container-\u0026gt;get(\u0027number\u0027);              \/\/ 4\n$container-\u0026gt;get(Foo::class);            \/\/ new Foo\n$container-\u0026gt;get(BarInterface::class);   \/\/ new Bar(new Foo, __DIR__ . \u0027\/bar\u0027, 4, 69.123)\n$container-\u0026gt;getParameter(\u0027ROOT_DIR\u0027);   \/\/ __DIR__\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch4\u003EOptions\u003C\/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Eclass\u003C\/code\u003E \u2013 the class of the service, will default to the service name if not given.\nIf the given class implements the \u003Ccode\u003EResolver\u003C\/code\u003E interface, it will be instantiated,\nand it\u2019s \u003Ccode\u003Eresolve\u003C\/code\u003E method executed to provide an actual value to be put in the container.\u003C\/p\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Earguments\u003C\/code\u003E \u2013 constructor arguments.\u003C\/p\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Ecalls\u003C\/code\u003E \u2013 method calls to be executed right after constructing the service (setter injection etc.)\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E\u0027calls\u0027 =\u0026gt; [\n    [\u0027setLogger\u0027, [\u0027@logger\u0027]],\n    [\u0027registerListener\u0027, [\u0027@listenerA\u0027]],\n    [\u0027registerListener\u0027, [\u0027@listenerB\u0027]],\n],\u003C\/code\u003E\u003C\/pre\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Etags\u003C\/code\u003E \u2013 an array of string that help group similar services together; tagged services can be injected with \u003Ccode\u003E#tagName\u003C\/code\u003E:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E$container-\u0026gt;setDefinition(HandlerA::class, [\u0027tags\u0027 =\u0026gt; \u0027handler\u0027]);\n$container-\u0026gt;setDefinition(HandlerB::class, [\u0027tags\u0027 =\u0026gt; \u0027handler\u0027]);\n$container-\u0026gt;setDefinition(HandlerC::class, [\u0027tags\u0027 =\u0026gt; \u0027handler\u0027]);\n$container-\u0026gt;setDefinition(Manager::class, [\u0027arguments\u0027 =\u0026gt; [\u0027$handlers\u0027 =\u0026gt; \u0027#handler\u0027]]);\u003C\/code\u003E\u003C\/pre\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Efactory\u003C\/code\u003E -- determines if each \u003Ccode\u003Eget\u003C\/code\u003E should create a new service (\u003Ccode\u003Etrue\u003C\/code\u003E),\nor should one service be reused (\u003Ccode\u003Efalse\u003C\/code\u003E, default).\u003C\/p\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Eresolve\u003C\/code\u003E \u2013 instead of using \u003Ccode\u003Eclass\u003C\/code\u003E + \u003Ccode\u003Earguments\u003C\/code\u003E to construct the service,\nyou can use \u003Ccode\u003Eresolve\u003C\/code\u003E to define how it should be created:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E$container-\u0026gt;setDefinition(\u0027foo\u0027, [\u0027resolve\u0027 =\u0026gt; 4]); \/\/ 4\n$container-\u0026gt;setDefinition(\u0027language\u0027, [\u0027resolve\u0027 =\u0026gt; \u0027@Request.locale.language\u0027]); \/\/ $container-\u0026gt;get(\u0027Request\u0027)-\u0026gt;getLocale()-\u0026gt;getLanguage()\u003C\/code\u003E\u003C\/pre\u003E\n\u003C\/li\u003E\n\u003Cli\u003E\n\u003Cp\u003E\u003Ccode\u003Epublic\u003C\/code\u003E \u2013 determines if the service should be accessible directly with \u003Ccode\u003Eget\u003C\/code\u003E,\nor can it only be injected into other services.\u003C\/p\u003E\n\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Ch4\u003EContainerCompiler: autowiring and autoconfiguration\u003C\/h4\u003E\n\u003Cp\u003EUsually it\u2019s obvious, which service should be injected into another.\nFor instance when your service has a constructor argument \u003Ccode\u003EPsr\\Cache\\CacheItemPoolInterface $cache\u003C\/code\u003E,\nand the container does have a service named \u003Ccode\u003EPsr\\Cache\\CacheItemPoolInterface\u003C\/code\u003E,\nthen explicitly writing \u003Ccode\u003E[\u0027arguments\u0027 =\u0026gt; [\u0027$cache\u0027 =\u0026gt; \u0027@Psr\\Cache\\CacheItemPoolInterface\u0027]\u003C\/code\u003E is redundant.\nYou can always specify the dependencies manually, then autowiring won\u0027t overwrite them.\u003C\/p\u003E\n\u003Cp\u003EAutowiring is not magic \u2013 it just follows simple rules to determine,\nwhich service should be injected into the constructor:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eif the argument is a class which is defined in the container, use this service,\u003C\/li\u003E\n\u003Cli\u003Eif the argument is a class which is not defined in the container,\ntry to autowire that class and create a private service out of it,\u003C\/li\u003E\n\u003Cli\u003Eif the argument is an array and its name ends with \u003Ccode\u003Es\u003C\/code\u003E (e.g. \u003Ccode\u003Earray $helpers\u003C\/code\u003E),\ninject an array of services with a specific tag (\u003Ccode\u003E#helper\u003C\/code\u003E).\u003C\/li\u003E\n\u003Cli\u003Eif the argument is of type \u003Ccode\u003EBag\u003C\/code\u003E, inject the config value with its name\n(e.g. \u003Ccode\u003EBag $localisation\u003C\/code\u003E -\u0026gt; \u003Ccode\u003E@config.localisation\u003C\/code\u003E),\u003C\/li\u003E\n\u003Cli\u003Eif its name starts with \u003Ccode\u003Eenv\u003C\/code\u003E, inject a parameter:\n(e.g. \u003Ccode\u003Estring $envCacheDir\u003C\/code\u003E -\u0026gt; \u003Ccode\u003E%CACHE_DIR%\u003C\/code\u003E)\u003C\/li\u003E\n\u003Cli\u003Eif none of the above is true, but there is a default value, just use it,\u003C\/li\u003E\n\u003Cli\u003Eif none of the above is true, throw an exception \u2013 this argument should be defined explicitly.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EAutoconfiguration is another way to make your life simpler.\nFor instance, if you\u2019re using \u003Ca href=\u0022https:\/\/twig.symfony.com\/\u0022\u003ETwig\u003C\/a\u003E,\nyou might want all the classes in your code that extend \u003Ccode\u003ETwig\\Extension\\AbstractExtension\u003C\/code\u003E\nto be automatically registered as twig extension.\nAutoconfiguration lets you define what default config (tags, public etc.) should be added to them.\u003C\/p\u003E\n\u003Cp\u003ETo use autowiring and autoconfiguration, run the \u003Ccode\u003EContainerCompiler\u003C\/code\u003E:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$container = new Container;\n\n$services = [\n    \u0027App\\\u0027 =\u0026gt; [\n        \u0027dir\u0027 =\u0026gt; \u0027%MODULE_DIR%\/src\/\u0027,\n        \u0027exclude\u0027 =\u0026gt; [\u0027#^Entity\/#\u0027],\n    ],\n\n    \u0027App\\Foo\u0027 =\u0026gt; [\n        \u0027arguments\u0027 =\u0026gt; [\n            \u0027$bar\u0027 =\u0026gt; 5,\n        ],\n    ],\n\n    \u0027App\\Bar\u0027 =\u0026gt; [\n        \u0027public\u0027 =\u0026gt; true,\n    ],\n];\n\n$autoconfiguration = [\n    \u0027Twig\\Extension\\AbstractExtension\u0027 =\u0026gt; [\n        \u0027tags\u0027 =\u0026gt; [\u0027twigExtension\u0027],\n    ],\n];\n\n$definitions = new ContainerCompiler(\n    $container,\n    $services,\n    $autoconfiguration\n))-\u0026gt;compile();\n\n\/** @var ServiceDefinition $definition *\/\nforeach ($definitions as $name =\u0026gt; $definition) {\n    if (!$container-\u0026gt;has($name)) {\n        $container-\u0026gt;setDefinition($name, $definition);\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIn this example, the whole \u003Ccode\u003E%MODULE_DIR%\/src\/\u003C\/code\u003E except for (the \u003Ccode\u003E\/src\/Entity\u003C\/code\u003E dir) will be scanned for PHP files\nand all the found classes will be autowired as private services. If some of them are not used and not public,\nthey will be removed from the container.\u003C\/p\u003E\n\u003Cp\u003ECompiling the container has \u003Cstrong\u003Eno impact on performance\u003C\/strong\u003E on production environment,\nas long as you \u003Cstrong\u003Ecache the result of \u003Ccode\u003Ecompile()\u003C\/code\u003E\u003C\/strong\u003E.\u003C\/p\u003E\n\u003Ch4\u003EServiceLocator\u003C\/h4\u003E\n\u003Cp\u003EService locator restricts access to services in the container only to a selected list of names:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$container = new Container();\n$container-\u0026gt;set(\u0027foo\u0027, \u0027abc\u0027);\n$container-\u0026gt;set(\u0027bar\u0027, \u0027def\u0027);\n$container-\u0026gt;set(\u0027secret\u0027, \u0027XYZ\u0027);\n\n$locator = new ServiceLocator($container, [\u0027foo\u0027, \u0027bar\u0027]);\n\n$locator-\u0026gt;get(\u0027foo\u0027);     \/\/ \u0027abc\u0027\n$locator-\u0026gt;get(\u0027bar\u0027);     \/\/ \u0027def\u0027\n$locator-\u0026gt;get(\u0027secret\u0027);  \/\/ Exception\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EContainerAssistedBuilder\u003C\/h3\u003E\n\u003Cp\u003E\u003Ccode\u003EContainerAssistedBuilder\u003C\/code\u003E can be used to join together a couple of \u003Ccode\u003EContainerBuilderExtension\u003C\/code\u003Es\nwhich encapsulate a set of service definitions that form a library together.\nFor an example, see \u003Ca href=\u0022https:\/\/gitlab.com\/Avris\/Localisator\u0022\u003EAvris Localisator\u003C\/a\u003E.\u003C\/p\u003E\n\u003Ch4\u003EMicrus\u003C\/h4\u003E\n\u003Cp\u003EThis container was originally built as a part of the \u003Ca href=\u0022https:\/\/micrus.avris.it\u0022\u003EMicrus framework\u003C\/a\u003E.\u003C\/p\u003E","words":868,"readTime":4,"lang":"en"}}},"projects\/avris-dotenv":{"key":"projects\/avris-dotenv","type":"article","published":true,"meta":{"createdAt":"2017-12-31T14:35:44+01:00","publishedAt":"2017-12-31T14:34:00+01:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Dotenv","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-dotenv"},"content":{"en":{"slug":"avris-dotenv","title":"Avris Dotenv","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-dotenv_small.png\u0022 alt=\u0022Avris Dotenv\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHnElEQVRIiX2X268lRRXGf+tS1b33OXufM2dmzjDchpsooo4QjSTEgCQIxKBB4wVNiD7og8YH\/wTfffPBaHzWJ+MLYqLRCCSQKBqVi0i4zADDQIaZOZd9bru7avnQewajxkpXdyddl6++r1atr+Xa626NKBUhiFohKuZOLYUgUBFcDVMluTOfH1BrhQiIQAARQRiKiCAiQHCpRMSi1stPoiICKoK5YyI0zQiPECKCUgsSIEApBWEYWFUHoKJQK0mVfjEBIot2XG4vAiCoynuTy6V3IWpB1Ih4r58hJDW0FvxSB0EJKhGgIYgOkxGCqgNCXyqGoAghiqku2BgWggzfFtfiZrgb83mHCEQMCxRRdNHXVHEzkioeolDLguCBESKQEFQNN8VEMFHcFIlKrZVayzCA+7D6WlG1y1KpCFErQbCyusrGhYtAhRDEIWpBVSDATElmJL8ESAAVqAu2BER0IYOhKGZGtsS4aSB6dvf2aNxpkpM9UWullJ6+FBCG\/VIrAMePrXOwPQPqQFoEqnkBSjEdJM6muGgiokPCQCp6eZMKajZQawkVRcVQcZaaJQ6NVrg420RDiRI0nlDPEIUQoe+7gSGBE9dey9unXkckkABTQQggIRILyZRkimvK9MEiYuxSrIDqwI4apkZ2J1tCMUQSTU5cf3SZnd0ZB13HsSvWme\/tMtvaxkTIrtSo3Pnpu3jlhReZ5oYghsgCVAdRVEEEkinZDQ9NSFrIhVADTBRVQ0Vxc1yN7BkXIZmTJJG0YW1thc9\/\/YusHVrlD7\/4NW+cfgNrlkk+RNNkZZk777mL5x9\/itV2BBQiYgCjggqYCaYDa+6Ckxukd2qNhb4Aiqih5ogYbo5qws0WNXH3A5\/k4e98mZee+QeP\/vDnJAo3H7+SqJXtnRmeja99\/3s888vHuHo6ZV46avRElEE6XURXGvZQToopuLRL1K4gtQ7nUABiiDhqiWSOqpHMaVNm7M43vv0F7nvobmYXt3nsx4+SdcShccuNN13Jzua7bO7uc893v0pSoZ5+kw8cX2e2t8t+P6fUnlI70Epyw0xwV5ILKRku4yky76FUAoY9giKSUDHEE1mdNiXGKfHwI\/dy30N3A\/D3J58j0TBZnrA8bti+sMeJj32ETz94J942PP2Dn7J+ZAWNjs2toO8TJQZAIRXVIGXHXUhJ8Sy4jCaQeyggoRCKoCAOmjB1kjk5JU6evJ7PfulTl1PC1jszDh87ylUn1jlx6zVcc\/sNLB2ZEhG88ZsnWBplSl2mbXpyU0EqNTr6fk7UDjVQDcyN3DieBGeyAn0hQgYwMUSX4bgmFEcWB+Aj33rg8skMcP837+U\/S0Rw5ok\/Mz\/3LkevO8zBZqbb2WCqHWIVsaDWjtofIPSoCeZKyhlvGlwnE1JhyDMoHkrCyOLkMLIaGeX2k9dy1VVr\/wXg30sthVd++ye2X3qFdklpV8YcveVq+s0lts4KKnOEDiQTJSFShwhLjuWMNSN8sjqhFGDBkCF4OK0YLUqL0VThjk\/c9H\/BzM5t8sKvnmJ+\/hyjBlDo9nbZPd\/hMme80lC7Mpw9BioJWcil7lhu0WaEX7E2ou+DWqCGoqEoRgohYbShNEW4+YYj\/xPI3tYu\/3zyOc785UWydIzaOqSOCtFBPTBolDxSIikmsZApoaZYymhukNRAHuEfWm\/Y3e+YHxTmfaWUSqkVCUGiYkUwEY4cGl8GsTPb4\/RLZzj1t1c58\/xrNNEx9kKkglPJWgkNsID5kCslBckDN8FM0JSQ3EIyyBlyA97iDx56i539ymzWsbVTmfXB9gHszmG\/g3kvlB5+8qOfsbezx8a7W+xc2CKXQqKQqLRaGVnPUgqmbWU+rjAJUgitDTZDSod6YNmwGnSdYUzoDgxtE5sXx3S0yN23Holpm9jfH6ySWybEqTghiYJTNdNXCFFQx8y5Yv0YF86fJ7szypn1I6vsb2\/RSM8kw9Gl4PihwvoRY2lJeP3sBrO9PUruWL3yKh7\/3R85dfaAaVtZX59w0AVPv7CDLK+uhaaMqGEp4U2LqOG5wVPGUoOqkZsW80TyzOG1NW7\/6G200bG5tcfx9XXWpsucOfUmy9lZTsaF106x2sLhZeXQEiTtSdazutRz88kxG2dnnDu7xWQELj1H77iX3Wd\/j9\/yua\/QLE\/pK7RLEzw1qDpiTvKEiCEMeUdCkFJpTFg6ts7Ozj7j5cosgjo3YmWdjf0DRtMpB5OOAxd2kpKT0aZKtcp+U+lslbX3w8bsVdJIaLynv7jL8gfvwwvGbHcPLDHf3kZkB9DBC8VgwAcvBFIqlIrUyhsvvwS1kM2QEkgEVgsJ49laGKfMJDkTd1aaxDgJroVxhtl+x8c\/POXNi0q90DMdwWTjLd53\/234xdMvM+861DO1VLwZwXv\/EAjgKaGqSARRA40BgNaK9P1lg1+7A1yMjXdep80tLsLK8pTD01U8Kru7WxyaTvjri87bp0e8s7nPqfP7jHLQ6JzP9G8iR268JWop9PM5tTsgLU2QEMp8H03NkA5qj+cWYPDcgNZAESQueejBi2sEQsVEcBVcQKlkM1SDvu8QCusTZ6U1LuwNNrdxuOME\/AvjxTpTHhTrrwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-dotenv_small.png\u0022 alt=\u0022Avris Dotenv\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E.env file handler\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-dotenv_big.png\u0022 alt=\u0022Avris Dotenv\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHnElEQVRIiX2X268lRRXGf+tS1b33OXufM2dmzjDchpsooo4QjSTEgCQIxKBB4wVNiD7og8YH\/wTfffPBaHzWJ+MLYqLRCCSQKBqVi0i4zADDQIaZOZd9bru7avnQewajxkpXdyddl6++r1atr+Xa626NKBUhiFohKuZOLYUgUBFcDVMluTOfH1BrhQiIQAARQRiKiCAiQHCpRMSi1stPoiICKoK5YyI0zQiPECKCUgsSIEApBWEYWFUHoKJQK0mVfjEBIot2XG4vAiCoynuTy6V3IWpB1Ih4r58hJDW0FvxSB0EJKhGgIYgOkxGCqgNCXyqGoAghiqku2BgWggzfFtfiZrgb83mHCEQMCxRRdNHXVHEzkioeolDLguCBESKQEFQNN8VEMFHcFIlKrZVayzCA+7D6WlG1y1KpCFErQbCyusrGhYtAhRDEIWpBVSDATElmJL8ESAAVqAu2BER0IYOhKGZGtsS4aSB6dvf2aNxpkpM9UWullJ6+FBCG\/VIrAMePrXOwPQPqQFoEqnkBSjEdJM6muGgiokPCQCp6eZMKajZQawkVRcVQcZaaJQ6NVrg420RDiRI0nlDPEIUQoe+7gSGBE9dey9unXkckkABTQQggIRILyZRkimvK9MEiYuxSrIDqwI4apkZ2J1tCMUQSTU5cf3SZnd0ZB13HsSvWme\/tMtvaxkTIrtSo3Pnpu3jlhReZ5oYghsgCVAdRVEEEkinZDQ9NSFrIhVADTBRVQ0Vxc1yN7BkXIZmTJJG0YW1thc9\/\/YusHVrlD7\/4NW+cfgNrlkk+RNNkZZk777mL5x9\/itV2BBQiYgCjggqYCaYDa+6Ckxukd2qNhb4Aiqih5ogYbo5qws0WNXH3A5\/k4e98mZee+QeP\/vDnJAo3H7+SqJXtnRmeja99\/3s888vHuHo6ZV46avRElEE6XURXGvZQToopuLRL1K4gtQ7nUABiiDhqiWSOqpHMaVNm7M43vv0F7nvobmYXt3nsx4+SdcShccuNN13Jzua7bO7uc893v0pSoZ5+kw8cX2e2t8t+P6fUnlI70Epyw0xwV5ILKRku4yky76FUAoY9giKSUDHEE1mdNiXGKfHwI\/dy30N3A\/D3J58j0TBZnrA8bti+sMeJj32ETz94J942PP2Dn7J+ZAWNjs2toO8TJQZAIRXVIGXHXUhJ8Sy4jCaQeyggoRCKoCAOmjB1kjk5JU6evJ7PfulTl1PC1jszDh87ylUn1jlx6zVcc\/sNLB2ZEhG88ZsnWBplSl2mbXpyU0EqNTr6fk7UDjVQDcyN3DieBGeyAn0hQgYwMUSX4bgmFEcWB+Aj33rg8skMcP837+U\/S0Rw5ok\/Mz\/3LkevO8zBZqbb2WCqHWIVsaDWjtofIPSoCeZKyhlvGlwnE1JhyDMoHkrCyOLkMLIaGeX2k9dy1VVr\/wXg30sthVd++ye2X3qFdklpV8YcveVq+s0lts4KKnOEDiQTJSFShwhLjuWMNSN8sjqhFGDBkCF4OK0YLUqL0VThjk\/c9H\/BzM5t8sKvnmJ+\/hyjBlDo9nbZPd\/hMme80lC7Mpw9BioJWcil7lhu0WaEX7E2ou+DWqCGoqEoRgohYbShNEW4+YYj\/xPI3tYu\/3zyOc785UWydIzaOqSOCtFBPTBolDxSIikmsZApoaZYymhukNRAHuEfWm\/Y3e+YHxTmfaWUSqkVCUGiYkUwEY4cGl8GsTPb4\/RLZzj1t1c58\/xrNNEx9kKkglPJWgkNsID5kCslBckDN8FM0JSQ3EIyyBlyA97iDx56i539ymzWsbVTmfXB9gHszmG\/g3kvlB5+8qOfsbezx8a7W+xc2CKXQqKQqLRaGVnPUgqmbWU+rjAJUgitDTZDSod6YNmwGnSdYUzoDgxtE5sXx3S0yN23Holpm9jfH6ySWybEqTghiYJTNdNXCFFQx8y5Yv0YF86fJ7szypn1I6vsb2\/RSM8kw9Gl4PihwvoRY2lJeP3sBrO9PUruWL3yKh7\/3R85dfaAaVtZX59w0AVPv7CDLK+uhaaMqGEp4U2LqOG5wVPGUoOqkZsW80TyzOG1NW7\/6G200bG5tcfx9XXWpsucOfUmy9lZTsaF106x2sLhZeXQEiTtSdazutRz88kxG2dnnDu7xWQELj1H77iX3Wd\/j9\/yua\/QLE\/pK7RLEzw1qDpiTvKEiCEMeUdCkFJpTFg6ts7Ozj7j5cosgjo3YmWdjf0DRtMpB5OOAxd2kpKT0aZKtcp+U+lslbX3w8bsVdJIaLynv7jL8gfvwwvGbHcPLDHf3kZkB9DBC8VgwAcvBFIqlIrUyhsvvwS1kM2QEkgEVgsJ49laGKfMJDkTd1aaxDgJroVxhtl+x8c\/POXNi0q90DMdwWTjLd53\/234xdMvM+861DO1VLwZwXv\/EAjgKaGqSARRA40BgNaK9P1lg1+7A1yMjXdep80tLsLK8pTD01U8Kru7WxyaTvjri87bp0e8s7nPqfP7jHLQ6JzP9G8iR268JWop9PM5tTsgLU2QEMp8H03NkA5qj+cWYPDcgNZAESQueejBi2sEQsVEcBVcQKlkM1SDvu8QCusTZ6U1LuwNNrdxuOME\/AvjxTpTHhTrrwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-dotenv_big.png\u0022 alt=\u0022Avris Dotenv\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E.env file handler\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs bash border\u0022\u003Ecomposer require avris\/dotenv\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003E.env\u003C\/h3\u003E\n\u003Cp\u003EEnvironmental variables are a great way to adjust your application to a specific environment,\nespecially when it comes to sensitive data, like database password.\u003C\/p\u003E\n\u003Cp\u003EThose variables should be set by your HTTP server,\nbut for local development having a \u003Ccode\u003E.env\u003C\/code\u003E file is way simpler:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs border\u0022\u003EAPP_ENV=prod\nAPP_DEBUG=0\n\nDB_HOST=127.0.0.1\nDB_DATABASE=prod\nDB_USER=user\nDB_PASS=pass\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAll you need to do in your front controller is load this file using Dotenv:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E(\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\Avris\\Dotenv\\Dotenv)-\u0026gt;load(\u003Cspan class=\u0022hljs-keyword\u0022\u003E__DIR__\u003C\/span\u003E . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/..\/.env\u0027\u003C\/span\u003E);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ENow all the variables from the file will be available via \u003Ccode\u003Egetenv\u003C\/code\u003E, \u003Ccode\u003E$_ENV\u003C\/code\u003E and \u003Ccode\u003E$_SERVER\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003EAdvanced\u003C\/h3\u003E\n\u003Cp\u003EIf your variable value contains a space, remember to quote it:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs border\u0022\u003EFOO=\u0026quot;Lorem ipsum\u0026quot;\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can add a comment if you start a line with a \u003Ccode\u003E#\u003C\/code\u003E:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs border\u0022\u003E# comment\nFOO=bar\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can use vars inside other vars (escape \u003Ccode\u003E$\u003C\/code\u003E with \u003Ccode\u003E\\$\u003C\/code\u003E):\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs border\u0022\u003EVAR1=osiem\nVAR2=\u0026quot;${VAR1}na\u015bcie ${CZEGO}\u0026quot;\nVAR3=\\$ESC\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can also use output of a command (\u003Ccode\u003Esymfony\/process\u003C\/code\u003E is required, escape \u003Ccode\u003E$\u003C\/code\u003E with \u003Ccode\u003E\\$\u003C\/code\u003E):\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs border\u0022\u003ECOMM1=$(whoami)\nCOMM2=\\$(whoami)\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ERemember .env is a valid bash script.\u003C\/p\u003E\n\u003Ch3\u003EOther features\u003C\/h3\u003E\n\u003Cp\u003EYou can just read the variables from a file without populating it as environment variables: \u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$vars = (\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\Avris\\Dotenv\\Dotenv)-\u0026gt;read(\u003Cspan class=\u0022hljs-keyword\u0022\u003E__DIR__\u003C\/span\u003E . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/..\/.env\u0027\u003C\/span\u003E);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EOr just populate vars into environment without parsing a file:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E(\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\Avris\\Dotenv\\Dotenv)-\u0026gt;populate([\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027FOO\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E,\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027BAR\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E,\n]);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can also rever the process and dump vars into a .env file:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E(\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\Avris\\Dotenv\\Dotenv)-\u0026gt;save(\u003Cspan class=\u0022hljs-keyword\u0022\u003E__DIR__\u003C\/span\u003E . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/.env\u0027\u003C\/span\u003E, [\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027FOO\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E,\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027BAR\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E,\n]);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EOr without saving to a file:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$string = (\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\Avris\\Dotenv\\Dotenv)-\u0026gt;dump([\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027FOO\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E,\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027BAR\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E,\n]);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EFill command\u003C\/h3\u003E\n\u003Cp\u003EDotenv provides a simple way to fill your \u003Ccode\u003E.env\u003C\/code\u003E file with values based on user input and defaults.\u003C\/p\u003E\n\u003Cp\u003ELet\u0027s say you have an application with two modules that require some environment variables: \u003Ccode\u003EFoo\\Database\u003C\/code\u003E and \u003Ccode\u003EFoo\\Mailer\u003C\/code\u003E.\nInstall \u003Ccode\u003Esymfony\/console\u003C\/code\u003E and create a command that extends \u003Ccode\u003EAvris\\Dotenv\\Command\\FillCommand\u003C\/code\u003E, providing all the defaults:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efinal\u003C\/span\u003E \u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EFillCommandImplementation\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EAvris\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EDotenv\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003ECommand\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EFillCommand\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprotected\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetDefaults\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Eiterable\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eyield\u003C\/span\u003E `Foo\\Database` =\u0026gt; [\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027DB_HOST\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027127.0.0.1\u0027\u003C\/span\u003E,\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027DB_DATABASE\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E,\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027DB_USER\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027root\u0027\u003C\/span\u003E,\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027DB_PASS\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\u0027\u003C\/span\u003E,\n        ]; \n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eyield\u003C\/span\u003E `Foo\\Mailer` =\u0026gt; [\n            \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027MAILER_URL\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027null:\/\/localhost\u0027\u003C\/span\u003E,\n        ];\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ELet\u2019s say your \u003Ccode\u003E.env\u003C\/code\u003E file looks like this:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs bash border\u0022\u003E\u003Cspan class=\u0022hljs-comment\u0022\u003E###\u0026gt; Foo\\Database ###\u003C\/span\u003E\nDB_HOST=127.0.0.1\nDB_DATABASE=prod\nDB_USER=user\n\u003Cspan class=\u0022hljs-comment\u0022\u003E###\u0026lt; Foo\\Database ###\u003C\/span\u003E\n\nFOO=bar\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EBoth \u003Ccode\u003EDB_PASS\u003C\/code\u003E and \u003Ccode\u003EMAILER_URL\u003C\/code\u003E are missing and an additional variable \u003Ccode\u003EFOO\u003C\/code\u003E is defined.\u003C\/p\u003E\n\u003Cp\u003EIf you now \u003Ca href=\u0022https:\/\/symfony.com\/doc\/current\/components\/console.html\u0022\u003Erun the command\u003C\/a\u003E,\nyou will be asked what should \u003Ccode\u003EDB_PASS\u003C\/code\u003E and \u003Ccode\u003EMAILER_URL\u003C\/code\u003E be (defaults \u003Ccode\u003E\u0027\u0027\u003C\/code\u003E and \u003Ccode\u003Enull:\/\/localhost\u003C\/code\u003E are offered),\nand the values you submit will be put in the \u003Ccode\u003E.env\u003C\/code\u003E file without removing \u003Ccode\u003EFOO\u003C\/code\u003E.  \u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["dotenv","environment variables","php"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-dotenv_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-dotenv_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-dotenv_mini.png\u0022 alt=\u0022Avris Dotenv\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E.env file handler\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-dotenv_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-dotenv_mini.png\u0022 alt=\u0022Avris Dotenv\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E.env file handler\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Ecomposer require avris\/dotenv\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003E.env\u003C\/h3\u003E\n\u003Cp\u003EEnvironmental variables are a great way to adjust your application to a specific environment,\nespecially when it comes to sensitive data, like database password.\u003C\/p\u003E\n\u003Cp\u003EThose variables should be set by your HTTP server,\nbut for local development having a \u003Ccode\u003E.env\u003C\/code\u003E file is way simpler:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-dotenv\u0022\u003EAPP_ENV=prod\nAPP_DEBUG=0\n\nDB_HOST=127.0.0.1\nDB_DATABASE=prod\nDB_USER=user\nDB_PASS=pass\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAll you need to do in your front controller is load this file using Dotenv:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E(new \\Avris\\Dotenv\\Dotenv)-\u0026gt;load(__DIR__ . \u0027\/..\/.env\u0027);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ENow all the variables from the file will be available via \u003Ccode\u003Egetenv\u003C\/code\u003E, \u003Ccode\u003E$_ENV\u003C\/code\u003E and \u003Ccode\u003E$_SERVER\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003EAdvanced\u003C\/h3\u003E\n\u003Cp\u003EIf your variable value contains a space, remember to quote it:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-dotenv\u0022\u003EFOO=\u0022Lorem ipsum\u0022\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can add a comment if you start a line with a \u003Ccode\u003E#\u003C\/code\u003E:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-dotenv\u0022\u003E# comment\nFOO=bar\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can use vars inside other vars (escape \u003Ccode\u003E$\u003C\/code\u003E with \u003Ccode\u003E\\$\u003C\/code\u003E):\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-dotenv\u0022\u003EVAR1=osiem\nVAR2=\u0022${VAR1}na\u015bcie ${CZEGO}\u0022\nVAR3=\\$ESC\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can also use output of a command (\u003Ccode\u003Esymfony\/process\u003C\/code\u003E is required, escape \u003Ccode\u003E$\u003C\/code\u003E with \u003Ccode\u003E\\$\u003C\/code\u003E):\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-dotenv\u0022\u003ECOMM1=$(whoami)\nCOMM2=\\$(whoami)\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ERemember .env is a valid bash script.\u003C\/p\u003E\n\u003Ch3\u003EOther features\u003C\/h3\u003E\n\u003Cp\u003EYou can just read the variables from a file without populating it as environment variables: \u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$vars = (new \\Avris\\Dotenv\\Dotenv)-\u0026gt;read(__DIR__ . \u0027\/..\/.env\u0027);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EOr just populate vars into environment without parsing a file:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E(new \\Avris\\Dotenv\\Dotenv)-\u0026gt;populate([\n    \u0027FOO\u0027 =\u0026gt; \u0027foo\u0027,\n    \u0027BAR\u0027 =\u0026gt; \u0027bar\u0027,\n]);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can also rever the process and dump vars into a .env file:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E(new \\Avris\\Dotenv\\Dotenv)-\u0026gt;save(__DIR__ . \u0027\/.env\u0027, [\n    \u0027FOO\u0027 =\u0026gt; \u0027foo\u0027,\n    \u0027BAR\u0027 =\u0026gt; \u0027bar\u0027,\n]);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EOr without saving to a file:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$string = (new \\Avris\\Dotenv\\Dotenv)-\u0026gt;dump([\n    \u0027FOO\u0027 =\u0026gt; \u0027foo\u0027,\n    \u0027BAR\u0027 =\u0026gt; \u0027bar\u0027,\n]);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EFill command\u003C\/h3\u003E\n\u003Cp\u003EDotenv provides a simple way to fill your \u003Ccode\u003E.env\u003C\/code\u003E file with values based on user input and defaults.\u003C\/p\u003E\n\u003Cp\u003ELet\u0027s say you have an application with two modules that require some environment variables: \u003Ccode\u003EFoo\\Database\u003C\/code\u003E and \u003Ccode\u003EFoo\\Mailer\u003C\/code\u003E.\nInstall \u003Ccode\u003Esymfony\/console\u003C\/code\u003E and create a command that extends \u003Ccode\u003EAvris\\Dotenv\\Command\\FillCommand\u003C\/code\u003E, providing all the defaults:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003Efinal class FillCommandImplementation extends \\Avris\\Dotenv\\Command\\FillCommand\n{\n    protected function getDefaults(): iterable\n    {\n        yield `Foo\\Database` =\u0026gt; [\n            \u0027DB_HOST\u0027 =\u0026gt; \u0027127.0.0.1\u0027,\n            \u0027DB_DATABASE\u0027 =\u0026gt; \u0027foo\u0027,\n            \u0027DB_USER\u0027 =\u0026gt; \u0027root\u0027,\n            \u0027DB_PASS\u0027 =\u0026gt; \u0027\u0027,\n        ]; \n        yield `Foo\\Mailer` =\u0026gt; [\n            \u0027MAILER_URL\u0027 =\u0026gt; \u0027null:\/\/localhost\u0027,\n        ];\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ELet\u2019s say your \u003Ccode\u003E.env\u003C\/code\u003E file looks like this:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003E###\u0026gt; Foo\\Database ###\nDB_HOST=127.0.0.1\nDB_DATABASE=prod\nDB_USER=user\n###\u0026lt; Foo\\Database ###\n\nFOO=bar\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EBoth \u003Ccode\u003EDB_PASS\u003C\/code\u003E and \u003Ccode\u003EMAILER_URL\u003C\/code\u003E are missing and an additional variable \u003Ccode\u003EFOO\u003C\/code\u003E is defined.\u003C\/p\u003E\n\u003Cp\u003EIf you now \u003Ca href=\u0022https:\/\/symfony.com\/doc\/current\/components\/console.html\u0022\u003Erun the command\u003C\/a\u003E,\nyou will be asked what should \u003Ccode\u003EDB_PASS\u003C\/code\u003E and \u003Ccode\u003EMAILER_URL\u003C\/code\u003E be (defaults \u003Ccode\u003E\u0027\u0027\u003C\/code\u003E and \u003Ccode\u003Enull:\/\/localhost\u003C\/code\u003E are offered),\nand the values you submit will be put in the \u003Ccode\u003E.env\u003C\/code\u003E file without removing \u003Ccode\u003EFOO\u003C\/code\u003E.  \u003C\/p\u003E","words":464,"readTime":2,"lang":"en"}}},"projects\/avris-dispatcher":{"key":"projects\/avris-dispatcher","type":"article","published":true,"meta":{"createdAt":"2017-12-31T13:16:19+01:00","publishedAt":"2017-12-31T13:14:00+01:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Dispatcher","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-dispatcher"},"content":{"en":{"slug":"avris-dispatcher","title":"Avris Dispatcher","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-dispatcher_small.png\u0022 alt=\u0022Avris Dispatcher\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHp0lEQVRIiX2X2a8lV3XGf2vYu+qce88dum\/f7nbb3QYbgzFgBSXCkoXsWPKAiEEkYpYQPJCHIB74E3jnjQcE4jl5ivJCiAICMUhYYogygONYmLjdbjdWT3c490xVey8e6nQbJVG2aleVVHv49vfttddXcvH+RyJKRQiiVoiKuVNLIQhUBFfDVEnurFZLaq0QAREIICIIQxERRAQI7pSIWNd690lUREBFMHdMhKYZ4RFCRFBqQQIEKKUgDAOr6gBUFGolqdKvJ0Bk3Y677UUABFV5a3K58y5ELYgaEW\/1M4SkhtaC3+kgKEElAjQE0WEyQlB1QOhLxRAUIUQx1TUbw0KQ4dv6Wt8Md2O16hCBiGGBIoqu+5oqbkZSxUMUalkTPDBCBBKCquGmmAgmipsiUam1UmsZBnAfVl8rqnZXKhUhaiUItnd2OLh1G6gQgjhELagKBJgpyYzkdwAJoAJ1zZaAiK5lMBTFzMiWGDcNRM9sPqdxp0lO9kStlVJ6+lJAGPZLrQCcP7vP8ngK1IG0CFTzGpRiOkicTXHRRESHhIFU9O4mFdRsoNYSKoqKoeJsNBvsjra5PT1EQ4kSNJ5QzxCFEKHvu4EhgUsXL\/L7V19DJJAAU0EIICESa8mUZIpryvTBOmLsTqyA6sCOGqZGdidbQjFEEk1OvO3MJiezKcuu4+y5fVbzGdOjY0yE7EqNyuPPPMErL77EVm4IYogsQHUQRRVEIJmS3fDQhKS1XAg1wERRNVQUN8fVyJ5xEZI5SRJJG06d2uYvP\/9xTu3u8KO\/\/yeuXL6CNZskH6Jpsr3J4089wW9+\/DN22hFQiIgBjAoqYCaYDqy5C05ukN6pNdb6AiiihpojYrg5qgk3W9fEkx\/6IJ\/+0id5+Zf\/yXe+\/nckCg+dv4eoleOTKZ6Nz371K\/zyH77LvVtbrEpHjZ6IMkin6+hKwx7KSTEFl3aD2hWk1uEcCkAMEUctkcxRNZI5bcqM3fnC3\/wVz37sSaa3j\/nuN79D1hG745YHHryHk8MbHM4WPPXlz5BUqJdf513n95nOZyz6FaX2lNqBVpIbZoK7klxIyXAZbyGrHkolYNgjKCIJFUM8kdVpU2KcEp\/+3NM8+7EnAfj3n\/6aRMNkc8LmuOH41pxLf\/o+nnn+cbxteOFr32Z\/bxuNjsOjoO8TJQZAIRXVIGXHXUhJ8Sy4jCaQeyggoRCKoCAOmjB1kjk5JR599G185BN\/fjclHL055fTZM1y4tM+lR+7jvve\/nY29LSKCK9\/7CRujTKmbtE1PbipIpUZH36+I2qEGqoG5kRvHk+BMtqEvRMgAJoboMhzXhOLI+gD83F9\/6O7JDPDcF5\/mf5aI4OpPfsXq+g3O3H+a5WGmOzlgSzvEKmJBrR21XyL0qAnmSsoZbxpcJxNSYcgzKB5Kwsji5DCyGhnl\/Y9e5MKFU\/8LwB+XWgqvfP8XHL\/8Cu2G0m6POfPwvfSHGxxdE1RWCB1IJkpCpA4RlhzLGWtG+GRnQinAmiFD8HBaMVqUFqOpwmMfePD\/BTO9fsiL\/\/gzVjevM2oAhW4+Y3azw2XFeLuhdmU4ewxUErKWS92x3KLNCD93akTfB7VADUVDUYwUQsJoQ2mK8NDb9\/5PIPOjGf\/1019z9V9eIkvHqK1D6qgQHdSlQaPkkRJJMYm1TAk1xVJGc4OkBvIIf89+w2zRsVoWVn2llEqpFQlBomJFMBH2dsd3QZxM51x++Sqv\/tvvuPqb\/6aJjrEXIhWcStZKaIAFrIZcKSlIHrgJZoKmhOQWkkHOkBvwFn9+9w1OFpXptOPopDLtg+MlzFaw6GDVC6WHb33jb5mfzDm4ccTJrSNyKSQKiUqrlZH1bKRgq62sxhUmQQqhtcFmSOlQDywbVoOuM4wJ3dLQNnF4e0xHizz5yF5stYnFYrBKbpkQp+KEJApO1UxfIUQ5e\/4Cy8WS2ckMU13nOGOUHY\/Cub0JLI45sxGc3y3s7xkbG8Jr1w6YzueU3LFzzwV+\/IOf8+q1JVttZX9\/wrILXnjxBNncORWaMqKGpYQ3LaKG5wZPGUsNqkZuWswTf\/HM07QW3Lh5SNdVVIxL5\/dJ3nJ04yZnT+8gyznn9rYox4f016+wuwFJe5L17Gz0PPTomINrU65fO2IyApeeM489zew\/fog\/\/NFP0Wxu0VdoNyZ4alB1xJzkCRFDGPKOhLB58R7m8yXteMW79k9x9doN8vYGB7dP8NPneHPRsTs+zdVbS8ZsEulecjLaVKlWWTSVznY49U44mP6ONBIa7+lvz9h897N4wZjO5mCJ1fExIieADl4oBgM+eCGQUvnnN64gtVIXC34eFVdFSiARWC0kBm88TplJcibubDeJcRJcC+MM00XHn713i9dvK\/VWz9YIJgdv8I7n\/gS\/ffm3rLoO9UwtFW9G8NY\/BAJ4SqgqEkHUQGMAoLUifX\/X4NduiYtx8OZrtLnFRdje3OL01g4eldnsiN2tCf\/6kvP7yyPePFzw6s0Foxw0uuLD\/evI3gMPRy2FfrWidkvSxgQJoawWaGqGdFB7PLcAg+cGtAaKIHHHQw9eXCMQKiaCq+ACSiWboRr0fYdQ2J84261xaz7Y3MbhsUvwB7\/yQ03P6NWiAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-dispatcher_small.png\u0022 alt=\u0022Avris Dispatcher\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EAn event dispatcher\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-dispatcher_big.png\u0022 alt=\u0022Avris Dispatcher\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHp0lEQVRIiX2X2a8lV3XGf2vYu+qce88dum\/f7nbb3QYbgzFgBSXCkoXsWPKAiEEkYpYQPJCHIB74E3jnjQcE4jl5ivJCiAICMUhYYogygONYmLjdbjdWT3c490xVey8e6nQbJVG2aleVVHv49vfttddXcvH+RyJKRQiiVoiKuVNLIQhUBFfDVEnurFZLaq0QAREIICIIQxERRAQI7pSIWNd690lUREBFMHdMhKYZ4RFCRFBqQQIEKKUgDAOr6gBUFGolqdKvJ0Bk3Y677UUABFV5a3K58y5ELYgaEW\/1M4SkhtaC3+kgKEElAjQE0WEyQlB1QOhLxRAUIUQx1TUbw0KQ4dv6Wt8Md2O16hCBiGGBIoqu+5oqbkZSxUMUalkTPDBCBBKCquGmmAgmipsiUam1UmsZBnAfVl8rqnZXKhUhaiUItnd2OLh1G6gQgjhELagKBJgpyYzkdwAJoAJ1zZaAiK5lMBTFzMiWGDcNRM9sPqdxp0lO9kStlVJ6+lJAGPZLrQCcP7vP8ngK1IG0CFTzGpRiOkicTXHRRESHhIFU9O4mFdRsoNYSKoqKoeJsNBvsjra5PT1EQ4kSNJ5QzxCFEKHvu4EhgUsXL\/L7V19DJJAAU0EIICESa8mUZIpryvTBOmLsTqyA6sCOGqZGdidbQjFEEk1OvO3MJiezKcuu4+y5fVbzGdOjY0yE7EqNyuPPPMErL77EVm4IYogsQHUQRRVEIJmS3fDQhKS1XAg1wERRNVQUN8fVyJ5xEZI5SRJJG06d2uYvP\/9xTu3u8KO\/\/yeuXL6CNZskH6Jpsr3J4089wW9+\/DN22hFQiIgBjAoqYCaYDqy5C05ukN6pNdb6AiiihpojYrg5qgk3W9fEkx\/6IJ\/+0id5+Zf\/yXe+\/nckCg+dv4eoleOTKZ6Nz371K\/zyH77LvVtbrEpHjZ6IMkin6+hKwx7KSTEFl3aD2hWk1uEcCkAMEUctkcxRNZI5bcqM3fnC3\/wVz37sSaa3j\/nuN79D1hG745YHHryHk8MbHM4WPPXlz5BUqJdf513n95nOZyz6FaX2lNqBVpIbZoK7klxIyXAZbyGrHkolYNgjKCIJFUM8kdVpU2KcEp\/+3NM8+7EnAfj3n\/6aRMNkc8LmuOH41pxLf\/o+nnn+cbxteOFr32Z\/bxuNjsOjoO8TJQZAIRXVIGXHXUhJ8Sy4jCaQeyggoRCKoCAOmjB1kjk5JR599G185BN\/fjclHL055fTZM1y4tM+lR+7jvve\/nY29LSKCK9\/7CRujTKmbtE1PbipIpUZH36+I2qEGqoG5kRvHk+BMtqEvRMgAJoboMhzXhOLI+gD83F9\/6O7JDPDcF5\/mf5aI4OpPfsXq+g3O3H+a5WGmOzlgSzvEKmJBrR21XyL0qAnmSsoZbxpcJxNSYcgzKB5Kwsji5DCyGhnl\/Y9e5MKFU\/8LwB+XWgqvfP8XHL\/8Cu2G0m6POfPwvfSHGxxdE1RWCB1IJkpCpA4RlhzLGWtG+GRnQinAmiFD8HBaMVqUFqOpwmMfePD\/BTO9fsiL\/\/gzVjevM2oAhW4+Y3azw2XFeLuhdmU4ewxUErKWS92x3KLNCD93akTfB7VADUVDUYwUQsJoQ2mK8NDb9\/5PIPOjGf\/1019z9V9eIkvHqK1D6qgQHdSlQaPkkRJJMYm1TAk1xVJGc4OkBvIIf89+w2zRsVoWVn2llEqpFQlBomJFMBH2dsd3QZxM51x++Sqv\/tvvuPqb\/6aJjrEXIhWcStZKaIAFrIZcKSlIHrgJZoKmhOQWkkHOkBvwFn9+9w1OFpXptOPopDLtg+MlzFaw6GDVC6WHb33jb5mfzDm4ccTJrSNyKSQKiUqrlZH1bKRgq62sxhUmQQqhtcFmSOlQDywbVoOuM4wJ3dLQNnF4e0xHizz5yF5stYnFYrBKbpkQp+KEJApO1UxfIUQ5e\/4Cy8WS2ckMU13nOGOUHY\/Cub0JLI45sxGc3y3s7xkbG8Jr1w6YzueU3LFzzwV+\/IOf8+q1JVttZX9\/wrILXnjxBNncORWaMqKGpYQ3LaKG5wZPGUsNqkZuWswTf\/HM07QW3Lh5SNdVVIxL5\/dJ3nJ04yZnT+8gyznn9rYox4f016+wuwFJe5L17Gz0PPTomINrU65fO2IyApeeM489zew\/fog\/\/NFP0Wxu0VdoNyZ4alB1xJzkCRFDGPKOhLB58R7m8yXteMW79k9x9doN8vYGB7dP8NPneHPRsTs+zdVbS8ZsEulecjLaVKlWWTSVznY49U44mP6ONBIa7+lvz9h897N4wZjO5mCJ1fExIieADl4oBgM+eCGQUvnnN64gtVIXC34eFVdFSiARWC0kBm88TplJcibubDeJcRJcC+MM00XHn713i9dvK\/VWz9YIJgdv8I7n\/gS\/ffm3rLoO9UwtFW9G8NY\/BAJ4SqgqEkHUQGMAoLUifX\/X4NduiYtx8OZrtLnFRdje3OL01g4eldnsiN2tCf\/6kvP7yyPePFzw6s0Foxw0uuLD\/evI3gMPRy2FfrWidkvSxgQJoawWaGqGdFB7PLcAg+cGtAaKIHHHQw9eXCMQKiaCq+ACSiWboRr0fYdQ2J84261xaz7Y3MbhsUvwB7\/yQ03P6NWiAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-dispatcher_big.png\u0022 alt=\u0022Avris Dispatcher\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EAn event dispatcher\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs bash border\u0022\u003Ecomposer require avris\/dispatcher\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EEvent Definition\u003C\/h3\u003E\n\u003Cp\u003EAn event is an object that extends \u003Ccode\u003EAvris\\Dispatcher\\Event\u003C\/code\u003E.\nIt has to provide its name (used as an identifier for triggering it)\nand a setter and getter for its value (for instance if \u003Ccode\u003ERequestEvent\u003C\/code\u003E is supposed to produce a \u003Ccode\u003EResponse\u003C\/code\u003E, that response is the value).\u003C\/p\u003E\n\u003Cp\u003EFor example:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efinal\u003C\/span\u003E \u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003ERequestEvent\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EEvent\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/** \u003Cspan class=\u0022hljs-doctag\u0022\u003E@var\u003C\/span\u003E RequestInterface *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $request;\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/** \u003Cspan class=\u0022hljs-doctag\u0022\u003E@var\u003C\/span\u003E ResponseInterface *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $response;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003E__construct\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E(RequestInterface $request)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;request = $request;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetName\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Estring\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027request\u0027\u003C\/span\u003E;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetRequest\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003ERequestInterface\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;request;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetResponse\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: ?\u003Cspan class=\u0022hljs-title\u0022\u003EResponseInterface\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;response;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EsetResponse\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E(ResponseInterface $response)\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Eself\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;response = $response;\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EsetValue\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($value)\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003EEvent\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;response = $value;\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetValue\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;response;\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EAttaching listeners\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$dispatcher = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E EventDispatcher();\n$dispatcher-\u0026gt;attachListener(\u2019request\u2019, \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E(RequestEvent $event)\u003C\/span\u003E \u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (substr($event-\u0026gt;getRequest()-\u0026gt;getUrl(), \u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E, \u003Cspan class=\u0022hljs-number\u0022\u003E6\u003C\/span\u003E) === \u2018\/admin\u2019 \u0026amp;\u0026amp; !is_admin()) {\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Response(\u2019Forbidden\u2019, \u003Cspan class=\u0022hljs-number\u0022\u003E403\u003C\/span\u003E);\n    }\n});\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EReturning anything other than \u003Ccode\u003Enull\u003C\/code\u003E will automatically set event\u2019s value\n(you can also do it explicitly with \u003Ccode\u003E$event-\u0026gt;setResponse($response)\u003C\/code\u003E).\u003C\/p\u003E\n\u003Cp\u003EAs a last argument of \u003Ccode\u003EattachListener\u003C\/code\u003E you can specify the priority of the listener, for example:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$dispatcher-\u0026gt;attachListener(\u2019request\u2019, [\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E, \u2018one\u2019]);\n$dispatcher-\u0026gt;attachListener(\u2019request\u2019, [\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E, \u2018two\u2019]);\n$dispatcher-\u0026gt;attachListener(\u2019request\u2019, [\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E, \u2018three\u2019], \u003Cspan class=\u0022hljs-number\u0022\u003E-9\u003C\/span\u003E);\n$dispatcher-\u0026gt;attachListener(\u2019request\u2019, [\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E, \u2018four\u2019], \u003Cspan class=\u0022hljs-number\u0022\u003E9\u003C\/span\u003E);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EThe listeners will be triggered in order: \u003Ccode\u003Efour\u003C\/code\u003E, \u003Ccode\u003Eone\u003C\/code\u003E, \u003Ccode\u003Etwo\u003C\/code\u003E, \u003Ccode\u003Ethree\u003C\/code\u003E.\u003C\/p\u003E\n\u003Cp\u003ETo stop propagation of an event, use \u003Ccode\u003E$event-\u0026gt;stopPropagation()\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003ETriggering events\u003C\/h3\u003E\n\u003Cp\u003ETo trigger an event:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E$response = $dispatcher-\u0026gt;trigger(new RequestEvent($request));\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAs you can see, the value of the event after executing the listeners will be returned for convenience\n(again, you can explicitly use \u003Ccode\u003E$event-\u0026gt;getResponse()\u003C\/code\u003E).\u003C\/p\u003E\n\u003Ch3\u003EEvent subscribers\u003C\/h3\u003E\n\u003Cp\u003EA subscriber is an object that defines event listeners in an elegant manner,\nencapsulating possibly multiple listeners related to one service.\u003C\/p\u003E\n\u003Cp\u003EIt needs to implement \u003Ccode\u003EAvris\\Dispatcher\\EventSubscriberInterface\u003C\/code\u003E:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efinal\u003C\/span\u003E \u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EFooService\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eimplements\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EEventSubscriberInterface\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetSubscribedEvents\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Eiterable\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eyield\u003C\/span\u003E \u2018request\u2019 =\u0026gt; [\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E, \u2018onRequest\u2019];\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eyield\u003C\/span\u003E \u2018response:\u003Cspan class=\u0022hljs-number\u0022\u003E9\u003C\/span\u003E\u2019 =\u0026gt; [\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E, \u2018onResponse\u2019]; \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ priority 9\u003C\/span\u003E\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ETo register them in the dispatcher:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$fooService = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E FooService();\n\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;dispatcher-\u0026gt;registerSubscriber($fooService);\n\u003C\/code\u003E\u003C\/pre\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["dispatcher","event","library","php"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-dispatcher_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-dispatcher_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-dispatcher_mini.png\u0022 alt=\u0022Avris Dispatcher\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EAn event dispatcher\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-dispatcher_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-dispatcher_mini.png\u0022 alt=\u0022Avris Dispatcher\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EAn event dispatcher\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Ecomposer require avris\/dispatcher\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EEvent Definition\u003C\/h3\u003E\n\u003Cp\u003EAn event is an object that extends \u003Ccode\u003EAvris\\Dispatcher\\Event\u003C\/code\u003E.\nIt has to provide its name (used as an identifier for triggering it)\nand a setter and getter for its value (for instance if \u003Ccode\u003ERequestEvent\u003C\/code\u003E is supposed to produce a \u003Ccode\u003EResponse\u003C\/code\u003E, that response is the value).\u003C\/p\u003E\n\u003Cp\u003EFor example:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003Efinal class RequestEvent extends Event\n{\n    \/** @var RequestInterface *\/\n    private $request;\n\n    \/** @var ResponseInterface *\/\n    private $response;\n\n    public function __construct(RequestInterface $request)\n    {\n        $this-\u0026gt;request = $request;\n    }\n\n    public function getName(): string\n    {\n        return \u0027request\u0027;\n    }\n\n    public function getRequest(): RequestInterface\n    {\n        return $this-\u0026gt;request;\n    }\n\n    public function getResponse(): ?ResponseInterface\n    {\n        return $this-\u0026gt;response;\n    }\n\n    public function setResponse(ResponseInterface $response): self\n    {\n        $this-\u0026gt;response = $response;\n\n        return $this;\n    }\n\n    public function setValue($value): Event\n    {\n        $this-\u0026gt;response = $value;\n\n        return $this;\n    }\n\n    public function getValue()\n    {\n        return $this-\u0026gt;response;\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EAttaching listeners\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$dispatcher = new EventDispatcher();\n$dispatcher-\u0026gt;attachListener(\u2019request\u2019, function (RequestEvent $event) {\n    if (substr($event-\u0026gt;getRequest()-\u0026gt;getUrl(), 0, 6) === \u2018\/admin\u2019 \u0026amp;\u0026amp; !is_admin()) {\n        return new Response(\u2019Forbidden\u2019, 403);\n    }\n});\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EReturning anything other than \u003Ccode\u003Enull\u003C\/code\u003E will automatically set event\u2019s value\n(you can also do it explicitly with \u003Ccode\u003E$event-\u0026gt;setResponse($response)\u003C\/code\u003E).\u003C\/p\u003E\n\u003Cp\u003EAs a last argument of \u003Ccode\u003EattachListener\u003C\/code\u003E you can specify the priority of the listener, for example:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$dispatcher-\u0026gt;attachListener(\u2019request\u2019, [$this, \u2018one\u2019]);\n$dispatcher-\u0026gt;attachListener(\u2019request\u2019, [$this, \u2018two\u2019]);\n$dispatcher-\u0026gt;attachListener(\u2019request\u2019, [$this, \u2018three\u2019], -9);\n$dispatcher-\u0026gt;attachListener(\u2019request\u2019, [$this, \u2018four\u2019], 9);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EThe listeners will be triggered in order: \u003Ccode\u003Efour\u003C\/code\u003E, \u003Ccode\u003Eone\u003C\/code\u003E, \u003Ccode\u003Etwo\u003C\/code\u003E, \u003Ccode\u003Ethree\u003C\/code\u003E.\u003C\/p\u003E\n\u003Cp\u003ETo stop propagation of an event, use \u003Ccode\u003E$event-\u0026gt;stopPropagation()\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003ETriggering events\u003C\/h3\u003E\n\u003Cp\u003ETo trigger an event:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E$response = $dispatcher-\u0026gt;trigger(new RequestEvent($request));\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAs you can see, the value of the event after executing the listeners will be returned for convenience\n(again, you can explicitly use \u003Ccode\u003E$event-\u0026gt;getResponse()\u003C\/code\u003E).\u003C\/p\u003E\n\u003Ch3\u003EEvent subscribers\u003C\/h3\u003E\n\u003Cp\u003EA subscriber is an object that defines event listeners in an elegant manner,\nencapsulating possibly multiple listeners related to one service.\u003C\/p\u003E\n\u003Cp\u003EIt needs to implement \u003Ccode\u003EAvris\\Dispatcher\\EventSubscriberInterface\u003C\/code\u003E:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003Efinal class FooService implements EventSubscriberInterface\n{\n    public function getSubscribedEvents(): iterable\n    {\n        yield \u2018request\u2019 =\u0026gt; [$this, \u2018onRequest\u2019];\n        yield \u2018response:9\u2019 =\u0026gt; [$this, \u2018onResponse\u2019]; \/\/ priority 9\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ETo register them in the dispatcher:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$fooService = new FooService();\n$this-\u0026gt;dispatcher-\u0026gt;registerSubscriber($fooService);\u003C\/code\u003E\u003C\/pre\u003E","words":349,"readTime":2,"lang":"en"}}},"projects\/avris-bag-nicer-arrays-for-php":{"key":"projects\/avris-bag-nicer-arrays-for-php","type":"article","published":true,"meta":{"createdAt":"2017-12-25T21:04:49+01:00","publishedAt":"2017-12-25T21:02:00+01:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Bag","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-bag-nicer-arrays-for-php"},"content":{"en":{"slug":"avris-bag-nicer-arrays-for-php","title":"Avris Bag \u2022 Nicer arrays for PHP","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-bag_small.png\u0022 alt=\u0022Avris Bag\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHlElEQVRIiX2X268lRRXGf+tS1b3PPvtc5nKGmQGGmyiiTiQaSYhhJEEgBhWNFzQh+qAPGh\/8E3z3zQej8VmfjC+IiUYjkECiSLwAInKZGRgGMsycOefsc9vdVcuH3jMYNVa6ujvpunz1fbVqfS3X33B7RKkIQdQKUTF3aikEgYrgapgqyZ3ZbJ9aK0RABAKICMJQRAQRAYIrJSLmtV59EhURUBHMHROhaUZ4hBARlFqQAAFKKQjDwKo6ABWFWkmq9PMJEJm342p7EQBBVd6dXK68C1ELokbEu\/0MIamhteBXOghKUIkADUF0mIwQVB0Q+lIxBEUIUUx1zsawEGT4Nr\/mN8PdmM06RCBiWKCIovO+poqbkVTxEIVa5gQPjBCBhKBquCkmgonipkhUaq3UWoYB3IfV14qqXZVKRYhaCYLllRUuX1oHKoQgDlELqgIBZkoyI\/kVQAKoQJ2zJSCicxkMRTEzsiUWmgaiZ2d3l8adJjnZE7VWSunpSwFh2C+1AnD0yBr7W1OgDqRFoJrnoBTTQeJsiosmIjokDKSiVzepoGYDtZZQUVQMFWfcjFkdLbM+3UBDiRI0nlDPEIUQoe+7gSGBE9dfz1unzyISSICpIASQEIm5ZEoyxTVl+mAeMXYlVkB1YEcNUyO7ky2hGCKJJiduPLzI9s6U\/a7jyDVrzHZ3mG5uYSJkV2pU7vrk3bzywoss5YYghsgCVAdRVEEEkinZDQ9NSJrLhVADTBRVQ0Vxc1yN7BkXIZmTJJG04cCBZT73tS9wYHWF3\/\/8V7x+5nWsWST5EE2T5UXuuudunn\/8KVbaEVCIiAGMCipgJpgOrLkLTm6Q3qk15voCKKKGmiNiuDmqCTeb18SpBz7Ow9\/+Ei8983ce\/cHPSBRuPXqMqJWt7Smeja9+77s884vHuHZpiVnpqNETUQbpdB5dadhDOSmm4NKOqV1Bah3OoQDEEHHUEskcVSOZ06bMgjtf\/9bnue+hU0zXt3jsR4+SdcTqQsvNtxxje+MdNnb2uOc7XyGpUM+8wfuOrjHd3WGvn1FqT6kdaCW5YSa4K8mFlAyXhSVk1kOpBAx7BEUkoWKIJ7I6bUospMTDj9zLfQ+dAuCvTz5HomGyOGFxoWHr0i4nPvIhPvngXXjb8PT3f8LaoWU0OjY2g75PlBgAhVRUg5QddyElxbPgMppA7qGAhEIogoI4aMLUSebklDh58kY+\/cVPXE0Jm29POXjkMMdPrHHi9uu47o6bGB9aIiJ4\/ddPMB5lSl2kbXpyU0EqNTr6fkbUDjVQDcyN3DieBGeyDH0hQgYwMUSX4bgmFEfmB+Aj33zg6skMcP837uU\/S0Rw7ok\/MbvwDodvOMj+RqbbvsySdohVxIJaO2q\/j9CjJpgrKWe8aXCdTEiFIc+geCgJI4uTw8hqZJQ7Tl7P8eMH\/gvAv5daCq\/85o9svfQK7Vhplxc4fNu19BtjNs8LKjOEDiQTJSFShwhLjuWMNSN8sjKhFGDOkCF4OK0YLUqL0VThzo\/d8n\/BTC9s8MIvn2J28QKjBlDodnfYudjhMmNhuaF2ZTh7DFQSMpdL3bHcos0Iv+bAiL4PaoEaioaiGCmEhNGG0hTh1psO\/U8gu5s7\/OPJ5zj37Itk6Ri1dUgdFaKDum\/QKHmkRFJMYi5TQk2xlNHcIKmBPMI\/sNaws9cx2y\/M+koplVIrEoJExYpgIhxaXbgKYnu6y5mXznH6L69y7vnXaKJjwQuRCk4layU0wAJmQ66UFCQP3AQzQVNCcgvJIGfIDXiLP7j6Jtt7lem0Y3O7Mu2DrX3YmcFeB7NeKD38+Ic\/ZXd7l8vvbLJ9aZNcColCotJqZWQ94xQstZXZQoVJkEJobbAZUjrUA8uG1aDrDGNCt29om9hYX6CjRU7dfiiW2sTe3mCV3DIhTsUJSRScqpm+QoiCOiKKq5HcOXbkCBvr62QTGoVxhkmGw+Pg6Gph7ZAxHgtnz19murtLyR0rx47z+G\/\/wOnz+yy1lbW1Cftd8PQL28jiyoHQlBE1LCW8aRE1PDd4ylhqUDVy02KeSJ5xSzTNiIPLyzz82Qd49tnnWF0cE33htef\/yXIyVltlpYWDi8rqGJL2JOtZGffcenKBy+enXDi\/yWQELj2H77yXnb\/9Dr\/tM1+mWVyir9COJ3hqUHXEnOQJEUMY8o6EIKVCKUgNRma8fHGHxWMn8JQ4fmiZ1y7s0wvsm7LvwnZScjLaVKlW2Wsqna1w4L1wefoqaSQ03tOv77D4\/vvwgjHd2QVLzLa2ENkGdPBCMRjwwQsxB1ORWql7e1ALb559GSmBRuAEVgWthYWUmSRn4s5yk1hIgmthIcN0r+OjH1zijXWlXupZGsHk8pu85\/4P4+tnXmbWdahnaql4M4J3\/yEQwFNCVZEIog6TSwRaK9L3Vw1+7fZxMS6\/fZY2t7gIy4tLHFxawaOys7PJ6tKEP7\/ovHVmxNsbe5y+uMcoB43O+FT\/BnLo5tuilkI\/m1G7fdJ4goRQZntoaoZ0UHs8twCD5wa0BoogccVDD15cIxAqJoKr4AJKJZuhGvR9h1BYmzjLrXFpd7C5jcOdJ+BfJyEzM18yLlUAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-bag_small.png\u0022 alt=\u0022Avris Bag\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cem\u003EAvris Bag\u003C\/em\u003E is a set of helpers that make working with arrays in PHP way easier.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-bag_big.png\u0022 alt=\u0022Avris Bag\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHlElEQVRIiX2X268lRRXGf+tS1b3PPvtc5nKGmQGGmyiiTiQaSYhhJEEgBhWNFzQh+qAPGh\/8E3z3zQej8VmfjC+IiUYjkECiSLwAInKZGRgGMsycOefsc9vdVcuH3jMYNVa6ujvpunz1fbVqfS3X33B7RKkIQdQKUTF3aikEgYrgapgqyZ3ZbJ9aK0RABAKICMJQRAQRAYIrJSLmtV59EhURUBHMHROhaUZ4hBARlFqQAAFKKQjDwKo6ABWFWkmq9PMJEJm342p7EQBBVd6dXK68C1ELokbEu\/0MIamhteBXOghKUIkADUF0mIwQVB0Q+lIxBEUIUUx1zsawEGT4Nr\/mN8PdmM06RCBiWKCIovO+poqbkVTxEIVa5gQPjBCBhKBquCkmgonipkhUaq3UWoYB3IfV14qqXZVKRYhaCYLllRUuX1oHKoQgDlELqgIBZkoyI\/kVQAKoQJ2zJSCicxkMRTEzsiUWmgaiZ2d3l8adJjnZE7VWSunpSwFh2C+1AnD0yBr7W1OgDqRFoJrnoBTTQeJsiosmIjokDKSiVzepoGYDtZZQUVQMFWfcjFkdLbM+3UBDiRI0nlDPEIUQoe+7gSGBE9dfz1unzyISSICpIASQEIm5ZEoyxTVl+mAeMXYlVkB1YEcNUyO7ky2hGCKJJiduPLzI9s6U\/a7jyDVrzHZ3mG5uYSJkV2pU7vrk3bzywoss5YYghsgCVAdRVEEEkinZDQ9NSJrLhVADTBRVQ0Vxc1yN7BkXIZmTJJG04cCBZT73tS9wYHWF3\/\/8V7x+5nWsWST5EE2T5UXuuudunn\/8KVbaEVCIiAGMCipgJpgOrLkLTm6Q3qk15voCKKKGmiNiuDmqCTeb18SpBz7Ow9\/+Ei8983ce\/cHPSBRuPXqMqJWt7Smeja9+77s884vHuHZpiVnpqNETUQbpdB5dadhDOSmm4NKOqV1Bah3OoQDEEHHUEskcVSOZ06bMgjtf\/9bnue+hU0zXt3jsR4+SdcTqQsvNtxxje+MdNnb2uOc7XyGpUM+8wfuOrjHd3WGvn1FqT6kdaCW5YSa4K8mFlAyXhSVk1kOpBAx7BEUkoWKIJ7I6bUospMTDj9zLfQ+dAuCvTz5HomGyOGFxoWHr0i4nPvIhPvngXXjb8PT3f8LaoWU0OjY2g75PlBgAhVRUg5QddyElxbPgMppA7qGAhEIogoI4aMLUSebklDh58kY+\/cVPXE0Jm29POXjkMMdPrHHi9uu47o6bGB9aIiJ4\/ddPMB5lSl2kbXpyU0EqNTr6fkbUDjVQDcyN3DieBGeyDH0hQgYwMUSX4bgmFEfmB+Aj33zg6skMcP837uU\/S0Rw7ok\/MbvwDodvOMj+RqbbvsySdohVxIJaO2q\/j9CjJpgrKWe8aXCdTEiFIc+geCgJI4uTw8hqZJQ7Tl7P8eMH\/gvAv5daCq\/85o9svfQK7Vhplxc4fNu19BtjNs8LKjOEDiQTJSFShwhLjuWMNSN8sjKhFGDOkCF4OK0YLUqL0VThzo\/d8n\/BTC9s8MIvn2J28QKjBlDodnfYudjhMmNhuaF2ZTh7DFQSMpdL3bHcos0Iv+bAiL4PaoEaioaiGCmEhNGG0hTh1psO\/U8gu5s7\/OPJ5zj37Itk6Ri1dUgdFaKDum\/QKHmkRFJMYi5TQk2xlNHcIKmBPMI\/sNaws9cx2y\/M+koplVIrEoJExYpgIhxaXbgKYnu6y5mXznH6L69y7vnXaKJjwQuRCk4layU0wAJmQ66UFCQP3AQzQVNCcgvJIGfIDXiLP7j6Jtt7lem0Y3O7Mu2DrX3YmcFeB7NeKD38+Ic\/ZXd7l8vvbLJ9aZNcColCotJqZWQ94xQstZXZQoVJkEJobbAZUjrUA8uG1aDrDGNCt29om9hYX6CjRU7dfiiW2sTe3mCV3DIhTsUJSRScqpm+QoiCOiKKq5HcOXbkCBvr62QTGoVxhkmGw+Pg6Gph7ZAxHgtnz19murtLyR0rx47z+G\/\/wOnz+yy1lbW1Cftd8PQL28jiyoHQlBE1LCW8aRE1PDd4ylhqUDVy02KeSJ5xSzTNiIPLyzz82Qd49tnnWF0cE33htef\/yXIyVltlpYWDi8rqGJL2JOtZGffcenKBy+enXDi\/yWQELj2H77yXnb\/9Dr\/tM1+mWVyir9COJ3hqUHXEnOQJEUMY8o6EIKVCKUgNRma8fHGHxWMn8JQ4fmiZ1y7s0wvsm7LvwnZScjLaVKlW2Wsqna1w4L1wefoqaSQ03tOv77D4\/vvwgjHd2QVLzLa2ENkGdPBCMRjwwQsxB1ORWql7e1ALb559GSmBRuAEVgWthYWUmSRn4s5yk1hIgmthIcN0r+OjH1zijXWlXupZGsHk8pu85\/4P4+tnXmbWdahnaql4M4J3\/yEQwFNCVZEIog6TSwRaK9L3Vw1+7fZxMS6\/fZY2t7gIy4tLHFxawaOys7PJ6tKEP7\/ovHVmxNsbe5y+uMcoB43O+FT\/BnLo5tuilkI\/m1G7fdJ4goRQZntoaoZ0UHs8twCD5wa0BoogccVDD15cIxAqJoKr4AJKJZuhGvR9h1BYmzjLrXFpd7C5jcOdJ+BfJyEzM18yLlUAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-bag_big.png\u0022 alt=\u0022Avris Bag\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cem\u003EAvris Bag\u003C\/em\u003E is a set of helpers that make working with arrays in PHP way easier.\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs bash border\u0022\u003Ecomposer require avris\/bag\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EBag\u003C\/h3\u003E\n\u003Cp\u003E\u003Cem\u003EBag\u003C\/em\u003E class represents a key-value store. It can be used both as an array and as an object, leading to a shorter and nicer code:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$array = [\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E,\n    \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027lorem\u0027\u003C\/span\u003E =\u0026gt; [\n        \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027ipsum\u0027\u003C\/span\u003E =\u0026gt; [\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027dolor\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027sit\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027amet\u0027\u003C\/span\u003E]\n    ],\n];\n\n$bag = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Bag($array);\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all return \u0027bar\u0027\u003C\/span\u003E\nvar_dump(\u003Cspan class=\u0022hljs-keyword\u0022\u003Eisset\u003C\/span\u003E($array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E]) ? $array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E] : \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E);\nvar_dump($bag-\u0026gt;get(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E));\nvar_dump($bag[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E]);\nvar_dump($bag(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E));\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all return null\u003C\/span\u003E\nvar_dump(\u003Cspan class=\u0022hljs-keyword\u0022\u003Eisset\u003C\/span\u003E($array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E]) ? $array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E] : \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E);\nvar_dump($bag-\u0026gt;get(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E));\nvar_dump($bag[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E]);\nvar_dump($bag(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E));\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all return \u0027default\u0027\u003C\/span\u003E\nvar_dump(\u003Cspan class=\u0022hljs-keyword\u0022\u003Eisset\u003C\/span\u003E($array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E]) ? $array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E] : \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027default\u0027\u003C\/span\u003E);\nvar_dump($bag-\u0026gt;get(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027default\u0027\u003C\/span\u003E));\nvar_dump($bag[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E] ?: \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027default\u0027\u003C\/span\u003E);\nvar_dump($bag(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027nonexistent\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027default\u0027\u003C\/span\u003E));\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all are equivalent\u003C\/span\u003E\n$array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027x\u0027\u003C\/span\u003E] =\u0026gt; \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027y\u0027\u003C\/span\u003E;\n$bag-\u0026gt;set(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027x\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027y\u0027\u003C\/span\u003E);\n$bag[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027x\u0027\u003C\/span\u003E] = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027y\u0027\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all are equivalent\u003C\/span\u003E\nvar_dump(count($array));\nvar_dump($bag-\u0026gt;count());\nvar_dump(count($bag));\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all are equivalent\u003C\/span\u003E\nvar_dump(count($array) === \u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E);\nvar_dump($bag-\u0026gt;isEmpty());\nvar_dump(count($bag) === \u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E);\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all are equivalent\u003C\/span\u003E\nvar_dump(array_keys($array));\nvar_dump($bag-\u0026gt;keys());\nvar_dump(array_keys($bag));\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all are equivalent\u003C\/span\u003E\nvar_dump(\u003Cspan class=\u0022hljs-keyword\u0022\u003Eisset\u003C\/span\u003E($array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E]));\nvar_dump($bag-\u0026gt;has(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E));\nvar_dump(\u003Cspan class=\u0022hljs-keyword\u0022\u003Eisset\u003C\/span\u003E($bag[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E]));\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all are equivalent\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Eunset\u003C\/span\u003E($array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E]);\n$bag-\u0026gt;delete(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E);\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Eunset\u003C\/span\u003E($bag[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E]);\n\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ all are equivalent\u003C\/span\u003E\n$array = [];\n$bag-\u0026gt;clear();\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EJust like with a simple array, you can also \u003Cstrong\u003Eiterate\u003C\/strong\u003E over a Bag and \u003Cstrong\u003Ejson_encode\u003C\/strong\u003E it.\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EAdditional features\u003C\/strong\u003E include:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;getDeep($key, $default = null)\u003C\/code\u003E \u2013 gets a value from a nested tree, using dot-separated string as a key, for instance \u003Ccode\u003E$bag-\u0026gt;getDeep(\u0027lorem.ipsum.1\u0027)\u003C\/code\u003E will return \u2018sit\u2019;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;add(array)\u003C\/code\u003E \u2013 merges the other array into the Bag, without overwriting the existing keys;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;replace(array)\u003C\/code\u003E \u2013 merges the other array into the Bag, with overwriting the existing keys;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;appendToElement($key, $value)\u003C\/code\u003E \u2013 treats \u003Ccode\u003E$bag[$key]\u003C\/code\u003E as a list and appends \u003Ccode\u003E$value\u003C\/code\u003E at its end;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;prependToElement($key, $value)\u003C\/code\u003E \u2013 treats \u003Ccode\u003E$bag[$key]\u003C\/code\u003E as a list and prepends \u003Ccode\u003E$value\u003C\/code\u003E at its beginning;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;map(function ($key, $value) {})\u003C\/code\u003E \u2013 returns a new Bag, with values mapped by the callback function;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;filter(function ($key, $value) {})\u003C\/code\u003E \u2013 returns a new Bag, with only those values from the original Bag, for which the callback function returns true.\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;flatten()\u003C\/code\u003E \u2013 returns a new Bag, with keys flattened, for instance \u003Ccode\u003Elorem.ipsum\u003C\/code\u003E.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Ch3\u003ESet\u003C\/h3\u003E\n\u003Cp\u003ESet is a similar structure, but it doesn\u2019t care about the keys and it makes sure that all the values are unique. Sometimes it\u2019s helpful to instantiate it with a callback:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$set = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Set([\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027post\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027get\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027GET\u0027\u003C\/span\u003E], \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027strtoupper\u0027\u003C\/span\u003E);\n\u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ internal values are: \u0027POST\u0027 and \u0027GET\u0027\u003C\/span\u003E\n\nvar_dump($set-\u0026gt;has(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027post\u0027\u003C\/span\u003E)); \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ true\u003C\/span\u003E\nvar_dump($set-\u0026gt;has(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027POST\u0027\u003C\/span\u003E)); \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ true\u003C\/span\u003E\nvar_dump($set-\u0026gt;has(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027DELETE\u0027\u003C\/span\u003E)); \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ false\u003C\/span\u003E\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EMethods:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;add($value)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;addMultiple(array $values)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;has($value)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;delete($value)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;first()\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;last()\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;clear()\u003C\/code\u003E\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Ch3\u003EBagHelper\u003C\/h3\u003E\n\u003Cp\u003EThis class provides four static methods:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003EBagHelper::isArray($object)\u003C\/code\u003E \u2013 checks if \u003Ccode\u003E$object\u003C\/code\u003E is an array or array-like (\u003Ccode\u003E\\Traversable\u003C\/code\u003E);\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003EBagHelper::toArray($object)\u003C\/code\u003E \u2013 converts \u003Ccode\u003E$object\u003C\/code\u003E to an array;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003EBagHelper::magicGetter($object, $attr, $default = null)\u003C\/code\u003E \u2013 tries to get a value from object, using:\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003E$object[\u0027key\u0027]\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;key()\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;getKey()\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;key\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;get(\u0027key\u0027)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003Eor fallbacks to \u003Ccode\u003E$default\u003C\/code\u003E.\u003C\/li\u003E\n\u003C\/ul\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003EBagHelper::magicSetter($object, $attr, $value)\u003C\/code\u003E \u2013 tries to set a value to object, using:\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003E$object[\u0027key\u0027] = $value\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;setKey($value)\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;key = $value\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;set($key, $value)\u003C\/code\u003E\u003C\/li\u003E\n\u003C\/ul\u003E\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Ch3\u003ENested\u003C\/h3\u003E\n\u003Cp\u003EThis class provides two static methods for a nested access to arrays and objects:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ENested::get($object, array $keys, $default = null)\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003ENested::set(array \u0026amp;$array, array $keys, $value)\u003C\/code\u003E (only arrays are supported).\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EFor example:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003ENested::get($container, [\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027github\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027webhooks\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E]);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Ecould return a value of:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$container-\u0026gt;get(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027github\u0027\u003C\/span\u003E)-\u0026gt;getWebhooks()[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E];\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eunless any of the chain elements doesn\u2019t exist \u2013 then \u003Ccode\u003E$default\u003C\/code\u003E is returned.\u003C\/p\u003E\n\u003Cp\u003ESimilarly:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003ENested::set($array, [\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027baz\u0027\u003C\/span\u003E], \u003Cspan class=\u0022hljs-number\u0022\u003E8\u003C\/span\u003E);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eis in equivalent to:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$array[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E][\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E][\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027baz\u0027\u003C\/span\u003E] = \u003Cspan class=\u0022hljs-number\u0022\u003E8\u003C\/span\u003E;\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Ebut it creates all the arrays on the way, if they don\u2019t exist yet.\u003C\/p\u003E\n\u003Ch3\u003EQueueBag\u003C\/h3\u003E\n\u003Cp\u003EThis class works like a normal queue, but lets you enqueue and dequeue\na key-value pair and gives you standard \u003Ccode\u003EBag\u003C\/code\u003E features.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$queue = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E QueueBag();\n$queue-\u0026gt;enqueue(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027key\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027value\u0027\u003C\/span\u003E)\n$queue-\u0026gt;enqueue(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E)\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Elist\u003C\/span\u003E($key, $value) = $queue-\u0026gt;dequeue()\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EStackBag\u003C\/h3\u003E\n\u003Cp\u003EThis class works like a normal stack, but lets you push and pop\na key-value pair and gives you standard \u003Ccode\u003EBag\u003C\/code\u003E features.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$stack = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E StackBag();\n$stack-\u0026gt;push(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027key\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027value\u0027\u003C\/span\u003E)\n$stack-\u0026gt;push(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027foo\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027bar\u0027\u003C\/span\u003E)\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Elist\u003C\/span\u003E($key, $value) = $stack-\u0026gt;pop()\n\u003C\/code\u003E\u003C\/pre\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["accessor","array","helper","php","queue","stack"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-bag_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-bag_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-bag_mini.png\u0022 alt=\u0022Avris Bag\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cem\u003EAvris Bag\u003C\/em\u003E is a set of helpers that make working with arrays in PHP way easier.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-bag_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-bag_mini.png\u0022 alt=\u0022Avris Bag\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cem\u003EAvris Bag\u003C\/em\u003E is a set of helpers that make working with arrays in PHP way easier.\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Ecomposer require avris\/bag\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EBag\u003C\/h3\u003E\n\u003Cp\u003E\u003Cem\u003EBag\u003C\/em\u003E class represents a key-value store. It can be used both as an array and as an object, leading to a shorter and nicer code:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$array = [\n    \u0027foo\u0027 =\u0026gt; \u0027bar\u0027,\n    \u0027lorem\u0027 =\u0026gt; [\n        \u0027ipsum\u0027 =\u0026gt; [\u0027dolor\u0027, \u0027sit\u0027, \u0027amet\u0027]\n    ],\n];\n\n$bag = new Bag($array);\n\n\/\/ all return \u0027bar\u0027\nvar_dump(isset($array[\u0027foo\u0027]) ? $array[\u0027foo\u0027] : null);\nvar_dump($bag-\u0026gt;get(\u0027foo\u0027));\nvar_dump($bag[\u0027foo\u0027]);\nvar_dump($bag(\u0027foo\u0027));\n\n\/\/ all return null\nvar_dump(isset($array[\u0027nonexistent\u0027]) ? $array[\u0027nonexistent\u0027] : null);\nvar_dump($bag-\u0026gt;get(\u0027nonexistent\u0027));\nvar_dump($bag[\u0027nonexistent\u0027]);\nvar_dump($bag(\u0027nonexistent\u0027));\n\n\/\/ all return \u0027default\u0027\nvar_dump(isset($array[\u0027nonexistent\u0027]) ? $array[\u0027nonexistent\u0027] : \u0027default\u0027);\nvar_dump($bag-\u0026gt;get(\u0027nonexistent\u0027, \u0027default\u0027));\nvar_dump($bag[\u0027nonexistent\u0027] ?: \u0027default\u0027);\nvar_dump($bag(\u0027nonexistent\u0027, \u0027default\u0027));\n\n\/\/ all are equivalent\n$array[\u0027x\u0027] =\u0026gt; \u0027y\u0027;\n$bag-\u0026gt;set(\u0027x\u0027, \u0027y\u0027);\n$bag[\u0027x\u0027] = \u0027y\u0027;\n\n\/\/ all are equivalent\nvar_dump(count($array));\nvar_dump($bag-\u0026gt;count());\nvar_dump(count($bag));\n\n\/\/ all are equivalent\nvar_dump(count($array) === 0);\nvar_dump($bag-\u0026gt;isEmpty());\nvar_dump(count($bag) === 0);\n\n\/\/ all are equivalent\nvar_dump(array_keys($array));\nvar_dump($bag-\u0026gt;keys());\nvar_dump(array_keys($bag));\n\n\/\/ all are equivalent\nvar_dump(isset($array[\u0027foo\u0027]));\nvar_dump($bag-\u0026gt;has(\u0027foo\u0027));\nvar_dump(isset($bag[\u0027foo\u0027]));\n\n\/\/ all are equivalent\nunset($array[\u0027foo\u0027]);\n$bag-\u0026gt;delete(\u0027foo\u0027);\nunset($bag[\u0027foo\u0027]);\n\n\/\/ all are equivalent\n$array = [];\n$bag-\u0026gt;clear();\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EJust like with a simple array, you can also \u003Cstrong\u003Eiterate\u003C\/strong\u003E over a Bag and \u003Cstrong\u003Ejson_encode\u003C\/strong\u003E it.\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EAdditional features\u003C\/strong\u003E include:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;getDeep($key, $default = null)\u003C\/code\u003E \u2013 gets a value from a nested tree, using dot-separated string as a key, for instance \u003Ccode\u003E$bag-\u0026gt;getDeep(\u0027lorem.ipsum.1\u0027)\u003C\/code\u003E will return \u2018sit\u2019;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;add(array)\u003C\/code\u003E \u2013 merges the other array into the Bag, without overwriting the existing keys;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;replace(array)\u003C\/code\u003E \u2013 merges the other array into the Bag, with overwriting the existing keys;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;appendToElement($key, $value)\u003C\/code\u003E \u2013 treats \u003Ccode\u003E$bag[$key]\u003C\/code\u003E as a list and appends \u003Ccode\u003E$value\u003C\/code\u003E at its end;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;prependToElement($key, $value)\u003C\/code\u003E \u2013 treats \u003Ccode\u003E$bag[$key]\u003C\/code\u003E as a list and prepends \u003Ccode\u003E$value\u003C\/code\u003E at its beginning;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;map(function ($key, $value) {})\u003C\/code\u003E \u2013 returns a new Bag, with values mapped by the callback function;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;filter(function ($key, $value) {})\u003C\/code\u003E \u2013 returns a new Bag, with only those values from the original Bag, for which the callback function returns true.\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$bag-\u0026gt;flatten()\u003C\/code\u003E \u2013 returns a new Bag, with keys flattened, for instance \u003Ccode\u003Elorem.ipsum\u003C\/code\u003E.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Ch3\u003ESet\u003C\/h3\u003E\n\u003Cp\u003ESet is a similar structure, but it doesn\u2019t care about the keys and it makes sure that all the values are unique. Sometimes it\u2019s helpful to instantiate it with a callback:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$set = new Set([\u0027post\u0027, \u0027get\u0027, \u0027GET\u0027], \u0027strtoupper\u0027);\n\/\/ internal values are: \u0027POST\u0027 and \u0027GET\u0027\n\nvar_dump($set-\u0026gt;has(\u0027post\u0027)); \/\/ true\nvar_dump($set-\u0026gt;has(\u0027POST\u0027)); \/\/ true\nvar_dump($set-\u0026gt;has(\u0027DELETE\u0027)); \/\/ false\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EMethods:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;add($value)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;addMultiple(array $values)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;has($value)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;delete($value)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;first()\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;last()\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$set-\u0026gt;clear()\u003C\/code\u003E\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Ch3\u003EBagHelper\u003C\/h3\u003E\n\u003Cp\u003EThis class provides four static methods:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003EBagHelper::isArray($object)\u003C\/code\u003E \u2013 checks if \u003Ccode\u003E$object\u003C\/code\u003E is an array or array-like (\u003Ccode\u003E\\Traversable\u003C\/code\u003E);\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003EBagHelper::toArray($object)\u003C\/code\u003E \u2013 converts \u003Ccode\u003E$object\u003C\/code\u003E to an array;\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003EBagHelper::magicGetter($object, $attr, $default = null)\u003C\/code\u003E \u2013 tries to get a value from object, using:\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003E$object[\u0027key\u0027]\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;key()\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;getKey()\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;key\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;get(\u0027key\u0027)\u003C\/code\u003E\u003C\/li\u003E\n\u003Cli\u003Eor fallbacks to \u003Ccode\u003E$default\u003C\/code\u003E.\u003C\/li\u003E\n\u003C\/ul\u003E\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003EBagHelper::magicSetter($object, $attr, $value)\u003C\/code\u003E \u2013 tries to set a value to object, using:\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003E$object[\u0027key\u0027] = $value\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;setKey($value)\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;key = $value\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003E$object-\u0026gt;set($key, $value)\u003C\/code\u003E\u003C\/li\u003E\n\u003C\/ul\u003E\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Ch3\u003ENested\u003C\/h3\u003E\n\u003Cp\u003EThis class provides two static methods for a nested access to arrays and objects:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ENested::get($object, array $keys, $default = null)\u003C\/code\u003E,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003ENested::set(array \u0026amp;$array, array $keys, $value)\u003C\/code\u003E (only arrays are supported).\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EFor example:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003ENested::get($container, [\u0027github\u0027, \u0027webhooks\u0027, \u0027foo\u0027]);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Ecould return a value of:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$container-\u0026gt;get(\u0027github\u0027)-\u0026gt;getWebhooks()[\u0027foo\u0027];\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eunless any of the chain elements doesn\u2019t exist \u2013 then \u003Ccode\u003E$default\u003C\/code\u003E is returned.\u003C\/p\u003E\n\u003Cp\u003ESimilarly:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003ENested::set($array, [\u0027foo\u0027, \u0027bar\u0027, \u0027baz\u0027], 8);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eis in equivalent to:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$array[\u0027foo\u0027][\u0027bar\u0027][\u0027baz\u0027] = 8;\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Ebut it creates all the arrays on the way, if they don\u2019t exist yet.\u003C\/p\u003E\n\u003Ch3\u003EQueueBag\u003C\/h3\u003E\n\u003Cp\u003EThis class works like a normal queue, but lets you enqueue and dequeue\na key-value pair and gives you standard \u003Ccode\u003EBag\u003C\/code\u003E features.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$queue = new QueueBag();\n$queue-\u0026gt;enqueue(\u0027key\u0027, \u0027value\u0027)\n$queue-\u0026gt;enqueue(\u0027foo\u0027, \u0027bar\u0027)\nlist($key, $value) = $queue-\u0026gt;dequeue()\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EStackBag\u003C\/h3\u003E\n\u003Cp\u003EThis class works like a normal stack, but lets you push and pop\na key-value pair and gives you standard \u003Ccode\u003EBag\u003C\/code\u003E features.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$stack = new StackBag();\n$stack-\u0026gt;push(\u0027key\u0027, \u0027value\u0027)\n$stack-\u0026gt;push(\u0027foo\u0027, \u0027bar\u0027)\nlist($key, $value) = $stack-\u0026gt;pop()\u003C\/code\u003E\u003C\/pre\u003E","words":782,"readTime":4,"lang":"en"}}},"blog\/technology\/perfect-code-coverage":{"key":"blog\/technology\/perfect-code-coverage","type":"article","published":true,"meta":{"createdAt":"2017-12-25T00:26:11+01:00","publishedAt":"2017-12-25T13:30:00+01:00","group":"mocks","category":"blog","subcategory":"technology","slug":"perfect-code-coverage"},"content":{"en":{"slug":"perfect-code-coverage","title":"Perfect code coverage","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/coverage_small.png\u0022 alt=\u0022100% coverage - feels good!\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022203.90824129142\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAPCAYAAACMa21tAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADkklEQVRIibWWXYscRRSGn1Pd1R\/T053t3TXZ3QghaiDoteLdguAvyn\/ywh8RxIg3XngpIoKyH3Eyk5lZp6e7q7qry4tZJ5nNigbMCwUF\/RTn7ToflFhr\/dXVFd57vPeICBsJ4NFaYxxYB3Eo5GnEu1IQBIhzzjvn3lmQt5FSivDvzW3avbF\/V9\/3vHz5EgDnHCcnJ29tSkzX+qpZEUX6jY8rWzFrNwEynbEX3mHyx4SjoyO0DmlbA3iM6pi3c+q6xtU9R\/k9qtUaFSgODg7QOkIp4aK5BGA6nXIcHoMTet\/x8MMHtK1hlGSEjan44acfuXtyQhorYv3qtn6e\/8I3F98C8EHxkC8ffEHt\/+RiYcnyEetVhTWWSyZ89\/x7AB7tfcTx+D1svSbNMlq1Yras8AJf\/fY1AHuUPPn8CVpHWGv5ffYrnW855AjlgdEoAUAHu+lRIgQSXq8AgCjWBOFmr6OIKI4JJHjFoa65iCBQIILWGhWoLcNz4fLyOU+fPkVrTR7toSONUrKpIUHAQ+8ges1THCQcJgcA5NEYPDtrcI7BOeIg3nLjKN9hvBuwtkeFast8+slnWGs5PT0F4Pz8jPRQQyKEfvA0xpIAHg+vFXHjGibN5LqGRiDQdR2iAhBIRgnW2h2uiPMtF2iNBIogVDjvtszZ\/Jz8XsGzZ894\/PgxnRiKaLTJCnjqtSFUslM\/cP2XN+Scw\/sBANMaOtvh\/Zugcw4\/DPhhoLP9DtO3PcYY8jxHKUV+Z4xpWwY\/EIoIRZ4S69vb+2aoJE0I9aYjRYQkTRAr\/8CFiFIkaYTzm1nnPdzfP6EsS8qyxFrLo+OPmV29wJkBaZq1v5jOyfI7uMGjBPbHMSIwDMMmjZvwBNfzqmka0jQFoG0adBSD3OC8p2kNaZrQ9z11XdM7R9PU3D96n+l0SlmWLJdL0jRFRMiyDBmG4ZbE\/L8ahoGqqiiKgqqq0FpT1zXj8RhrLVmWbVlljeHi8pJqvX5nhlarFVmWsVgs0FrT9z3WWrTWtG0LwNnZGefn50jXNn42eYESwXtPURZM51NGaYKIwuNJsozJbEYURwgQxQl39+79JzN1XWOtRSlFURR0XUfTNCRJgjFma9QYQ1EUSNu2vq7rTXCdIsAweMJQcKYmiDMC8XTWMiiNVkLfW1CaIFAMptqMgddeC3EcE8cxAMvlkrIst122WCwoyxIRYT6fs7+\/vz3nvecvzIfJZKZPRaUAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/coverage_small.png\u0022 alt=\u0022100% coverage - feels good!\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022203.90824129142\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHaving 100% of LOC covered by unit tests certainly feels like a great achievement. But beware \u2013 that doesn\u2019t necessarily mean your code is \u003Cem\u003Eperfectly\u003C\/em\u003E covered. Lines of code coverage is a really nice indicator of your app\u2019s stability, but is can also hide some  risks.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/coverage_big.png\u0022 alt=\u0022100% coverage - feels good!\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022407.81648258284\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAPCAYAAACMa21tAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADkklEQVRIibWWXYscRRSGn1Pd1R\/T053t3TXZ3QghaiDoteLdguAvyn\/ywh8RxIg3XngpIoKyH3Eyk5lZp6e7q7qry4tZJ5nNigbMCwUF\/RTn7ToflFhr\/dXVFd57vPeICBsJ4NFaYxxYB3Eo5GnEu1IQBIhzzjvn3lmQt5FSivDvzW3avbF\/V9\/3vHz5EgDnHCcnJ29tSkzX+qpZEUX6jY8rWzFrNwEynbEX3mHyx4SjoyO0DmlbA3iM6pi3c+q6xtU9R\/k9qtUaFSgODg7QOkIp4aK5BGA6nXIcHoMTet\/x8MMHtK1hlGSEjan44acfuXtyQhorYv3qtn6e\/8I3F98C8EHxkC8ffEHt\/+RiYcnyEetVhTWWSyZ89\/x7AB7tfcTx+D1svSbNMlq1Yras8AJf\/fY1AHuUPPn8CVpHWGv5ffYrnW855AjlgdEoAUAHu+lRIgQSXq8AgCjWBOFmr6OIKI4JJHjFoa65iCBQIILWGhWoLcNz4fLyOU+fPkVrTR7toSONUrKpIUHAQ+8ges1THCQcJgcA5NEYPDtrcI7BOeIg3nLjKN9hvBuwtkeFast8+slnWGs5PT0F4Pz8jPRQQyKEfvA0xpIAHg+vFXHjGibN5LqGRiDQdR2iAhBIRgnW2h2uiPMtF2iNBIogVDjvtszZ\/Jz8XsGzZ894\/PgxnRiKaLTJCnjqtSFUslM\/cP2XN+Scw\/sBANMaOtvh\/Zugcw4\/DPhhoLP9DtO3PcYY8jxHKUV+Z4xpWwY\/EIoIRZ4S69vb+2aoJE0I9aYjRYQkTRAr\/8CFiFIkaYTzm1nnPdzfP6EsS8qyxFrLo+OPmV29wJkBaZq1v5jOyfI7uMGjBPbHMSIwDMMmjZvwBNfzqmka0jQFoG0adBSD3OC8p2kNaZrQ9z11XdM7R9PU3D96n+l0SlmWLJdL0jRFRMiyDBmG4ZbE\/L8ahoGqqiiKgqqq0FpT1zXj8RhrLVmWbVlljeHi8pJqvX5nhlarFVmWsVgs0FrT9z3WWrTWtG0LwNnZGefn50jXNn42eYESwXtPURZM51NGaYKIwuNJsozJbEYURwgQxQl39+79JzN1XWOtRSlFURR0XUfTNCRJgjFma9QYQ1EUSNu2vq7rTXCdIsAweMJQcKYmiDMC8XTWMiiNVkLfW1CaIFAMptqMgddeC3EcE8cxAMvlkrIst122WCwoyxIRYT6fs7+\/vz3nvecvzIfJZKZPRaUAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/coverage_big.png\u0022 alt=\u0022100% coverage - feels good!\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022407.81648258284\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHaving 100% of LOC covered by unit tests certainly feels like a great achievement. But beware \u2013 that doesn\u2019t necessarily mean your code is \u003Cem\u003Eperfectly\u003C\/em\u003E covered. Lines of code coverage is a really nice indicator of your app\u2019s stability, but is can also hide some  risks.\u003C\/p\u003E\n\u003Ch3\u003ECyclomatic complexity\u003C\/h3\u003E\n\u003Cp\u003ELet\u2019s say you have simple method to test:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EcalculateStuff\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Eint\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;debug) {\n        \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ do some stuff\u003C\/span\u003E\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $result;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E;\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EHow many test cases should you write for it? Obviously, two: one to check for a valid output if \u003Ccode\u003Edebug\u003C\/code\u003E is true, and one when it\u2019s false. If you only write one of them, LOC coverage will make it clearly visible that something is missing. But what if your code looks like this?\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EcalculateStuff\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Eint\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;debug ? \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;calculateResult() : \u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E;\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou still have exactly the same two \u003Cem\u003Elogic paths\u003C\/em\u003E, but only one line of code. Testing any of those paths will result in the entire method being \u201ccovered\u201d.\u003C\/p\u003E\n\u003Cp\u003EAnd what about this code?\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EonException\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E(ExceptionEvent $event)\u003C\/span\u003E:\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (!\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;debug) {\n        $event-\u0026gt;setResponse(\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;renderErrorPage());\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIf you first test the case when \u003Ccode\u003E$debug === false\u003C\/code\u003E, then grab some lunch, and then come back not remembering the details, but seeing a 100% coverage \u2013 will you remember to also test the other case?\u003C\/p\u003E\n\u003Cp\u003ESumming up: having the tests execute every single \u003Cstrong\u003Eline\u003C\/strong\u003E of your code still does not guarantee that all the \u003Cstrong\u003Elogical paths\u003C\/strong\u003E of your class are being executed.\u003C\/p\u003E\n\u003Ch3\u003EMissing assertions\u003C\/h3\u003E\n\u003Cp\u003EAlso: having all the logical paths \u003Cstrong\u003Eexecuted\u003C\/strong\u003E doesn\u2019t necessarily mean you got them \u003Cstrong\u003Etested\u003C\/strong\u003E.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EFoo\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Erun\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($input)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ lots of complex logic\u003C\/span\u003E\n\n        \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ write the result to a file\u003C\/span\u003E\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $output;\n    }\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ lots of private methods\u003C\/span\u003E\n}\n\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EFooTest\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003ETestCase\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EtestRun\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $foo = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Foo;\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;assertEquals(\u003Cspan class=\u0022hljs-number\u0022\u003E8\u003C\/span\u003E, $foo-\u0026gt;run(\u003Cspan class=\u0022hljs-number\u0022\u003E123\u003C\/span\u003E));\n        \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ other, similar test cases\u003C\/span\u003E\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIt\u2019s very possible this test will cover every single line of those \u003Ccode\u003Elots of complex logic\u003C\/code\u003E. But what about the side effects? We are writing something to a file after all (a log maybe?). We are testing that the output is correct for a given input, but we forgot to test that the side effects are also working as expected.\u003C\/p\u003E\n\u003Cp\u003EThe nicest would be, of course, to avoid any side effects and code more functional-programming-style, but that might not always be doable easily enough. So the second best thing would be this: don\u2019t trust your coverage, check if what was covered, was also tested.\u003C\/p\u003E\n\u003Ch3\u003ELeaky coverage\u003C\/h3\u003E\n\u003Cp\u003EIt\u2019s virtually impossible to mock \u003Cem\u003Eall\u003C\/em\u003E the dependencies away. Well, ok, it\u2019s \u003Cem\u003Epossible\u003C\/em\u003E, but sometimes it\u2019s more effort than it\u2019s \u003Cem\u003Eworth\u003C\/em\u003E. Most of the time when you\u2019re testing class \u003Ccode\u003EX\u003C\/code\u003E, some lines of classes \u003Ccode\u003EY\u003C\/code\u003E and \u003Ccode\u003EZ\u003C\/code\u003E will also get executed. Usually, it\u2019s harmless. But sometimes it could blow up in your face \u2013 you could get the entire class \u003Ccode\u003EY\u003C\/code\u003E \u201ccovered\u201d without actually testing it. It\u2019s then very tempting to just leave it as it is (100% achieved, why would I even bother?) and very simple to just miss it.\u003C\/p\u003E\n\u003Cp\u003EThat\u2019s why it\u2019s a great habit to tell the testing framework, what are you testing with this specific case. In \u003Ca href=\u0022https:\/\/phpunit.de\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E PHPUnit\u003C\/a\u003E you can do it using the \u003Ccode\u003E@covers\u003C\/code\u003E annotation. If you specify that \u003Ccode\u003EXTest\u003C\/code\u003E \u003Ccode\u003E@covers X\u003C\/code\u003E, PHPUnit will still execute the code of \u003Ccode\u003EY\u003C\/code\u003E and \u003Ccode\u003EZ\u003C\/code\u003E, but will not count that as a coverage of \u003Ccode\u003EY\u003C\/code\u003E and \u003Ccode\u003EZ\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003EAchieving 100%\u003C\/h3\u003E\n\u003Cp\u003ELet\u2019s not kid ourselves \u2013 the test cases for more complex applications are never \u003Cem\u003Eperfect\u003C\/em\u003E in the sense of covering every single scenario that your code might run into. It\u2019s just not worth the effort, \u201cgood enough\u201d is usually good enough.\u003C\/p\u003E\n\u003Cp\u003EStill, having high coverage is a good indicator. The higher it is, and the more \u003Cem\u003Emeaningful\u003C\/em\u003E it is (in terms of what I wrote above) \u2013 the safer you can feel when refactoring your code, or modifying it in any other way.\u003C\/p\u003E\n\u003Cp\u003EBut getting to 100% is not easy.\u003C\/p\u003E\n\u003Ch4\u003ETestable code\u003C\/h4\u003E\n\u003Cp\u003EFollow clean code rules and use design patterns. If you repeat yourself (\u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Don%27t_repeat_yourself\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E DRY\u003C\/a\u003E), you\u2019ll have to repeat the tests as well. If your class has multiple responsibilities (\u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Single_responsibility_principle\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E SRP\u003C\/a\u003E), it becomes considerably more difficult to test it. Testing (A+B) together can sometimes be way way harder than testing A + testing B.\u003C\/p\u003E\n\u003Cp\u003EUse \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Inversion_of_control\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E inversion of control\u003C\/a\u003E, like \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Dependency_injection\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E DI\u003C\/a\u003E. If your class creates its dependencies or fetches them as a singleton, it\u2019s almost impossible to reliably mock those dependencies away. If you just pass them in the constructor though \u2013 it\u2019s an easy peasy to just pass a mock instead of an actual object.\u003C\/p\u003E\n\u003Cp\u003EThe simpler your code, the easier it is to test it. When writing tests for \u003Ca href=\u0022\/projects\/micrus-v4\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus\u003C\/a\u003E v4.0, I was surprised how much has my coverage grew not by adding new tests but by removing some not-so-useful, outdated or too complex code.\u003C\/p\u003E\n\u003Ch4\u003EAspectMock\u003C\/h4\u003E\n\u003Cp\u003EThere is a really fancy and clever library for PHP, \u003Ca href=\u0022https:\/\/github.com\/Codeception\/AspectMock\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E AspectMock\u003C\/a\u003E, that uses the power of aspect oriented programming to \u003Cem\u003Emake everything testable\u003C\/em\u003E. Static methods, final classes, global system functions, dependencies created inside of the tested class \u2013 everything. It basically modifies the source code in run-time, letting you do \u003Cem\u003Eanything\u003C\/em\u003E with it.\u003C\/p\u003E\n\u003Cp\u003EAwesome as it may be, I\u2019ve decided to move away from it. Micrus v4.0 doesn\u2019t use it all, even though in v3.0 it was a basis for every single mock. Why?\u003C\/p\u003E\n\u003Cp\u003EIt\u2019s quite an overhead, both in terms of speed and the time spent to configure it (best case scenario is very simple, but as soon as anything is misconfigured, debugging becomes a nightmare).\u003C\/p\u003E\n\u003Cp\u003EBut most importantly: it \u003Cem\u003Elets you get away with ugly code\u003C\/em\u003E. Why would you care about passing the dependencies in the constructor, if AspectMock lets you mock all the anti-patterns you can think of? Why would you care about making your code testable (= simple), if \u003Cem\u003Eeverything is testable\u003C\/em\u003E?\u003C\/p\u003E\n\u003Cp\u003EIf you depend on a legacy code, AspectMock might be your only chance to test some parts of it at all. However, if you\u2019re writing something modern \u2013 I\u2019d recommend avoiding this kind of magic.\u003C\/p\u003E\n\u003Ch4\u003EAlternatives\u003C\/h4\u003E\n\u003Cp\u003EHow to achieve 100% coverage without resolving to AspectMock or similar magic? I\u2019ve got some advice more specific than just SOLID or DI (although they are essential).\u003C\/p\u003E\n\u003Cp\u003EMost importantly: mocking the global functions. That\u2019s the feature for which I was considering staying with AspectMock after all. Then I found \u003Ca href=\u0022https:\/\/marcelog.github.io\/articles\/php_mock_global_functions_for_unit_tests_with_phpunit.html\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E this article\u003C\/a\u003E, showing how to achieve a similar result without that much overhead. It wasn\u2019t easy to use and re-use though, so I decided to write a solution which is. That\u2019s how \u003Ca href=\u0022\/projects\/avris-functionmock\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Avris FunctionMock\u003C\/a\u003E was born.\u003C\/p\u003E\n\u003Cp\u003EBasically it lets you write something like:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003EFunctionMock::create(\u003Cspan class=\u0022hljs-keyword\u0022\u003E__NAMESPACE__\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027time\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-number\u0022\u003E123456789\u003C\/span\u003E);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eand it will make all the calls to \u003Ccode\u003Etime()\u003C\/code\u003E inside \u003Ccode\u003E__NAMESPACE__\u003C\/code\u003E return \u003Ccode\u003E123456789\u003C\/code\u003E regardless of what the actual time is. You can also use a callback, disable a mock and validate how many times and with what arguments was the mock invoked.\u003C\/p\u003E\n\u003Cp\u003EWhen it comes to the issue of accessing private\/protected methods\/properties (which you shouldn\u2019t do, but sometimes have to), reflections come to the rescue:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$r = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\ReflectionProperty(Application::class, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027commands\u0027\u003C\/span\u003E);\n$r-\u0026gt;setAccessible(\u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E);\n$commands = array_keys($r-\u0026gt;getValue($consoleApp));\n\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;assertEquals([\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027help\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027list\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027test\u0027\u003C\/span\u003E], $commands);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eor\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$r = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\ReflectionMethod(FillEnvCommand::class, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027getDefaults\u0027\u003C\/span\u003E);\n$r-\u0026gt;setAccessible(\u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E);\n$defaults = iterator_to_array($r-\u0026gt;invoke($command));\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIf you\u2019re being nice and keep your classes \u003Ccode\u003Efinal\u003C\/code\u003E (to enforce \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Composition_over_inheritance\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Composition over inheritance\u003C\/a\u003E), you should also be nice and make them implement an interface and depend on that interface (\u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Dependency_inversion_principle\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Dependency inversion principle\u003C\/a\u003E). You can\u2019t mock a final class \u2013 but you can mock that interface.\u003C\/p\u003E\n\u003Cp\u003EAnd when it comes to mocking static methods \u2013 if you need to mock them, you\u2019re doing something wrong. Static methods should be avoided, except for things like factories etc.\u003C\/p\u003E\n\u003Ch3\u003ESumming up\u003C\/h3\u003E\n\u003Cp\u003EHaving high code coverage is nice, but remember it might give you a false feeling of security. Don\u2019t rely simply on that one number. And that\u2019s basically all I wanted to say \ud83d\ude01\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["aspectmock","code","coverage","mock","php","phpunit","programming","tests","unit tests"],"hasMore":true,"image":"https:\/\/avris.it\/image\/coverage_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/coverage_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/coverage_mini.png\u0022 alt=\u0022100% coverage - feels good!\u0022 width=\u0022240\u0022 height=\u0022101.95412064571\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHaving 100% of LOC covered by unit tests certainly feels like a great achievement. But beware \u2013 that doesn\u2019t necessarily mean your code is \u003Cem\u003Eperfectly\u003C\/em\u003E covered. Lines of code coverage is a really nice indicator of your app\u2019s stability, but is can also hide some  risks.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/coverage_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/coverage_mini.png\u0022 alt=\u0022100% coverage - feels good!\u0022 width=\u0022240\u0022 height=\u0022101.95412064571\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHaving 100% of LOC covered by unit tests certainly feels like a great achievement. But beware \u2013 that doesn\u2019t necessarily mean your code is \u003Cem\u003Eperfectly\u003C\/em\u003E covered. Lines of code coverage is a really nice indicator of your app\u2019s stability, but is can also hide some  risks.\u003C\/p\u003E\n\u003Ch3\u003ECyclomatic complexity\u003C\/h3\u003E\n\u003Cp\u003ELet\u2019s say you have simple method to test:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003Epublic function calculateStuff(): int\n{\n    if ($this-\u0026gt;debug) {\n        \/\/ do some stuff\n        return $result;\n    }\n\n    return 0;\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EHow many test cases should you write for it? Obviously, two: one to check for a valid output if \u003Ccode\u003Edebug\u003C\/code\u003E is true, and one when it\u2019s false. If you only write one of them, LOC coverage will make it clearly visible that something is missing. But what if your code looks like this?\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003Epublic function calculateStuff(): int\n{\n    return $this-\u0026gt;debug ? $this-\u0026gt;calculateResult() : 0;\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou still have exactly the same two \u003Cem\u003Elogic paths\u003C\/em\u003E, but only one line of code. Testing any of those paths will result in the entire method being \u201ccovered\u201d.\u003C\/p\u003E\n\u003Cp\u003EAnd what about this code?\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003Epublic function onException(ExceptionEvent $event):\n{\n    if (!$this-\u0026gt;debug) {\n        $event-\u0026gt;setResponse($this-\u0026gt;renderErrorPage());\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIf you first test the case when \u003Ccode\u003E$debug === false\u003C\/code\u003E, then grab some lunch, and then come back not remembering the details, but seeing a 100% coverage \u2013 will you remember to also test the other case?\u003C\/p\u003E\n\u003Cp\u003ESumming up: having the tests execute every single \u003Cstrong\u003Eline\u003C\/strong\u003E of your code still does not guarantee that all the \u003Cstrong\u003Elogical paths\u003C\/strong\u003E of your class are being executed.\u003C\/p\u003E\n\u003Ch3\u003EMissing assertions\u003C\/h3\u003E\n\u003Cp\u003EAlso: having all the logical paths \u003Cstrong\u003Eexecuted\u003C\/strong\u003E doesn\u2019t necessarily mean you got them \u003Cstrong\u003Etested\u003C\/strong\u003E.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003Eclass Foo\n{\n    public function run($input)\n    {\n        \/\/ lots of complex logic\n\n        \/\/ write the result to a file\n\n        return $output;\n    }\n\n    \/\/ lots of private methods\n}\n\nclass FooTest extends TestCase\n{\n    public function testRun()\n    {\n        $foo = new Foo;\n\n        $this-\u0026gt;assertEquals(8, $foo-\u0026gt;run(123));\n        \/\/ other, similar test cases\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIt\u2019s very possible this test will cover every single line of those \u003Ccode\u003Elots of complex logic\u003C\/code\u003E. But what about the side effects? We are writing something to a file after all (a log maybe?). We are testing that the output is correct for a given input, but we forgot to test that the side effects are also working as expected.\u003C\/p\u003E\n\u003Cp\u003EThe nicest would be, of course, to avoid any side effects and code more functional-programming-style, but that might not always be doable easily enough. So the second best thing would be this: don\u2019t trust your coverage, check if what was covered, was also tested.\u003C\/p\u003E\n\u003Ch3\u003ELeaky coverage\u003C\/h3\u003E\n\u003Cp\u003EIt\u2019s virtually impossible to mock \u003Cem\u003Eall\u003C\/em\u003E the dependencies away. Well, ok, it\u2019s \u003Cem\u003Epossible\u003C\/em\u003E, but sometimes it\u2019s more effort than it\u2019s \u003Cem\u003Eworth\u003C\/em\u003E. Most of the time when you\u2019re testing class \u003Ccode\u003EX\u003C\/code\u003E, some lines of classes \u003Ccode\u003EY\u003C\/code\u003E and \u003Ccode\u003EZ\u003C\/code\u003E will also get executed. Usually, it\u2019s harmless. But sometimes it could blow up in your face \u2013 you could get the entire class \u003Ccode\u003EY\u003C\/code\u003E \u201ccovered\u201d without actually testing it. It\u2019s then very tempting to just leave it as it is (100% achieved, why would I even bother?) and very simple to just miss it.\u003C\/p\u003E\n\u003Cp\u003EThat\u2019s why it\u2019s a great habit to tell the testing framework, what are you testing with this specific case. In \u003Ca href=\u0022https:\/\/phpunit.de\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E PHPUnit\u003C\/a\u003E you can do it using the \u003Ccode\u003E@covers\u003C\/code\u003E annotation. If you specify that \u003Ccode\u003EXTest\u003C\/code\u003E \u003Ccode\u003E@covers X\u003C\/code\u003E, PHPUnit will still execute the code of \u003Ccode\u003EY\u003C\/code\u003E and \u003Ccode\u003EZ\u003C\/code\u003E, but will not count that as a coverage of \u003Ccode\u003EY\u003C\/code\u003E and \u003Ccode\u003EZ\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003EAchieving 100%\u003C\/h3\u003E\n\u003Cp\u003ELet\u2019s not kid ourselves \u2013 the test cases for more complex applications are never \u003Cem\u003Eperfect\u003C\/em\u003E in the sense of covering every single scenario that your code might run into. It\u2019s just not worth the effort, \u201cgood enough\u201d is usually good enough.\u003C\/p\u003E\n\u003Cp\u003EStill, having high coverage is a good indicator. The higher it is, and the more \u003Cem\u003Emeaningful\u003C\/em\u003E it is (in terms of what I wrote above) \u2013 the safer you can feel when refactoring your code, or modifying it in any other way.\u003C\/p\u003E\n\u003Cp\u003EBut getting to 100% is not easy.\u003C\/p\u003E\n\u003Ch4\u003ETestable code\u003C\/h4\u003E\n\u003Cp\u003EFollow clean code rules and use design patterns. If you repeat yourself (\u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Don%27t_repeat_yourself\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E DRY\u003C\/a\u003E), you\u2019ll have to repeat the tests as well. If your class has multiple responsibilities (\u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Single_responsibility_principle\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E SRP\u003C\/a\u003E), it becomes considerably more difficult to test it. Testing (A+B) together can sometimes be way way harder than testing A + testing B.\u003C\/p\u003E\n\u003Cp\u003EUse \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Inversion_of_control\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E inversion of control\u003C\/a\u003E, like \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Dependency_injection\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E DI\u003C\/a\u003E. If your class creates its dependencies or fetches them as a singleton, it\u2019s almost impossible to reliably mock those dependencies away. If you just pass them in the constructor though \u2013 it\u2019s an easy peasy to just pass a mock instead of an actual object.\u003C\/p\u003E\n\u003Cp\u003EThe simpler your code, the easier it is to test it. When writing tests for \u003Ca href=\u0022\/projects\/micrus-v4.lite\u0022\u003E Micrus\u003C\/a\u003E v4.0, I was surprised how much has my coverage grew not by adding new tests but by removing some not-so-useful, outdated or too complex code.\u003C\/p\u003E\n\u003Ch4\u003EAspectMock\u003C\/h4\u003E\n\u003Cp\u003EThere is a really fancy and clever library for PHP, \u003Ca href=\u0022https:\/\/github.com\/Codeception\/AspectMock\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E AspectMock\u003C\/a\u003E, that uses the power of aspect oriented programming to \u003Cem\u003Emake everything testable\u003C\/em\u003E. Static methods, final classes, global system functions, dependencies created inside of the tested class \u2013 everything. It basically modifies the source code in run-time, letting you do \u003Cem\u003Eanything\u003C\/em\u003E with it.\u003C\/p\u003E\n\u003Cp\u003EAwesome as it may be, I\u2019ve decided to move away from it. Micrus v4.0 doesn\u2019t use it all, even though in v3.0 it was a basis for every single mock. Why?\u003C\/p\u003E\n\u003Cp\u003EIt\u2019s quite an overhead, both in terms of speed and the time spent to configure it (best case scenario is very simple, but as soon as anything is misconfigured, debugging becomes a nightmare).\u003C\/p\u003E\n\u003Cp\u003EBut most importantly: it \u003Cem\u003Elets you get away with ugly code\u003C\/em\u003E. Why would you care about passing the dependencies in the constructor, if AspectMock lets you mock all the anti-patterns you can think of? Why would you care about making your code testable (= simple), if \u003Cem\u003Eeverything is testable\u003C\/em\u003E?\u003C\/p\u003E\n\u003Cp\u003EIf you depend on a legacy code, AspectMock might be your only chance to test some parts of it at all. However, if you\u2019re writing something modern \u2013 I\u2019d recommend avoiding this kind of magic.\u003C\/p\u003E\n\u003Ch4\u003EAlternatives\u003C\/h4\u003E\n\u003Cp\u003EHow to achieve 100% coverage without resolving to AspectMock or similar magic? I\u2019ve got some advice more specific than just SOLID or DI (although they are essential).\u003C\/p\u003E\n\u003Cp\u003EMost importantly: mocking the global functions. That\u2019s the feature for which I was considering staying with AspectMock after all. Then I found \u003Ca href=\u0022https:\/\/marcelog.github.io\/articles\/php_mock_global_functions_for_unit_tests_with_phpunit.html\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E this article\u003C\/a\u003E, showing how to achieve a similar result without that much overhead. It wasn\u2019t easy to use and re-use though, so I decided to write a solution which is. That\u2019s how \u003Ca href=\u0022\/projects\/avris-functionmock.lite\u0022\u003E Avris FunctionMock\u003C\/a\u003E was born.\u003C\/p\u003E\n\u003Cp\u003EBasically it lets you write something like:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003EFunctionMock::create(__NAMESPACE__, \u0027time\u0027, 123456789);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eand it will make all the calls to \u003Ccode\u003Etime()\u003C\/code\u003E inside \u003Ccode\u003E__NAMESPACE__\u003C\/code\u003E return \u003Ccode\u003E123456789\u003C\/code\u003E regardless of what the actual time is. You can also use a callback, disable a mock and validate how many times and with what arguments was the mock invoked.\u003C\/p\u003E\n\u003Cp\u003EWhen it comes to the issue of accessing private\/protected methods\/properties (which you shouldn\u2019t do, but sometimes have to), reflections come to the rescue:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$r = new \\ReflectionProperty(Application::class, \u0027commands\u0027);\n$r-\u0026gt;setAccessible(true);\n$commands = array_keys($r-\u0026gt;getValue($consoleApp));\n$this-\u0026gt;assertEquals([\u0027help\u0027, \u0027list\u0027, \u0027test\u0027], $commands);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003Eor\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$r = new \\ReflectionMethod(FillEnvCommand::class, \u0027getDefaults\u0027);\n$r-\u0026gt;setAccessible(true);\n$defaults = iterator_to_array($r-\u0026gt;invoke($command));\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIf you\u2019re being nice and keep your classes \u003Ccode\u003Efinal\u003C\/code\u003E (to enforce \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Composition_over_inheritance\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Composition over inheritance\u003C\/a\u003E), you should also be nice and make them implement an interface and depend on that interface (\u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Dependency_inversion_principle\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Dependency inversion principle\u003C\/a\u003E). You can\u2019t mock a final class \u2013 but you can mock that interface.\u003C\/p\u003E\n\u003Cp\u003EAnd when it comes to mocking static methods \u2013 if you need to mock them, you\u2019re doing something wrong. Static methods should be avoided, except for things like factories etc.\u003C\/p\u003E\n\u003Ch3\u003ESumming up\u003C\/h3\u003E\n\u003Cp\u003EHaving high code coverage is nice, but remember it might give you a false feeling of security. Don\u2019t rely simply on that one number. And that\u2019s basically all I wanted to say \ud83d\ude01\u003C\/p\u003E","words":1332,"readTime":6,"lang":"en"}}},"projects\/oursong-let-s-choose-our-song":{"key":"projects\/oursong-let-s-choose-our-song","type":"article","published":true,"meta":{"createdAt":"2017-12-25T00:38:58+01:00","publishedAt":"2017-12-25T00:33:00+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/oursong.eurovote.eu\/","displayUrl":null}],"category":"projects","subcategory":null,"slug":"oursong-let-s-choose-our-song"},"content":{"en":{"slug":"oursong-let-s-choose-our-song","title":"OurSong \u2022 Let\u0027s choose our song!","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/oursong_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022603\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAkCAYAAACaJFpUAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHhElEQVRIiZWXW48cRxmGn6rq0\/ScdmfP6+Pasr2RA0bYkIOQIoyAIIE4SMAlEvwJFEWIn8AlcBP+ATdcAOIiikSEsAgRgQSvYsfr9SHrncPOoXu6u7qquBjvsOudxM4ntdSaqvre+qrrfd9vhLXWCSFwziGEIMsyOp0O9XodACHE9InjmE+LdncAJiGqTNZWq1WEEEfmiM+9\/Ir72qLkvlbc1j5KwNW6pVaJkM0ldjsd5poNiixle28fs3yWhw8eIpxFOAcLKzR\/8FPS0hBIyUoccHm+ylolYCnyyYzlUabZywo+Hmu8cT7GaY+2baDCmKzfZt8VMB7iD7psrKxSrYd4YcblhWVu3NlmN8vQKqLIxlSDACUgNxYLxJ7kYr3CfOgRSck+JQ3fQwlBw\/fwAAJPMiwjwjBkZOFG13BqcZ6fvfpV3vnnP3jxi1dZHN+l12mj4gYnHnV4e7tH2wZsNGM25JjyxDIAX2jV2JyLcc4hBcyFinXr2C9KxqXFE0BHS6wz5FoTBxJdgAsjtgcZr37nu\/h5nyRNOHt6nbgIqEtNpARv3R1wai4ifPAR15\/fRAqQwF6mETjW4oCqJ3HAYuiRGYtav\/jcL3vOo7AOz\/MYpynGlKz6OcnuXXof32e9EfKrP73DOdcBAautCmm\/w72B5e6jLiUeV659mZE27I4LPClo+IpQCZQU5MaRWwcCJEIx0gYRVvCaC8zNt1hZWmatEbE7KhHKY5wXbMxXuLk7oH1\/B18ZXl7L8VzJUj2mnyRsJwWPck2oJANt6BeGkbZ0spKxsYyNmeD89W9\/dwhASLyognKWMPApxinOWRpxRDX0ocyh1KA8wihA6JTeWFA6x0ALls5sIIUEIFKCQE6OsnQO6xzGgScEIs\/zIzwE8DzvCHecc9x4+03iWp3nr1w7NjYYDHDOoZQiCAIAtNaEYTjl8kH+Y4DOOdrtNk\/+ppSi0WgwHA5QykMpD3AIKREqxDqBA9p5lw\/2t1iK57i2+NyRHEIIjpbyODzPwxhDGIbUarUjY\/VaHanUkQqzEtLCkI\/2GfU6nCznqO8JWDyee+aR+r5Pt9vlozs7\/OXNt\/jjH37PzZs3wTmstdPFy8vLvPvuu+R5Pl0vpeTJfE+t0DnH3t4ev\/ntr\/ndG29QiWNKXbB26gybV77E1nvvcPvm+9OEWVGSFQYQBIEPCASWWsU\/fnqzALXWpFmGtpafv\/46J09vsPNwl\/fe3+KlF1\/g69\/6NvvdNjsfvg9AFEVYaemUu9xLdpC6hhuu85VLczjnng4ohCDPM775jev0en1Ori0RRiHddgcpBQuxYqV5mgtnTgJQlJZBotk3Bf2xRuoCvzCzUs8GlFLSnF\/Cbt\/hwYNH\/Ptf7yGkIpCOpHOfhXPrZIUhCv9vV2M3IvQU5+ILMK6wWCmeHRCgGtdIx5p00OHezn2KPOP8xec4sbJMFHiEvk8YTjgXedCqSQba4oQFz6JKfew4YaK1MyOOQj5\/5SqdTocz6y1CkTPq7XL54nnmalUkhnocAZAkCSoTrHpLnKotcGopJlqof5YKBYEvObu+yvd\/\/BO2b33A6XOXWGjNc2HjLGElRuuS\/nAITGgkpURKibEGgZidFhBFUTjgCG+sMZRm8tH3e11Gw8GUS845FpdXqVQqU7A0SUjHY6SUlGWJlBKlPFqt+eNKcxjoANgPAg4YVOoq6bCPcxYlJc5BvV7D94Pp\/H7m+O\/OCCEEgR+gPEEUOFqtZzzShw8fIqUEHM5Btdmi0WggpSRJEgaDIb7vT3edaslu6hN4Er+UJHlBHFqubBy\/NDPF+0m3yLIMYyxCTHT2wBEOKsyyDF2WRI\/dAT7ZLY5VKITgez\/8EeMiZzQa4QUBUgjATUABISWlLqlEIS+\/8AK\/eO01wiCAyTQ83ycMQ8qyfDalUdUYYw1xax4hPeYadcZZhrOTy5SmKVJ6yEqEEZK836YQAVGlMrlEaYq1dlrh4ZjJQ2MMxjr8IGSh2cCOUurCY96PcElOs9HECxTD0QgpBYVXJUkHaK2x1iKEQGs9K\/VsQKdLsAadZ4TOsra2zK0Pt7h1+yMCqVDGYq3FliXOWCQOKXw8pTDGYK09dg8+FXDU79PfazPs9tBIbm9tMRgM2dy8RFCrIQWMOl3S\/pBxmuKUoTQlWV5gjCHP8plg8Anf8PK1E7QurfKfP4\/xy5R6o05Z5OztPiAZpYTVECUdnrRIQIwSlFTAxKAd7jGtnhFwtXmJUIdsbvYQOmM4GHH9+ivUmzWMLpFKUVoo8oILF86jqnOYXveIwx9+PxwzWwxrLWVZPvbFnCRJprIWRRFxHE\/nBkFAng4YJjlBGBJFEVLKiWIdEocpD5\/cwUGSA3InowH37mxN+ees5cq1lwjDiVM453BSojyF7wcT9zcGO8OaZlb4pLYexM72LaKowtLK+rGxYbJPr9MnDCPmmk2MdVQq0bFW81ObqCcFfWl5DSnVkbEDFREiphpn1BvzKKVQj7s7daid\/EwVHh6bABzdzNPiSOdtjHnqilxbjJ3833OAc+B7EmsdQkyaqEqgUPKTjfcg\/geiigyZapzK0gAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/oursong_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022603\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA musical competition, in which the managers lead their countries every week to a fight for a title of the best song.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/oursong_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022800\u0022 height=\u00221005\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAkCAYAAACaJFpUAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHhElEQVRIiZWXW48cRxmGn6rq0\/ScdmfP6+Pasr2RA0bYkIOQIoyAIIE4SMAlEvwJFEWIn8AlcBP+ATdcAOIiikSEsAgRgQSvYsfr9SHrncPOoXu6u7qquBjvsOudxM4ntdSaqvre+qrrfd9vhLXWCSFwziGEIMsyOp0O9XodACHE9InjmE+LdncAJiGqTNZWq1WEEEfmiM+9\/Ir72qLkvlbc1j5KwNW6pVaJkM0ldjsd5poNiixle28fs3yWhw8eIpxFOAcLKzR\/8FPS0hBIyUoccHm+ylolYCnyyYzlUabZywo+Hmu8cT7GaY+2baDCmKzfZt8VMB7iD7psrKxSrYd4YcblhWVu3NlmN8vQKqLIxlSDACUgNxYLxJ7kYr3CfOgRSck+JQ3fQwlBw\/fwAAJPMiwjwjBkZOFG13BqcZ6fvfpV3vnnP3jxi1dZHN+l12mj4gYnHnV4e7tH2wZsNGM25JjyxDIAX2jV2JyLcc4hBcyFinXr2C9KxqXFE0BHS6wz5FoTBxJdgAsjtgcZr37nu\/h5nyRNOHt6nbgIqEtNpARv3R1wai4ifPAR15\/fRAqQwF6mETjW4oCqJ3HAYuiRGYtav\/jcL3vOo7AOz\/MYpynGlKz6OcnuXXof32e9EfKrP73DOdcBAautCmm\/w72B5e6jLiUeV659mZE27I4LPClo+IpQCZQU5MaRWwcCJEIx0gYRVvCaC8zNt1hZWmatEbE7KhHKY5wXbMxXuLk7oH1\/B18ZXl7L8VzJUj2mnyRsJwWPck2oJANt6BeGkbZ0spKxsYyNmeD89W9\/dwhASLyognKWMPApxinOWRpxRDX0ocyh1KA8wihA6JTeWFA6x0ALls5sIIUEIFKCQE6OsnQO6xzGgScEIs\/zIzwE8DzvCHecc9x4+03iWp3nr1w7NjYYDHDOoZQiCAIAtNaEYTjl8kH+Y4DOOdrtNk\/+ppSi0WgwHA5QykMpD3AIKREqxDqBA9p5lw\/2t1iK57i2+NyRHEIIjpbyODzPwxhDGIbUarUjY\/VaHanUkQqzEtLCkI\/2GfU6nCznqO8JWDyee+aR+r5Pt9vlozs7\/OXNt\/jjH37PzZs3wTmstdPFy8vLvPvuu+R5Pl0vpeTJfE+t0DnH3t4ev\/ntr\/ndG29QiWNKXbB26gybV77E1nvvcPvm+9OEWVGSFQYQBIEPCASWWsU\/fnqzALXWpFmGtpafv\/46J09vsPNwl\/fe3+KlF1\/g69\/6NvvdNjsfvg9AFEVYaemUu9xLdpC6hhuu85VLczjnng4ohCDPM775jev0en1Ori0RRiHddgcpBQuxYqV5mgtnTgJQlJZBotk3Bf2xRuoCvzCzUs8GlFLSnF\/Cbt\/hwYNH\/Ptf7yGkIpCOpHOfhXPrZIUhCv9vV2M3IvQU5+ILMK6wWCmeHRCgGtdIx5p00OHezn2KPOP8xec4sbJMFHiEvk8YTjgXedCqSQba4oQFz6JKfew4YaK1MyOOQj5\/5SqdTocz6y1CkTPq7XL54nnmalUkhnocAZAkCSoTrHpLnKotcGopJlqof5YKBYEvObu+yvd\/\/BO2b33A6XOXWGjNc2HjLGElRuuS\/nAITGgkpURKibEGgZidFhBFUTjgCG+sMZRm8tH3e11Gw8GUS845FpdXqVQqU7A0SUjHY6SUlGWJlBKlPFqt+eNKcxjoANgPAg4YVOoq6bCPcxYlJc5BvV7D94Pp\/H7m+O\/OCCEEgR+gPEEUOFqtZzzShw8fIqUEHM5Btdmi0WggpSRJEgaDIb7vT3edaslu6hN4Er+UJHlBHFqubBy\/NDPF+0m3yLIMYyxCTHT2wBEOKsyyDF2WRI\/dAT7ZLY5VKITgez\/8EeMiZzQa4QUBUgjATUABISWlLqlEIS+\/8AK\/eO01wiCAyTQ83ycMQ8qyfDalUdUYYw1xax4hPeYadcZZhrOTy5SmKVJ6yEqEEZK836YQAVGlMrlEaYq1dlrh4ZjJQ2MMxjr8IGSh2cCOUurCY96PcElOs9HECxTD0QgpBYVXJUkHaK2x1iKEQGs9K\/VsQKdLsAadZ4TOsra2zK0Pt7h1+yMCqVDGYq3FliXOWCQOKXw8pTDGYK09dg8+FXDU79PfazPs9tBIbm9tMRgM2dy8RFCrIQWMOl3S\/pBxmuKUoTQlWV5gjCHP8plg8Anf8PK1E7QurfKfP4\/xy5R6o05Z5OztPiAZpYTVECUdnrRIQIwSlFTAxKAd7jGtnhFwtXmJUIdsbvYQOmM4GHH9+ivUmzWMLpFKUVoo8oILF86jqnOYXveIwx9+PxwzWwxrLWVZPvbFnCRJprIWRRFxHE\/nBkFAng4YJjlBGBJFEVLKiWIdEocpD5\/cwUGSA3InowH37mxN+ees5cq1lwjDiVM453BSojyF7wcT9zcGO8OaZlb4pLYexM72LaKowtLK+rGxYbJPr9MnDCPmmk2MdVQq0bFW81ObqCcFfWl5DSnVkbEDFREiphpn1BvzKKVQj7s7daid\/EwVHh6bABzdzNPiSOdtjHnqilxbjJ3833OAc+B7EmsdQkyaqEqgUPKTjfcg\/geiigyZapzK0gAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/oursong_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022800\u0022 height=\u00221005\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA musical competition, in which the managers lead their countries every week to a fight for a title of the best song.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["competition","esc","eurovision","facebook","javascript","micrus","music","php","song","voting","websockets"],"hasMore":false,"image":"https:\/\/avris.it\/image\/oursong_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/oursong_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/oursong_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022301.5\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA musical competition, in which the managers lead their countries every week to a fight for a title of the best song.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/oursong_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/oursong_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022301.5\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EA musical competition, in which the managers lead their countries every week to a fight for a title of the best song.\u003C\/p\u003E","words":22,"readTime":null,"lang":"en"},"pl":{"slug":"oursong-wybierzmy-nasz\u0105-piosenk\u0119","title":"OurSong \u2022 Wybierzmy nasz\u0105 piosenk\u0119!","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/oursong_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022603\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAkCAYAAACaJFpUAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHhElEQVRIiZWXW48cRxmGn6rq0\/ScdmfP6+Pasr2RA0bYkIOQIoyAIIE4SMAlEvwJFEWIn8AlcBP+ATdcAOIiikSEsAgRgQSvYsfr9SHrncPOoXu6u7qquBjvsOudxM4ntdSaqvre+qrrfd9vhLXWCSFwziGEIMsyOp0O9XodACHE9InjmE+LdncAJiGqTNZWq1WEEEfmiM+9\/Ir72qLkvlbc1j5KwNW6pVaJkM0ldjsd5poNiixle28fs3yWhw8eIpxFOAcLKzR\/8FPS0hBIyUoccHm+ylolYCnyyYzlUabZywo+Hmu8cT7GaY+2baDCmKzfZt8VMB7iD7psrKxSrYd4YcblhWVu3NlmN8vQKqLIxlSDACUgNxYLxJ7kYr3CfOgRSck+JQ3fQwlBw\/fwAAJPMiwjwjBkZOFG13BqcZ6fvfpV3vnnP3jxi1dZHN+l12mj4gYnHnV4e7tH2wZsNGM25JjyxDIAX2jV2JyLcc4hBcyFinXr2C9KxqXFE0BHS6wz5FoTBxJdgAsjtgcZr37nu\/h5nyRNOHt6nbgIqEtNpARv3R1wai4ifPAR15\/fRAqQwF6mETjW4oCqJ3HAYuiRGYtav\/jcL3vOo7AOz\/MYpynGlKz6OcnuXXof32e9EfKrP73DOdcBAautCmm\/w72B5e6jLiUeV659mZE27I4LPClo+IpQCZQU5MaRWwcCJEIx0gYRVvCaC8zNt1hZWmatEbE7KhHKY5wXbMxXuLk7oH1\/B18ZXl7L8VzJUj2mnyRsJwWPck2oJANt6BeGkbZ0spKxsYyNmeD89W9\/dwhASLyognKWMPApxinOWRpxRDX0ocyh1KA8wihA6JTeWFA6x0ALls5sIIUEIFKCQE6OsnQO6xzGgScEIs\/zIzwE8DzvCHecc9x4+03iWp3nr1w7NjYYDHDOoZQiCAIAtNaEYTjl8kH+Y4DOOdrtNk\/+ppSi0WgwHA5QykMpD3AIKREqxDqBA9p5lw\/2t1iK57i2+NyRHEIIjpbyODzPwxhDGIbUarUjY\/VaHanUkQqzEtLCkI\/2GfU6nCznqO8JWDyee+aR+r5Pt9vlozs7\/OXNt\/jjH37PzZs3wTmstdPFy8vLvPvuu+R5Pl0vpeTJfE+t0DnH3t4ev\/ntr\/ndG29QiWNKXbB26gybV77E1nvvcPvm+9OEWVGSFQYQBIEPCASWWsU\/fnqzALXWpFmGtpafv\/46J09vsPNwl\/fe3+KlF1\/g69\/6NvvdNjsfvg9AFEVYaemUu9xLdpC6hhuu85VLczjnng4ohCDPM775jev0en1Ori0RRiHddgcpBQuxYqV5mgtnTgJQlJZBotk3Bf2xRuoCvzCzUs8GlFLSnF\/Cbt\/hwYNH\/Ptf7yGkIpCOpHOfhXPrZIUhCv9vV2M3IvQU5+ILMK6wWCmeHRCgGtdIx5p00OHezn2KPOP8xec4sbJMFHiEvk8YTjgXedCqSQba4oQFz6JKfew4YaK1MyOOQj5\/5SqdTocz6y1CkTPq7XL54nnmalUkhnocAZAkCSoTrHpLnKotcGopJlqof5YKBYEvObu+yvd\/\/BO2b33A6XOXWGjNc2HjLGElRuuS\/nAITGgkpURKibEGgZidFhBFUTjgCG+sMZRm8tH3e11Gw8GUS845FpdXqVQqU7A0SUjHY6SUlGWJlBKlPFqt+eNKcxjoANgPAg4YVOoq6bCPcxYlJc5BvV7D94Pp\/H7m+O\/OCCEEgR+gPEEUOFqtZzzShw8fIqUEHM5Btdmi0WggpSRJEgaDIb7vT3edaslu6hN4Er+UJHlBHFqubBy\/NDPF+0m3yLIMYyxCTHT2wBEOKsyyDF2WRI\/dAT7ZLY5VKITgez\/8EeMiZzQa4QUBUgjATUABISWlLqlEIS+\/8AK\/eO01wiCAyTQ83ycMQ8qyfDalUdUYYw1xax4hPeYadcZZhrOTy5SmKVJ6yEqEEZK836YQAVGlMrlEaYq1dlrh4ZjJQ2MMxjr8IGSh2cCOUurCY96PcElOs9HECxTD0QgpBYVXJUkHaK2x1iKEQGs9K\/VsQKdLsAadZ4TOsra2zK0Pt7h1+yMCqVDGYq3FliXOWCQOKXw8pTDGYK09dg8+FXDU79PfazPs9tBIbm9tMRgM2dy8RFCrIQWMOl3S\/pBxmuKUoTQlWV5gjCHP8plg8Anf8PK1E7QurfKfP4\/xy5R6o05Z5OztPiAZpYTVECUdnrRIQIwSlFTAxKAd7jGtnhFwtXmJUIdsbvYQOmM4GHH9+ivUmzWMLpFKUVoo8oILF86jqnOYXveIwx9+PxwzWwxrLWVZPvbFnCRJprIWRRFxHE\/nBkFAng4YJjlBGBJFEVLKiWIdEocpD5\/cwUGSA3InowH37mxN+ees5cq1lwjDiVM453BSojyF7wcT9zcGO8OaZlb4pLYexM72LaKowtLK+rGxYbJPr9MnDCPmmk2MdVQq0bFW81ObqCcFfWl5DSnVkbEDFREiphpn1BvzKKVQj7s7daid\/EwVHh6bABzdzNPiSOdtjHnqilxbjJ3833OAc+B7EmsdQkyaqEqgUPKTjfcg\/geiigyZapzK0gAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/oursong_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022603\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EKonkurs muzyczny, w kt\u00f3rym menad\u017cerowie prowadz\u0105cy wybrane przez siebie kraje z ca\u0142ego \u015bwiata co tydzie\u0144 rywalizuj\u0105 o miano najlepszego utworu.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/oursong_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022800\u0022 height=\u00221005\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAkCAYAAACaJFpUAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHhElEQVRIiZWXW48cRxmGn6rq0\/ScdmfP6+Pasr2RA0bYkIOQIoyAIIE4SMAlEvwJFEWIn8AlcBP+ATdcAOIiikSEsAgRgQSvYsfr9SHrncPOoXu6u7qquBjvsOudxM4ntdSaqvre+qrrfd9vhLXWCSFwziGEIMsyOp0O9XodACHE9InjmE+LdncAJiGqTNZWq1WEEEfmiM+9\/Ir72qLkvlbc1j5KwNW6pVaJkM0ldjsd5poNiixle28fs3yWhw8eIpxFOAcLKzR\/8FPS0hBIyUoccHm+ylolYCnyyYzlUabZywo+Hmu8cT7GaY+2baDCmKzfZt8VMB7iD7psrKxSrYd4YcblhWVu3NlmN8vQKqLIxlSDACUgNxYLxJ7kYr3CfOgRSck+JQ3fQwlBw\/fwAAJPMiwjwjBkZOFG13BqcZ6fvfpV3vnnP3jxi1dZHN+l12mj4gYnHnV4e7tH2wZsNGM25JjyxDIAX2jV2JyLcc4hBcyFinXr2C9KxqXFE0BHS6wz5FoTBxJdgAsjtgcZr37nu\/h5nyRNOHt6nbgIqEtNpARv3R1wai4ifPAR15\/fRAqQwF6mETjW4oCqJ3HAYuiRGYtav\/jcL3vOo7AOz\/MYpynGlKz6OcnuXXof32e9EfKrP73DOdcBAautCmm\/w72B5e6jLiUeV659mZE27I4LPClo+IpQCZQU5MaRWwcCJEIx0gYRVvCaC8zNt1hZWmatEbE7KhHKY5wXbMxXuLk7oH1\/B18ZXl7L8VzJUj2mnyRsJwWPck2oJANt6BeGkbZ0spKxsYyNmeD89W9\/dwhASLyognKWMPApxinOWRpxRDX0ocyh1KA8wihA6JTeWFA6x0ALls5sIIUEIFKCQE6OsnQO6xzGgScEIs\/zIzwE8DzvCHecc9x4+03iWp3nr1w7NjYYDHDOoZQiCAIAtNaEYTjl8kH+Y4DOOdrtNk\/+ppSi0WgwHA5QykMpD3AIKREqxDqBA9p5lw\/2t1iK57i2+NyRHEIIjpbyODzPwxhDGIbUarUjY\/VaHanUkQqzEtLCkI\/2GfU6nCznqO8JWDyee+aR+r5Pt9vlozs7\/OXNt\/jjH37PzZs3wTmstdPFy8vLvPvuu+R5Pl0vpeTJfE+t0DnH3t4ev\/ntr\/ndG29QiWNKXbB26gybV77E1nvvcPvm+9OEWVGSFQYQBIEPCASWWsU\/fnqzALXWpFmGtpafv\/46J09vsPNwl\/fe3+KlF1\/g69\/6NvvdNjsfvg9AFEVYaemUu9xLdpC6hhuu85VLczjnng4ohCDPM775jev0en1Ori0RRiHddgcpBQuxYqV5mgtnTgJQlJZBotk3Bf2xRuoCvzCzUs8GlFLSnF\/Cbt\/hwYNH\/Ptf7yGkIpCOpHOfhXPrZIUhCv9vV2M3IvQU5+ILMK6wWCmeHRCgGtdIx5p00OHezn2KPOP8xec4sbJMFHiEvk8YTjgXedCqSQba4oQFz6JKfew4YaK1MyOOQj5\/5SqdTocz6y1CkTPq7XL54nnmalUkhnocAZAkCSoTrHpLnKotcGopJlqof5YKBYEvObu+yvd\/\/BO2b33A6XOXWGjNc2HjLGElRuuS\/nAITGgkpURKibEGgZidFhBFUTjgCG+sMZRm8tH3e11Gw8GUS845FpdXqVQqU7A0SUjHY6SUlGWJlBKlPFqt+eNKcxjoANgPAg4YVOoq6bCPcxYlJc5BvV7D94Pp\/H7m+O\/OCCEEgR+gPEEUOFqtZzzShw8fIqUEHM5Btdmi0WggpSRJEgaDIb7vT3edaslu6hN4Er+UJHlBHFqubBy\/NDPF+0m3yLIMYyxCTHT2wBEOKsyyDF2WRI\/dAT7ZLY5VKITgez\/8EeMiZzQa4QUBUgjATUABISWlLqlEIS+\/8AK\/eO01wiCAyTQ83ycMQ8qyfDalUdUYYw1xax4hPeYadcZZhrOTy5SmKVJ6yEqEEZK836YQAVGlMrlEaYq1dlrh4ZjJQ2MMxjr8IGSh2cCOUurCY96PcElOs9HECxTD0QgpBYVXJUkHaK2x1iKEQGs9K\/VsQKdLsAadZ4TOsra2zK0Pt7h1+yMCqVDGYq3FliXOWCQOKXw8pTDGYK09dg8+FXDU79PfazPs9tBIbm9tMRgM2dy8RFCrIQWMOl3S\/pBxmuKUoTQlWV5gjCHP8plg8Anf8PK1E7QurfKfP4\/xy5R6o05Z5OztPiAZpYTVECUdnrRIQIwSlFTAxKAd7jGtnhFwtXmJUIdsbvYQOmM4GHH9+ivUmzWMLpFKUVoo8oILF86jqnOYXveIwx9+PxwzWwxrLWVZPvbFnCRJprIWRRFxHE\/nBkFAng4YJjlBGBJFEVLKiWIdEocpD5\/cwUGSA3InowH37mxN+ees5cq1lwjDiVM453BSojyF7wcT9zcGO8OaZlb4pLYexM72LaKowtLK+rGxYbJPr9MnDCPmmk2MdVQq0bFW81ObqCcFfWl5DSnVkbEDFREiphpn1BvzKKVQj7s7daid\/EwVHh6bABzdzNPiSOdtjHnqilxbjJ3833OAc+B7EmsdQkyaqEqgUPKTjfcg\/geiigyZapzK0gAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/oursong_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022800\u0022 height=\u00221005\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EKonkurs muzyczny, w kt\u00f3rym menad\u017cerowie prowadz\u0105cy wybrane przez siebie kraje z ca\u0142ego \u015bwiata co tydzie\u0144 rywalizuj\u0105 o miano najlepszego utworu.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["eurowizja","facebook","g\u0142osowanie","javascript","konkurs","muzyka","php","piosenka","programowanie","websockets"],"hasMore":false,"image":"https:\/\/avris.it\/image\/oursong_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/oursong_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/oursong_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022301.5\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EKonkurs muzyczny, w kt\u00f3rym menad\u017cerowie prowadz\u0105cy wybrane przez siebie kraje z ca\u0142ego \u015bwiata co tydzie\u0144 rywalizuj\u0105 o miano najlepszego utworu.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/oursong_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/oursong_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022301.5\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EKonkurs muzyczny, w kt\u00f3rym menad\u017cerowie prowadz\u0105cy wybrane przez siebie kraje z ca\u0142ego \u015bwiata co tydzie\u0144 rywalizuj\u0105 o miano najlepszego utworu.\u003C\/p\u003E","words":24,"readTime":null,"lang":"pl"}}},"projects\/avris-functionmock":{"key":"projects\/avris-functionmock","type":"article","published":true,"meta":{"createdAt":"2017-12-21T10:18:21+01:00","publishedAt":"2017-12-21T10:16:00+01:00","group":"mocks","links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/FunctionMock","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-functionmock"},"content":{"en":{"slug":"avris-functionmock","title":"Avris FunctionMock","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-function-mock_small.png\u0022 alt=\u0022Avris FunctionMock\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHpklEQVRIiX2X269dVRXGf+My59p7n7PPpZfTlkJLAVFStZFIJCFKIQEhBhM0atAE9UEfMD74J\/jumw9G47M+GV8QE41GIIFEwXgBRCLYAgVKS9tz2\/vsvdacw4e1W4waZ9ZcayVrXr75fXPM8S05duPJiFIRgqgVomLu1FIIAhXB1TBVkjvz+YxaK0RABAKICEJfRAQRAYKrJSIWtV57EhURUBHMHROhaYZ4hBARlFqQAAFKKQj9wKraAxWFWkmqdIsJEFm041p7EQBBVd6fXK6+C1ELokbE+\/0MIamhteBXOwhKUIkADUG0n4wQVB0QulIxBEUIUUx1wUa\/EKT\/trgWN8PdmM9bRCCiX6CIoou+poqbkVTxEIVaFgT3jBCBhKBquCkmgonipkhUaq3UWvoB3PvV14qqXZNKRYhaCYLVtTWuXLoMVAhBHKIWVAUCzJRkRvKrgARQgbpgS0BEFzIYimJmZEuMmgaiYzKd0rjTJCd7otZKKR1dKSD0+6VWAI4c2mC2vQPUnrQIVPMClGLaS5xNcdFERIuEgVT02iYV1Kyn1hIqioqh4iw1S6wPV7m8s4mGEiVoPKGeIQohQte1PUMCx48d450zryMSSICpIASQEImFZEoyxTVlumARMXY1VkC1Z0cNUyO7ky2hGCKJJidOHFxmd7LDrG05dHiD+XTCztY2JkJ2pUblrvvv5tWXXmYlNwTRRxag2ouiCiKQTMlueGhC0kIuhBpgoqgaKoqb42pkz7gIyZwkiaQN+\/at8rmvfYF962v87me\/5I2zb2DNMsn7aBqvLnPXvXfz4pPPsDYYAoWI6MGooAJmgmnPmrvg5AbpnFpjoS+AImqoOSKGm6OacLNFTZx+8JM88q0v8cpzf+Px7\/+UROHWI9cRtbK9u4Nn4yvf\/Q7P\/fwJrl9ZYV5aanRElF46XURX6vdQToopuAyWqG1Bau3PoQDEEHHUEskcVSOZM0iZkTtff+zzfPrh0+xc3uaJHz5O1iHrowE333Idu5sX2Zzsce+3v0xSoZ59kw8d2WBnOmGvm1NqR6ktaCW5YSa4K8mFlAyX0Qoy76BUAvo9giKSUDHEE1mdQUqMUuKRR+\/j0w+fBuAvT79AomG8PGZ51LB9acrxj3+U+x+6Cx80PPu9H7NxYBWNls2toOsSJXpAIRXVIGXHXUhJ8Sy4DMeQOyggoRCKoCAOmjB1kjk5JU6dOsFnv3jPtZSwdX6H\/YcOcvT4BsdP3sANt9\/E0oEVIoI3fvUUS8NMqcsMmo7cVJBKjZaumxO1RQ1UA3MjN44nwRmvQleIkB5M9NFlOK4JxZHFAfjoNx+8djIDPPCN+\/jPEhGce+p55hcucvDG\/cw2M+3uFVa0RawiFtTaUrsZQoeaYK6knPGmwXU8JhX6PIPioSSMLE4OI6uRUW4\/dYyjR\/f9F4B\/L7UUXv31H9h+5VUGS8pgdcTB266n21xi621BZY7QgmSiJERqH2HJsZyxZoiP18aUAiwYMgQPZyDGAGWA0VThzk\/c8n\/B7FzY5KVfPMP8vQsMG0ChnU6YvNfiMme02lDb0p89BioJWcil7lgeoM0QP7xvSNcFtUANRUNRjBRCwhiE0hTh1psO\/E8g060Jf3\/6Bc798WWytAwHtU8dFaKFOjNolDxUIikmsZApoaZYymhukNRAHuIf3miY7LXMZ4V5VymlUmpFQpCoWBFMhAPro2sgdnemnH3lHGf+\/BrnXvwnTbSMvBCp4FSyVkIDLGDe50pJQfLATTATNCUkDyAZ5Ay5AR\/gD62\/xe5eZWenZWu3stMF2zOYzGGvhXknlA5+9IOfMN2dcuXiFruXtsilkCgkKgOtDK1jKQUrg8p8VGEcpBAG1tsMKS3qgWXDatC2hjGmnRk6SGxeHtEyQE6fPBArg8TeXm+V3DIhTsUJSRScqpmNw9dx\/uJFutI7AVejaRrWxsvMJlOyCY3C+nJmnIPVNOfIeuHEiXWo27z+9hV2plNKblm77ihP\/ub3nHl7xsqgsrExZtYGz760iz93rqIpEFUsGd4kRA3PDZ4ylhpUjdP3fIr5Sy9zcH0N1IlQjmwc4sKFyxxcX8VDmE+mnPzgCRoRdi+8yygHZd8Yn13iUPc611vH2lLHradG3HPTMS68vcV4CC4dB++8j8lff4vc8dXHolleoaswWBrjqUHVEXOSJ0QMAa7fv8YwJdZGDRcvbTNKjtTKbDrjwHjEZHePaDuWkhN7Mw6vLjFSWM7KQAvtlQtkq6yOKredXKNZgteef43xUGi8Y3joEHllFS8YO5MpWGK+vY3ILqC9F4regKsol997FykVSkVqpe7tQS1kM6QEEoHVQqL3xqOUGSdn7M5qkxglwbUwyrCz13LHR1Z487JSL3WsDGF85S0+8MDH8Mtn\/8G8bVHP1FLxZgjv\/0MggKeEqiIRRA00egBaK9J11wx+bWe4GFfOv84gD3ARVpdX2L+yhkdlMtlifWXMn1523jk75PzmHmfe22OYg0bnfKZ7Ezlw821RS6Gbz6ntjLQ0RkIo8z00NX06qB2eBwC95wa0BoogcdVD915cIxAqJoKr4AJKJZuhGnRdi1DYGDurA+PStLe5jcOdx+FfEghCLeMqGgkAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-function-mock_small.png\u0022 alt=\u0022Avris FunctionMock\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022320\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFunctionMock is a simple and elegant way to mock away system\/global functions in your tests.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-function-mock_big.png\u0022 alt=\u0022Avris FunctionMock\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAYCAYAAACSuF9OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHpklEQVRIiX2X269dVRXGf+My59p7n7PPpZfTlkJLAVFStZFIJCFKIQEhBhM0atAE9UEfMD74J\/jumw9G47M+GV8QE41GIIFEwXgBRCLYAgVKS9tz2\/vsvdacw4e1W4waZ9ZcayVrXr75fXPM8S05duPJiFIRgqgVomLu1FIIAhXB1TBVkjvz+YxaK0RABAKICEJfRAQRAYKrJSIWtV57EhURUBHMHROhaYZ4hBARlFqQAAFKKQj9wKraAxWFWkmqdIsJEFm041p7EQBBVd6fXK6+C1ELokbE+\/0MIamhteBXOwhKUIkADUG0n4wQVB0QulIxBEUIUUx1wUa\/EKT\/trgWN8PdmM9bRCCiX6CIoou+poqbkVTxEIVaFgT3jBCBhKBquCkmgonipkhUaq3UWvoB3PvV14qqXZNKRYhaCYLVtTWuXLoMVAhBHKIWVAUCzJRkRvKrgARQgbpgS0BEFzIYimJmZEuMmgaiYzKd0rjTJCd7otZKKR1dKSD0+6VWAI4c2mC2vQPUnrQIVPMClGLaS5xNcdFERIuEgVT02iYV1Kyn1hIqioqh4iw1S6wPV7m8s4mGEiVoPKGeIQohQte1PUMCx48d450zryMSSICpIASQEImFZEoyxTVlumARMXY1VkC1Z0cNUyO7ky2hGCKJJidOHFxmd7LDrG05dHiD+XTCztY2JkJ2pUblrvvv5tWXXmYlNwTRRxag2ouiCiKQTMlueGhC0kIuhBpgoqgaKoqb42pkz7gIyZwkiaQN+\/at8rmvfYF962v87me\/5I2zb2DNMsn7aBqvLnPXvXfz4pPPsDYYAoWI6MGooAJmgmnPmrvg5AbpnFpjoS+AImqoOSKGm6OacLNFTZx+8JM88q0v8cpzf+Px7\/+UROHWI9cRtbK9u4Nn4yvf\/Q7P\/fwJrl9ZYV5aanRElF46XURX6vdQToopuAyWqG1Bau3PoQDEEHHUEskcVSOZM0iZkTtff+zzfPrh0+xc3uaJHz5O1iHrowE333Idu5sX2Zzsce+3v0xSoZ59kw8d2WBnOmGvm1NqR6ktaCW5YSa4K8mFlAyX0Qoy76BUAvo9giKSUDHEE1mdQUqMUuKRR+\/j0w+fBuAvT79AomG8PGZ51LB9acrxj3+U+x+6Cx80PPu9H7NxYBWNls2toOsSJXpAIRXVIGXHXUhJ8Sy4DMeQOyggoRCKoCAOmjB1kjk5JU6dOsFnv3jPtZSwdX6H\/YcOcvT4BsdP3sANt9\/E0oEVIoI3fvUUS8NMqcsMmo7cVJBKjZaumxO1RQ1UA3MjN44nwRmvQleIkB5M9NFlOK4JxZHFAfjoNx+8djIDPPCN+\/jPEhGce+p55hcucvDG\/cw2M+3uFVa0RawiFtTaUrsZQoeaYK6knPGmwXU8JhX6PIPioSSMLE4OI6uRUW4\/dYyjR\/f9F4B\/L7UUXv31H9h+5VUGS8pgdcTB266n21xi621BZY7QgmSiJERqH2HJsZyxZoiP18aUAiwYMgQPZyDGAGWA0VThzk\/c8n\/B7FzY5KVfPMP8vQsMG0ChnU6YvNfiMme02lDb0p89BioJWcil7lgeoM0QP7xvSNcFtUANRUNRjBRCwhiE0hTh1psO\/E8g060Jf3\/6Bc798WWytAwHtU8dFaKFOjNolDxUIikmsZApoaZYymhukNRAHuIf3miY7LXMZ4V5VymlUmpFQpCoWBFMhAPro2sgdnemnH3lHGf+\/BrnXvwnTbSMvBCp4FSyVkIDLGDe50pJQfLATTATNCUkDyAZ5Ay5AR\/gD62\/xe5eZWenZWu3stMF2zOYzGGvhXknlA5+9IOfMN2dcuXiFruXtsilkCgkKgOtDK1jKQUrg8p8VGEcpBAG1tsMKS3qgWXDatC2hjGmnRk6SGxeHtEyQE6fPBArg8TeXm+V3DIhTsUJSRScqpmNw9dx\/uJFutI7AVejaRrWxsvMJlOyCY3C+nJmnIPVNOfIeuHEiXWo27z+9hV2plNKblm77ihP\/ub3nHl7xsqgsrExZtYGz760iz93rqIpEFUsGd4kRA3PDZ4ylhpUjdP3fIr5Sy9zcH0N1IlQjmwc4sKFyxxcX8VDmE+mnPzgCRoRdi+8yygHZd8Yn13iUPc611vH2lLHradG3HPTMS68vcV4CC4dB++8j8lff4vc8dXHolleoaswWBrjqUHVEXOSJ0QMAa7fv8YwJdZGDRcvbTNKjtTKbDrjwHjEZHePaDuWkhN7Mw6vLjFSWM7KQAvtlQtkq6yOKredXKNZgteef43xUGi8Y3joEHllFS8YO5MpWGK+vY3ILqC9F4regKsol997FykVSkVqpe7tQS1kM6QEEoHVQqL3xqOUGSdn7M5qkxglwbUwyrCz13LHR1Z487JSL3WsDGF85S0+8MDH8Mtn\/8G8bVHP1FLxZgjv\/0MggKeEqiIRRA00egBaK9J11wx+bWe4GFfOv84gD3ARVpdX2L+yhkdlMtlifWXMn1523jk75PzmHmfe22OYg0bnfKZ7Ezlw821RS6Gbz6ntjLQ0RkIo8z00NX06qB2eBwC95wa0BoogcdVD915cIxAqJoKr4AJKJZuhGnRdi1DYGDurA+PStLe5jcOdx+FfEghCLeMqGgkAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-function-mock_big.png\u0022 alt=\u0022Avris FunctionMock\u0022 class=\u0022border\u0022 width=\u0022900\u0022 height=\u0022600\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFunctionMock is a simple and elegant way to mock away system\/global functions in your tests.\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs bash border\u0022\u003Ecomposer require --dev avris\/\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E-mock\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EUsage\u003C\/h3\u003E\n\u003Cp\u003ELet\u2019s say you have a class that writes something to a file:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EService\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EWorkService\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Ework\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E(int $input)\u003C\/span\u003E: \u003Cspan class=\u0022hljs-title\u0022\u003Ebool\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $result = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027correctResult\u0027\u003C\/span\u003E; \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ ... calculate the result somehow\u003C\/span\u003E\n\n        $filename = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/tmp\/foo\u0027\u003C\/span\u003E;\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E file_put_contents($filename, $result);\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ETo test it, without actually using filesystem, you can use FunctionMock:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003ETest\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Euse\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EApp\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EService\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EWorkService\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-title\u0022\u003Euse\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EPHPUnit\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003EFramework\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003ETestCase\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EServiceTest\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003ETestCase\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/** \u003Cspan class=\u0022hljs-doctag\u0022\u003E@var\u003C\/span\u003E WorkService *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $service;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprotected\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EsetUp\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;service = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E WorkService();\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprotected\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EtearDown\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E\n    \u003C\/span\u003E{\n        FunctionMock::clean();\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EtestWork\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $mock = FunctionMock::create(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027App\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027file_put_contents\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E);\n\n        $returnValue = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;service-\u0026gt;work(\u003Cspan class=\u0022hljs-number\u0022\u003E8\u003C\/span\u003E);\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;assertTrue($returnValue);\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;assertEquals([[\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/tmp\/foo\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027correctResult\u0027\u003C\/span\u003E]], $mock-\u0026gt;getInvocations());\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIf your tests are in the same namespace with your tested classes, it\u2019s recommended to use \u003Ccode\u003E__NAMESPACE__\u003C\/code\u003E. \u003C\/p\u003E\n\u003Cp\u003EThe third parameter, the return value of the mocked function, can either be a literal or a callable:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$mock = FunctionMock::create(\u003Cspan class=\u0022hljs-keyword\u0022\u003E__NAMESPACE__\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027file_put_contents\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-params\u0022\u003E($filename, $data)\u003C\/span\u003E \u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $filename === \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/tmp\/foo\u0027\u003C\/span\u003E;\n});\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can disable\/re-enable the mock by invoking \u003Ccode\u003E$mock-\u0026gt;disable()\u003C\/code\u003E and \u003Ccode\u003E$mock-\u0026gt;enable()\u003C\/code\u003E,\nas well as clear the list of logged invocations with \u003Ccode\u003E$mock-\u0026gt;clearInvocations()\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003EKnown limitations\u003C\/h3\u003E\n\u003Cp\u003EFunctionMock works thanks to the fact that \u003Ccode\u003Efile_get_contents\u003C\/code\u003E inside of \u003Ccode\u003EApp\\Service\u003C\/code\u003E namespace references to the function\n\u003Ccode\u003EApp\\Service\\file_get_contents\u003C\/code\u003E and only if it\u2019s not defined it falls back to the global \u003Ccode\u003E\\file_get_contents\u003C\/code\u003E.\u003C\/p\u003E\n\u003Cp\u003EFunctionMock defines this \u003Ccode\u003EApp\\Service\\file_get_contents\u003C\/code\u003E function on the fly\nand makes it adjustable to your needs in the runtime.\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIf you already have invoked \u003Ccode\u003Efile_get_contents\u003C\/code\u003E inside of \u003Ccode\u003EApp\\Service\u003C\/code\u003E before registering a mock,\nPHP will continue using the global function and it cannot be overwritten anymore.\u003C\/li\u003E\n\u003Cli\u003EIf you\u2019re using \u003Ccode\u003E\\file_get_contents\u003C\/code\u003E (explicitly global namespace) inside your tested class,\nthere\u2019s obviously no way to mock it.\u003C\/li\u003E\n\u003Cli\u003EAfter being registered, mock function cannot be really unset, only disabled.\u003C\/li\u003E\n\u003C\/ul\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["function","global function","mock","namespaces","php","tests"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-function-mock_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-function-mock_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-function-mock_mini.png\u0022 alt=\u0022Avris FunctionMock\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFunctionMock is a simple and elegant way to mock away system\/global functions in your tests.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-function-mock_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-function-mock_mini.png\u0022 alt=\u0022Avris FunctionMock\u0022 width=\u0022240\u0022 height=\u0022160\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFunctionMock is a simple and elegant way to mock away system\/global functions in your tests.\u003C\/p\u003E\n\u003Ch3\u003EInstalation\u003C\/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Ecomposer require --dev avris\/function-mock\u003C\/code\u003E\u003C\/pre\u003E\n\u003Ch3\u003EUsage\u003C\/h3\u003E\n\u003Cp\u003ELet\u2019s say you have a class that writes something to a file:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\nnamespace App\\Service;\n\nclass WorkService\n{\n    public function work(int $input): bool\n    {\n        $result = \u0027correctResult\u0027; \/\/ ... calculate the result somehow\n\n        $filename = \u0027\/tmp\/foo\u0027;\n\n        return file_put_contents($filename, $result);\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ETo test it, without actually using filesystem, you can use FunctionMock:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\nnamespace App\\Test;\n\nuse App\\Service\\WorkService\nuse PHPUnit\\Framework\\TestCase;\n\nclass ServiceTest extends TestCase\n{\n    \/** @var WorkService *\/\n    private $service;\n\n    protected function setUp()\n    {\n        $this-\u0026gt;service = new WorkService();\n    }\n\n    protected function tearDown()\n    {\n        FunctionMock::clean();\n    }\n\n    public function testWork()\n    {\n        $mock = FunctionMock::create(\u0027App\u0027, \u0027file_put_contents\u0027, true);\n\n        $returnValue = $this-\u0026gt;service-\u0026gt;work(8);\n\n        $this-\u0026gt;assertTrue($returnValue);\n        $this-\u0026gt;assertEquals([[\u0027\/tmp\/foo\u0027, \u0027correctResult\u0027]], $mock-\u0026gt;getInvocations());\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EIf your tests are in the same namespace with your tested classes, it\u2019s recommended to use \u003Ccode\u003E__NAMESPACE__\u003C\/code\u003E. \u003C\/p\u003E\n\u003Cp\u003EThe third parameter, the return value of the mocked function, can either be a literal or a callable:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E$mock = FunctionMock::create(__NAMESPACE__, \u0027file_put_contents\u0027, function ($filename, $data) {\n    return $filename === \u0027\/tmp\/foo\u0027;\n});\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can disable\/re-enable the mock by invoking \u003Ccode\u003E$mock-\u0026gt;disable()\u003C\/code\u003E and \u003Ccode\u003E$mock-\u0026gt;enable()\u003C\/code\u003E,\nas well as clear the list of logged invocations with \u003Ccode\u003E$mock-\u0026gt;clearInvocations()\u003C\/code\u003E.\u003C\/p\u003E\n\u003Ch3\u003EKnown limitations\u003C\/h3\u003E\n\u003Cp\u003EFunctionMock works thanks to the fact that \u003Ccode\u003Efile_get_contents\u003C\/code\u003E inside of \u003Ccode\u003EApp\\Service\u003C\/code\u003E namespace references to the function\n\u003Ccode\u003EApp\\Service\\file_get_contents\u003C\/code\u003E and only if it\u2019s not defined it falls back to the global \u003Ccode\u003E\\file_get_contents\u003C\/code\u003E.\u003C\/p\u003E\n\u003Cp\u003EFunctionMock defines this \u003Ccode\u003EApp\\Service\\file_get_contents\u003C\/code\u003E function on the fly\nand makes it adjustable to your needs in the runtime.\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIf you already have invoked \u003Ccode\u003Efile_get_contents\u003C\/code\u003E inside of \u003Ccode\u003EApp\\Service\u003C\/code\u003E before registering a mock,\nPHP will continue using the global function and it cannot be overwritten anymore.\u003C\/li\u003E\n\u003Cli\u003EIf you\u2019re using \u003Ccode\u003E\\file_get_contents\u003C\/code\u003E (explicitly global namespace) inside your tested class,\nthere\u2019s obviously no way to mock it.\u003C\/li\u003E\n\u003Cli\u003EAfter being registered, mock function cannot be really unset, only disabled.\u003C\/li\u003E\n\u003C\/ul\u003E","words":346,"readTime":2,"lang":"en"}}},"blog\/technology\/the-non-magic-of-autowiring":{"key":"blog\/technology\/the-non-magic-of-autowiring","type":"article","published":true,"meta":{"createdAt":"2017-09-01T22:58:27+02:00","publishedAt":"2017-09-05T20:00:00+02:00","group":null,"category":"blog","subcategory":"technology","slug":"the-non-magic-of-autowiring"},"content":{"en":{"slug":"the-non-magic-of-autowiring","title":"The non-magic of autowiring","intro":"\u003Cp\u003EWhen I first heard the term \u201cAutowiring\u201d, I thought it sounds exciting. But when I learned more or less what is it about, I got pretty sceptical of the idea. Too much \u003Cem\u003Emagic\u003C\/em\u003E, too much implied information... However, when I finally used it for the first time... Gosh I wish I could never define services manually again!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cp\u003EWhen I first heard the term \u201cAutowiring\u201d, I thought it sounds exciting. But when I learned more or less what is it about, I got pretty sceptical of the idea. Too much \u003Cem\u003Emagic\u003C\/em\u003E, too much implied information... However, when I finally used it for the first time... Gosh I wish I could never define services manually again!\u003C\/p\u003E\n\u003Cp\u003E\u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Dependency_injection\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Dependency Injection Container\u003C\/a\u003E is definitely my favourite design pattern \u2013 so elegant, simple and flexible!\u003C\/p\u003E\n\u003Cp\u003EIt comes with one inconvenience though: you basically need to code the same thing twice. If your service requires some dependency, it cannot just \u003Cem\u003Etake\u003C\/em\u003E it or just \u003Cem\u003Ecreate\u003C\/em\u003E it. Instead, it should \u003Cem\u003Eexpect\u003C\/em\u003E its dependencies, while the container \u003Cem\u003Eprovides\u003C\/em\u003E them from the outside. When you want to add a new dependency, you need to both add it in the class and in some external service definition. This doesn\u2019t look like much effort, but when you\u2019re intensively playing around with your services, it all adds up. I didn\u2019t even realise, how annoying that process is, until I no longer had to follow it.\u003C\/p\u003E\n\u003Cp\u003EWhen I started a fresh project recently, using the latest version of \u003Ca href=\u0022https:\/\/symfony.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Symfony\u003C\/a\u003E after over a year of working almost exclusively with \u003Ca href=\u0022https:\/\/phalconphp.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Phalcon\u003C\/a\u003E and \u003Ca href=\u0022https:\/\/micrus.avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus\u003C\/a\u003E, I had already forgotten about those two new features of Symfony: autowiring and autoconfiguration. I remembered by accident.\u003C\/p\u003E\n\u003Cp\u003EI needed a \u003Ca href=\u0022https:\/\/symfony.com\/doc\/current\/templating\/twig_extension.html\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Twig extension\u003C\/a\u003E, so I quickly created one: simple class, extending \u003Ccode\u003E\\Twig_Extension\u003C\/code\u003E, expecting some other service in its constructor, providing some filter in \u003Ccode\u003EgetFilters()\u003C\/code\u003E. I refreshed the tab in the browser and immediately realised I\u2019m gonna see an exception \u2013 in the end, I forgot to tag this class as a \u003Ccode\u003Etwig.extension\u003C\/code\u003E, I forgot to provide the dependencies, it has to fail! But no, \u003Cstrong\u003Eit just worked\u003C\/strong\u003E!\u003C\/p\u003E\n\u003Cp\u003EThe idea behind \u003Cstrong\u003Eautowiring\u003C\/strong\u003E is pretty simple: if a service requires an instance of \u003Ccode\u003EAppBundle\\Api\\CmsService\u003C\/code\u003E, and your DI container happens to have a service called \u003Ccode\u003EAppBundle\\Api\\CmsService\u003C\/code\u003E, which is of type \u003Ccode\u003EAppBundle\\Api\\CmsService\u003C\/code\u003E, then it\u2019s pretty safe to assume, that\u2019s the one you want.\u003C\/p\u003E\n\u003Cp\u003EIdeally, you should depend on abstractions, interfaces. For instance when you have multiple cache adapters, all implementing \u003Ccode\u003EPsr\\Cache\\CacheItemPoolInterface\u003C\/code\u003E, your services should depend on \u003Ccode\u003EPsr\\Cache\\CacheItemPoolInterface\u003C\/code\u003E, not on any specific implementation. This way you can always switch for instance from \u003Ccode\u003EFilesystemAdapter\u003C\/code\u003E to \u003Ccode\u003ERedisAdapter\u003C\/code\u003E without any trouble.\u003C\/p\u003E\n\u003Cp\u003EBut if you have more than one implementation of an interface, how does the autowired container know, which one to use? You tell it, by creating an alias:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs yaml border\u0022\u003E\u003Cspan class=\u0022hljs-string\u0022\u003EPsr\\Cache\\CacheItemPoolInterface:\u003C\/span\u003E \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027@Symfony\\Component\\Cache\\Adapter\u0027\u003C\/span\u003E\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAnd what about the scalars and arrays? If my service expects \u003Ccode\u003Estring $baseUrl\u003C\/code\u003E, how does the container know \u003Cem\u003Ethat\u003C\/em\u003E? Of course it doesn\u2019t, I need to specify it myself:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs yaml border\u0022\u003E\u003Cspan class=\u0022hljs-string\u0022\u003EAppBundle\\Service\\ExternalSerive:\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-attr\u0022\u003Earguments:\u003C\/span\u003E\n        \u003Cspan class=\u0022hljs-string\u0022\u003E$baseUsr:\u003C\/span\u003E \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027https:\/\/api.external.org\/\u0027\u003C\/span\u003E\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can do the same thing for services as well. For instance when all of them should use some generic logger, except for one, which requires something more specific, just overwrite its \u003Ccode\u003Earguments.$logger\u003C\/code\u003E with the other one.\u003C\/p\u003E\n\u003Cp\u003EPretty simple, right? The idea behind \u003Cstrong\u003Eautoconfiguration\u003C\/strong\u003E is even simpler. You (or libraries, like Twig) can define that all the services which are an instance of \u003Ccode\u003EFoo\u003C\/code\u003E should automatically receive some specific config, for instance a tag. That\u2019s why I didn\u2019t have to register my Twig extension.\u003C\/p\u003E\n\u003Cp\u003EMain concern one might have about autowiring is that, although pretty simple, it sure is time-consuming. Should I sacrifice my website\u2019s performance for the developer\u2019s convenience? Of course not! Symfony\u2019s container is compiled, so autowiring has absolutely no overhead on production!\u003C\/p\u003E\n\u003Cp\u003EAlso, Symfony Container has a concept of public and private services. Public ones you can just fetch from the container wherever you wish. Private ones can only be injected into other services \u2013 if they aren\u2019t used, they get removed from the container completely.\u003C\/p\u003E\n\u003Cp\u003EAutowiring is amazing. Simple, but effective. It makes the development faster, especially in the early phase. It prevents you from repeating yourself and maintaining a lengthy \u003Ccode\u003Eservices.yml\u003C\/code\u003E file full of information noise. And, although magical, it\u2019s not magic!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["autoconfiguration","autowiring","config","container","dependency injection","php","service","symfony"],"hasMore":true,"image":null,"introLite":"\u003Cp\u003EWhen I first heard the term \u201cAutowiring\u201d, I thought it sounds exciting. But when I learned more or less what is it about, I got pretty sceptical of the idea. Too much \u003Cem\u003Emagic\u003C\/em\u003E, too much implied information... However, when I finally used it for the first time... Gosh I wish I could never define services manually again!\u003C\/p\u003E","contentLite":"\u003Cp\u003EWhen I first heard the term \u201cAutowiring\u201d, I thought it sounds exciting. But when I learned more or less what is it about, I got pretty sceptical of the idea. Too much \u003Cem\u003Emagic\u003C\/em\u003E, too much implied information... However, when I finally used it for the first time... Gosh I wish I could never define services manually again!\u003C\/p\u003E\n\u003Cp\u003E\u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Dependency_injection\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Dependency Injection Container\u003C\/a\u003E is definitely my favourite design pattern \u2013 so elegant, simple and flexible!\u003C\/p\u003E\n\u003Cp\u003EIt comes with one inconvenience though: you basically need to code the same thing twice. If your service requires some dependency, it cannot just \u003Cem\u003Etake\u003C\/em\u003E it or just \u003Cem\u003Ecreate\u003C\/em\u003E it. Instead, it should \u003Cem\u003Eexpect\u003C\/em\u003E its dependencies, while the container \u003Cem\u003Eprovides\u003C\/em\u003E them from the outside. When you want to add a new dependency, you need to both add it in the class and in some external service definition. This doesn\u2019t look like much effort, but when you\u2019re intensively playing around with your services, it all adds up. I didn\u2019t even realise, how annoying that process is, until I no longer had to follow it.\u003C\/p\u003E\n\u003Cp\u003EWhen I started a fresh project recently, using the latest version of \u003Ca href=\u0022https:\/\/symfony.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Symfony\u003C\/a\u003E after over a year of working almost exclusively with \u003Ca href=\u0022https:\/\/phalconphp.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Phalcon\u003C\/a\u003E and \u003Ca href=\u0022https:\/\/micrus.avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Micrus\u003C\/a\u003E, I had already forgotten about those two new features of Symfony: autowiring and autoconfiguration. I remembered by accident.\u003C\/p\u003E\n\u003Cp\u003EI needed a \u003Ca href=\u0022https:\/\/symfony.com\/doc\/current\/templating\/twig_extension.html\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Twig extension\u003C\/a\u003E, so I quickly created one: simple class, extending \u003Ccode\u003E\\Twig_Extension\u003C\/code\u003E, expecting some other service in its constructor, providing some filter in \u003Ccode\u003EgetFilters()\u003C\/code\u003E. I refreshed the tab in the browser and immediately realised I\u2019m gonna see an exception \u2013 in the end, I forgot to tag this class as a \u003Ccode\u003Etwig.extension\u003C\/code\u003E, I forgot to provide the dependencies, it has to fail! But no, \u003Cstrong\u003Eit just worked\u003C\/strong\u003E!\u003C\/p\u003E\n\u003Cp\u003EThe idea behind \u003Cstrong\u003Eautowiring\u003C\/strong\u003E is pretty simple: if a service requires an instance of \u003Ccode\u003EAppBundle\\Api\\CmsService\u003C\/code\u003E, and your DI container happens to have a service called \u003Ccode\u003EAppBundle\\Api\\CmsService\u003C\/code\u003E, which is of type \u003Ccode\u003EAppBundle\\Api\\CmsService\u003C\/code\u003E, then it\u2019s pretty safe to assume, that\u2019s the one you want.\u003C\/p\u003E\n\u003Cp\u003EIdeally, you should depend on abstractions, interfaces. For instance when you have multiple cache adapters, all implementing \u003Ccode\u003EPsr\\Cache\\CacheItemPoolInterface\u003C\/code\u003E, your services should depend on \u003Ccode\u003EPsr\\Cache\\CacheItemPoolInterface\u003C\/code\u003E, not on any specific implementation. This way you can always switch for instance from \u003Ccode\u003EFilesystemAdapter\u003C\/code\u003E to \u003Ccode\u003ERedisAdapter\u003C\/code\u003E without any trouble.\u003C\/p\u003E\n\u003Cp\u003EBut if you have more than one implementation of an interface, how does the autowired container know, which one to use? You tell it, by creating an alias:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-yaml\u0022\u003EPsr\\Cache\\CacheItemPoolInterface: \u0027@Symfony\\Component\\Cache\\Adapter\u0027\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EAnd what about the scalars and arrays? If my service expects \u003Ccode\u003Estring $baseUrl\u003C\/code\u003E, how does the container know \u003Cem\u003Ethat\u003C\/em\u003E? Of course it doesn\u2019t, I need to specify it myself:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-yaml\u0022\u003EAppBundle\\Service\\ExternalSerive:\n    arguments:\n        $baseUsr: \u0027https:\/\/api.external.org\/\u0027\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EYou can do the same thing for services as well. For instance when all of them should use some generic logger, except for one, which requires something more specific, just overwrite its \u003Ccode\u003Earguments.$logger\u003C\/code\u003E with the other one.\u003C\/p\u003E\n\u003Cp\u003EPretty simple, right? The idea behind \u003Cstrong\u003Eautoconfiguration\u003C\/strong\u003E is even simpler. You (or libraries, like Twig) can define that all the services which are an instance of \u003Ccode\u003EFoo\u003C\/code\u003E should automatically receive some specific config, for instance a tag. That\u2019s why I didn\u2019t have to register my Twig extension.\u003C\/p\u003E\n\u003Cp\u003EMain concern one might have about autowiring is that, although pretty simple, it sure is time-consuming. Should I sacrifice my website\u2019s performance for the developer\u2019s convenience? Of course not! Symfony\u2019s container is compiled, so autowiring has absolutely no overhead on production!\u003C\/p\u003E\n\u003Cp\u003EAlso, Symfony Container has a concept of public and private services. Public ones you can just fetch from the container wherever you wish. Private ones can only be injected into other services \u2013 if they aren\u2019t used, they get removed from the container completely.\u003C\/p\u003E\n\u003Cp\u003EAutowiring is amazing. Simple, but effective. It makes the development faster, especially in the early phase. It prevents you from repeating yourself and maintaining a lengthy \u003Ccode\u003Eservices.yml\u003C\/code\u003E file full of information noise. And, although magical, it\u2019s not magic!\u003C\/p\u003E","words":680,"readTime":3,"lang":"en"}}},"blog\/technology\/brand-new-blog":{"key":"blog\/technology\/brand-new-blog","type":"article","published":true,"meta":{"createdAt":"2017-04-30T21:22:09+02:00","publishedAt":"2017-04-30T21:05:00+02:00","group":"redesign","category":"blog","subcategory":"technology","slug":"brand-new-blog"},"content":{"en":{"slug":"brand-new-blog","title":"Brand new blog","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/new-avris_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022250.4347826087\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAASCAYAAAAzI3woAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFD0lEQVRIia2Wz4tdWRHHP1Xn3Pt+dNuvO+m0GY2jOATHzDBmoy5E3Sgogrh14d6\/wIUr3fgfjBtBXLgRN6OIIDKIoMK4GCGgTMCJmR9J6CTTmZ5+\/d6995yqcnFfMll0Bgc8m1N1qjj1rTp1v3UlIoIz1u3btymlsFjssru7OMuFo3feISKYb22xXJ6ws7Pg\/v377O\/v8+DBA5qmQVXp+57JZEI66Zg\/tU8\/DMznc0SEiEBEAEb5Z7\/8VSAgqogqDogk3J1mOkUF0ASipJSJpIgIWRKBsLkLBUISouOBANRKRECAh1PKAEkJq4RV3AyvhbAAN1RApp96LnQ6JU1mpJRxnEDI0zm5acnTGeRMmswgNaTpjEYSOSlIptpAAhRBRGimc6pVdOgRB\/ERTB06hnDCg269xLolpe\/x9Ql1tcLLwEQSudVEO5mRZ9tIkxmGNeHBpG1xTeCG9IaKItMM3Sky3xkhWCEVw1QgTVE3ymqF1YqaM53vUK0Q3QohoUOHu6MB7qC1UiUhzYTkguPkWh1fvsd2bmjaltUwYC6gKyqJlBs8KU1AUwvNdI6dntBOJ1goeWvB1myHWn2s4HrN0o8xK1z5+AVidcxr12\/RVwerDH2PheOrY7wMYAbmuBsaTrYQwhPH7x4jqzWqiUgN666gjRJiaG759le\/RDuZ8cdXr\/GtL36Ol\/7yDybbC2S+YN1sEQyIKLu7mY\/plJyE1eoU6QuXLh4gAsuTJW++fQdD8DzFhop7hWqEGR6Q8s6FH3kYooJ5ECi+adRmOqfZ3uP5z1zm1z\/5AQh892tf5sXf\/ol3B6NPE8pg9N0pw9DRrU+o61PObc2IqLQ5oznhtdJKcOvOIe91A1EGrFSslBGQVbwWkkD2cNAGcxAFs0pKCXKLaSIi+PrnX+D1t+\/wr\/+8xYsvvcxyqGhqiG6NJSMINAIPo20yxbbAjW4oZAlKGejNKO50\/Rpxx6oRCBGCx1gIEKS5+NkgJyQl0AbJCZl9hDTbxsNA4MrlZ7h0YZ\/ejINze\/zmb69iCJoTmltAUDNwx7xyaW9BrRW8kiLACtTK4eFd+m5FVAeCqAPhPtJAKbSayBEOoeCBpA2X1Ep0HSEBqtw7vIfUyt3jJSdPnRLrDgS8aYhhQDSBCF7HJn3r9ilujoYRpRJWEDe8VsKciAJDIawiHkQ4EgJhSDp4NiQlIo3ZelJSnoxfl8jIcAJoi6SM5rw5UzSN\/aYBgcOGZ3AjfHzGsIJbRZ1Nr9gIxHrCIGwgzBCEVgX5819fCUR4RLmim10eUTpsgG2YOZCHLgRAvC\/LRg8CiRif5uFwCh89wkfnx3VARJC+7+Oh8nCuPL4\/vs6y\/b\/lfObUBG7evEnOmel0SkSwWCxo2\/aR\/caNG+zt7VFrRVU5f\/78k676UOuJgI4Ob3Jut+V0DZNJw9Jakiqq0M4OODg4IKWRFh5m+XhF\/xf5QwH6xc9\/zNVn3yU5\/O73d7m4P+M739hnZydx\/pnv8emr36dpGgBevnaXi3sdz31iB4BSCsAj+\/VbJxwe93zlyj4AtVbc\/cy4+iRAR1Wwy3v84e8d33z+C5R7c3740zd4bfccw+KFMXB1+mJsTzOTrPTFqPZ+oIigmpOTMMlKN9gHVucDK3S07Hnlnys+Wc9x\/c271GIcHxauvX7E6Rv\/5umPXsVC6Abj0gJg4N7RwKxN5E2aKSXWgzFX5+k9pesHlPFfS\/XsWvwX97wRJTQmN3gAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/new-avris_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022250.4347826087\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFinally. I got to work and rewrote the code of my sweet blog. Brand new design, new framework, \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus\u003C\/a\u003E, better support for language versions, a couple of new features in the admin panel, ditching custom comments for the awesomeness of \u003Ca href=\u0022https:\/\/disqus.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Disqus\u003C\/a\u003E, ditching TinyMCE for the beauty and simplicity of Markdown. It was a lot of work, but it was definitely worth it!\u003C\/p\u003E\n\u003Cp\u003EHope you like it! :)\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/new-avris_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022500.86956521739\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAASCAYAAAAzI3woAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFD0lEQVRIia2Wz4tdWRHHP1Xn3Pt+dNuvO+m0GY2jOATHzDBmoy5E3Sgogrh14d6\/wIUr3fgfjBtBXLgRN6OIIDKIoMK4GCGgTMCJmR9J6CTTmZ5+\/d6995yqcnFfMll0Bgc8m1N1qjj1rTp1v3UlIoIz1u3btymlsFjssru7OMuFo3feISKYb22xXJ6ws7Pg\/v377O\/v8+DBA5qmQVXp+57JZEI66Zg\/tU8\/DMznc0SEiEBEAEb5Z7\/8VSAgqogqDogk3J1mOkUF0ASipJSJpIgIWRKBsLkLBUISouOBANRKRECAh1PKAEkJq4RV3AyvhbAAN1RApp96LnQ6JU1mpJRxnEDI0zm5acnTGeRMmswgNaTpjEYSOSlIptpAAhRBRGimc6pVdOgRB\/ERTB06hnDCg269xLolpe\/x9Ql1tcLLwEQSudVEO5mRZ9tIkxmGNeHBpG1xTeCG9IaKItMM3Sky3xkhWCEVw1QgTVE3ymqF1YqaM53vUK0Q3QohoUOHu6MB7qC1UiUhzYTkguPkWh1fvsd2bmjaltUwYC6gKyqJlBs8KU1AUwvNdI6dntBOJ1goeWvB1myHWn2s4HrN0o8xK1z5+AVidcxr12\/RVwerDH2PheOrY7wMYAbmuBsaTrYQwhPH7x4jqzWqiUgN666gjRJiaG759le\/RDuZ8cdXr\/GtL36Ol\/7yDybbC2S+YN1sEQyIKLu7mY\/plJyE1eoU6QuXLh4gAsuTJW++fQdD8DzFhop7hWqEGR6Q8s6FH3kYooJ5ECi+adRmOqfZ3uP5z1zm1z\/5AQh892tf5sXf\/ol3B6NPE8pg9N0pw9DRrU+o61PObc2IqLQ5oznhtdJKcOvOIe91A1EGrFSslBGQVbwWkkD2cNAGcxAFs0pKCXKLaSIi+PrnX+D1t+\/wr\/+8xYsvvcxyqGhqiG6NJSMINAIPo20yxbbAjW4oZAlKGejNKO50\/Rpxx6oRCBGCx1gIEKS5+NkgJyQl0AbJCZl9hDTbxsNA4MrlZ7h0YZ\/ejINze\/zmb69iCJoTmltAUDNwx7xyaW9BrRW8kiLACtTK4eFd+m5FVAeCqAPhPtJAKbSayBEOoeCBpA2X1Ep0HSEBqtw7vIfUyt3jJSdPnRLrDgS8aYhhQDSBCF7HJn3r9ilujoYRpRJWEDe8VsKciAJDIawiHkQ4EgJhSDp4NiQlIo3ZelJSnoxfl8jIcAJoi6SM5rw5UzSN\/aYBgcOGZ3AjfHzGsIJbRZ1Nr9gIxHrCIGwgzBCEVgX5819fCUR4RLmim10eUTpsgG2YOZCHLgRAvC\/LRg8CiRif5uFwCh89wkfnx3VARJC+7+Oh8nCuPL4\/vs6y\/b\/lfObUBG7evEnOmel0SkSwWCxo2\/aR\/caNG+zt7VFrRVU5f\/78k676UOuJgI4Ob3Jut+V0DZNJw9Jakiqq0M4OODg4IKWRFh5m+XhF\/xf5QwH6xc9\/zNVn3yU5\/O73d7m4P+M739hnZydx\/pnv8emr36dpGgBevnaXi3sdz31iB4BSCsAj+\/VbJxwe93zlyj4AtVbc\/cy4+iRAR1Wwy3v84e8d33z+C5R7c3740zd4bfccw+KFMXB1+mJsTzOTrPTFqPZ+oIigmpOTMMlKN9gHVucDK3S07Hnlnys+Wc9x\/c271GIcHxauvX7E6Rv\/5umPXsVC6Abj0gJg4N7RwKxN5E2aKSXWgzFX5+k9pesHlPFfS\/XsWvwX97wRJTQmN3gAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/new-avris_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022500.86956521739\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFinally. I got to work and rewrote the code of my sweet blog. Brand new design, new framework, \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus\u003C\/a\u003E, better support for language versions, a couple of new features in the admin panel, ditching custom comments for the awesomeness of \u003Ca href=\u0022https:\/\/disqus.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Disqus\u003C\/a\u003E, ditching TinyMCE for the beauty and simplicity of Markdown. It was a lot of work, but it was definitely worth it!\u003C\/p\u003E\n\u003Cp\u003EHope you like it! :)\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["blog","comments","design","disqus","framework","markdown","php","programming","redesign","tinymce","work"],"hasMore":false,"image":"https:\/\/avris.it\/image\/new-avris_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/new-avris_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/new-avris_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022125.21739130435\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFinally. I got to work and rewrote the code of my sweet blog. Brand new design, new framework, \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework.lite\u0022\u003E Micrus\u003C\/a\u003E, better support for language versions, a couple of new features in the admin panel, ditching custom comments for the awesomeness of \u003Ca href=\u0022https:\/\/disqus.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Disqus\u003C\/a\u003E, ditching TinyMCE for the beauty and simplicity of Markdown. It was a lot of work, but it was definitely worth it!\u003C\/p\u003E\n\u003Cp\u003EHope you like it! :)\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/new-avris_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/new-avris_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022125.21739130435\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EFinally. I got to work and rewrote the code of my sweet blog. Brand new design, new framework, \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework.lite\u0022\u003E Micrus\u003C\/a\u003E, better support for language versions, a couple of new features in the admin panel, ditching custom comments for the awesomeness of \u003Ca href=\u0022https:\/\/disqus.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Disqus\u003C\/a\u003E, ditching TinyMCE for the beauty and simplicity of Markdown. It was a lot of work, but it was definitely worth it!\u003C\/p\u003E\n\u003Cp\u003EHope you like it! :)\u003C\/p\u003E","words":66,"readTime":null,"lang":"en"},"pl":{"slug":"nowiutki-blog","title":"Nowiutki blog","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/new-avris_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022250.4347826087\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAASCAYAAAAzI3woAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFD0lEQVRIia2Wz4tdWRHHP1Xn3Pt+dNuvO+m0GY2jOATHzDBmoy5E3Sgogrh14d6\/wIUr3fgfjBtBXLgRN6OIIDKIoMK4GCGgTMCJmR9J6CTTmZ5+\/d6995yqcnFfMll0Bgc8m1N1qjj1rTp1v3UlIoIz1u3btymlsFjssru7OMuFo3feISKYb22xXJ6ws7Pg\/v377O\/v8+DBA5qmQVXp+57JZEI66Zg\/tU8\/DMznc0SEiEBEAEb5Z7\/8VSAgqogqDogk3J1mOkUF0ASipJSJpIgIWRKBsLkLBUISouOBANRKRECAh1PKAEkJq4RV3AyvhbAAN1RApp96LnQ6JU1mpJRxnEDI0zm5acnTGeRMmswgNaTpjEYSOSlIptpAAhRBRGimc6pVdOgRB\/ERTB06hnDCg269xLolpe\/x9Ql1tcLLwEQSudVEO5mRZ9tIkxmGNeHBpG1xTeCG9IaKItMM3Sky3xkhWCEVw1QgTVE3ymqF1YqaM53vUK0Q3QohoUOHu6MB7qC1UiUhzYTkguPkWh1fvsd2bmjaltUwYC6gKyqJlBs8KU1AUwvNdI6dntBOJ1goeWvB1myHWn2s4HrN0o8xK1z5+AVidcxr12\/RVwerDH2PheOrY7wMYAbmuBsaTrYQwhPH7x4jqzWqiUgN666gjRJiaG759le\/RDuZ8cdXr\/GtL36Ol\/7yDybbC2S+YN1sEQyIKLu7mY\/plJyE1eoU6QuXLh4gAsuTJW++fQdD8DzFhop7hWqEGR6Q8s6FH3kYooJ5ECi+adRmOqfZ3uP5z1zm1z\/5AQh892tf5sXf\/ol3B6NPE8pg9N0pw9DRrU+o61PObc2IqLQ5oznhtdJKcOvOIe91A1EGrFSslBGQVbwWkkD2cNAGcxAFs0pKCXKLaSIi+PrnX+D1t+\/wr\/+8xYsvvcxyqGhqiG6NJSMINAIPo20yxbbAjW4oZAlKGejNKO50\/Rpxx6oRCBGCx1gIEKS5+NkgJyQl0AbJCZl9hDTbxsNA4MrlZ7h0YZ\/ejINze\/zmb69iCJoTmltAUDNwx7xyaW9BrRW8kiLACtTK4eFd+m5FVAeCqAPhPtJAKbSayBEOoeCBpA2X1Ep0HSEBqtw7vIfUyt3jJSdPnRLrDgS8aYhhQDSBCF7HJn3r9ilujoYRpRJWEDe8VsKciAJDIawiHkQ4EgJhSDp4NiQlIo3ZelJSnoxfl8jIcAJoi6SM5rw5UzSN\/aYBgcOGZ3AjfHzGsIJbRZ1Nr9gIxHrCIGwgzBCEVgX5819fCUR4RLmim10eUTpsgG2YOZCHLgRAvC\/LRg8CiRif5uFwCh89wkfnx3VARJC+7+Oh8nCuPL4\/vs6y\/b\/lfObUBG7evEnOmel0SkSwWCxo2\/aR\/caNG+zt7VFrRVU5f\/78k676UOuJgI4Ob3Jut+V0DZNJw9Jakiqq0M4OODg4IKWRFh5m+XhF\/xf5QwH6xc9\/zNVn3yU5\/O73d7m4P+M739hnZydx\/pnv8emr36dpGgBevnaXi3sdz31iB4BSCsAj+\/VbJxwe93zlyj4AtVbc\/cy4+iRAR1Wwy3v84e8d33z+C5R7c3740zd4bfccw+KFMXB1+mJsTzOTrPTFqPZ+oIigmpOTMMlKN9gHVucDK3S07Hnlnys+Wc9x\/c271GIcHxauvX7E6Rv\/5umPXsVC6Abj0gJg4N7RwKxN5E2aKSXWgzFX5+k9pesHlPFfS\/XsWvwX97wRJTQmN3gAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/new-avris_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022250.4347826087\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWreszcie. Wzi\u0105\u0142em si\u0119 do roboty i przepisa\u0142em od zera kod mojego blogaska. Zupe\u0142nie nowy design, nowy framework, \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus\u003C\/a\u003E, lepsze wsparcie dla wersji j\u0119zykowych, par\u0119 nowych ficzer\u00f3w w panelu administracyjnym, rzucenie w\u0142asnego systemu komentarzy na rzecz zajebisto\u015bci \u003Ca href=\u0022https:\/\/disqus.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Disqusa\u003C\/a\u003E, rzucenie TinyMCE dla pi\u0119kna i prostoty Markdownu. Zaj\u0119\u0142o to sporo pracy, ale zdecydowanie by\u0142o warto!\u003C\/p\u003E\n\u003Cp\u003EMam nadziej\u0119, \u017ce si\u0119 spodoba! :)\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/new-avris_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022500.86956521739\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAASCAYAAAAzI3woAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFD0lEQVRIia2Wz4tdWRHHP1Xn3Pt+dNuvO+m0GY2jOATHzDBmoy5E3Sgogrh14d6\/wIUr3fgfjBtBXLgRN6OIIDKIoMK4GCGgTMCJmR9J6CTTmZ5+\/d6995yqcnFfMll0Bgc8m1N1qjj1rTp1v3UlIoIz1u3btymlsFjssru7OMuFo3feISKYb22xXJ6ws7Pg\/v377O\/v8+DBA5qmQVXp+57JZEI66Zg\/tU8\/DMznc0SEiEBEAEb5Z7\/8VSAgqogqDogk3J1mOkUF0ASipJSJpIgIWRKBsLkLBUISouOBANRKRECAh1PKAEkJq4RV3AyvhbAAN1RApp96LnQ6JU1mpJRxnEDI0zm5acnTGeRMmswgNaTpjEYSOSlIptpAAhRBRGimc6pVdOgRB\/ERTB06hnDCg269xLolpe\/x9Ql1tcLLwEQSudVEO5mRZ9tIkxmGNeHBpG1xTeCG9IaKItMM3Sky3xkhWCEVw1QgTVE3ymqF1YqaM53vUK0Q3QohoUOHu6MB7qC1UiUhzYTkguPkWh1fvsd2bmjaltUwYC6gKyqJlBs8KU1AUwvNdI6dntBOJ1goeWvB1myHWn2s4HrN0o8xK1z5+AVidcxr12\/RVwerDH2PheOrY7wMYAbmuBsaTrYQwhPH7x4jqzWqiUgN666gjRJiaG759le\/RDuZ8cdXr\/GtL36Ol\/7yDybbC2S+YN1sEQyIKLu7mY\/plJyE1eoU6QuXLh4gAsuTJW++fQdD8DzFhop7hWqEGR6Q8s6FH3kYooJ5ECi+adRmOqfZ3uP5z1zm1z\/5AQh892tf5sXf\/ol3B6NPE8pg9N0pw9DRrU+o61PObc2IqLQ5oznhtdJKcOvOIe91A1EGrFSslBGQVbwWkkD2cNAGcxAFs0pKCXKLaSIi+PrnX+D1t+\/wr\/+8xYsvvcxyqGhqiG6NJSMINAIPo20yxbbAjW4oZAlKGejNKO50\/Rpxx6oRCBGCx1gIEKS5+NkgJyQl0AbJCZl9hDTbxsNA4MrlZ7h0YZ\/ejINze\/zmb69iCJoTmltAUDNwx7xyaW9BrRW8kiLACtTK4eFd+m5FVAeCqAPhPtJAKbSayBEOoeCBpA2X1Ep0HSEBqtw7vIfUyt3jJSdPnRLrDgS8aYhhQDSBCF7HJn3r9ilujoYRpRJWEDe8VsKciAJDIawiHkQ4EgJhSDp4NiQlIo3ZelJSnoxfl8jIcAJoi6SM5rw5UzSN\/aYBgcOGZ3AjfHzGsIJbRZ1Nr9gIxHrCIGwgzBCEVgX5819fCUR4RLmim10eUTpsgG2YOZCHLgRAvC\/LRg8CiRif5uFwCh89wkfnx3VARJC+7+Oh8nCuPL4\/vs6y\/b\/lfObUBG7evEnOmel0SkSwWCxo2\/aR\/caNG+zt7VFrRVU5f\/78k676UOuJgI4Ob3Jut+V0DZNJw9Jakiqq0M4OODg4IKWRFh5m+XhF\/xf5QwH6xc9\/zNVn3yU5\/O73d7m4P+M739hnZydx\/pnv8emr36dpGgBevnaXi3sdz31iB4BSCsAj+\/VbJxwe93zlyj4AtVbc\/cy4+iRAR1Wwy3v84e8d33z+C5R7c3740zd4bfccw+KFMXB1+mJsTzOTrPTFqPZ+oIigmpOTMMlKN9gHVucDK3S07Hnlnys+Wc9x\/c271GIcHxauvX7E6Rv\/5umPXsVC6Abj0gJg4N7RwKxN5E2aKSXWgzFX5+k9pesHlPFfS\/XsWvwX97wRJTQmN3gAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/new-avris_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022500.86956521739\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWreszcie. Wzi\u0105\u0142em si\u0119 do roboty i przepisa\u0142em od zera kod mojego blogaska. Zupe\u0142nie nowy design, nowy framework, \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrus\u003C\/a\u003E, lepsze wsparcie dla wersji j\u0119zykowych, par\u0119 nowych ficzer\u00f3w w panelu administracyjnym, rzucenie w\u0142asnego systemu komentarzy na rzecz zajebisto\u015bci \u003Ca href=\u0022https:\/\/disqus.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Disqusa\u003C\/a\u003E, rzucenie TinyMCE dla pi\u0119kna i prostoty Markdownu. Zaj\u0119\u0142o to sporo pracy, ale zdecydowanie by\u0142o warto!\u003C\/p\u003E\n\u003Cp\u003EMam nadziej\u0119, \u017ce si\u0119 spodoba! :)\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["design","framework","komentarz","markdown","php","praca","programowanie","redesign","tinymce"],"hasMore":false,"image":"https:\/\/avris.it\/image\/new-avris_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/new-avris_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/new-avris_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022125.21739130435\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWreszcie. Wzi\u0105\u0142em si\u0119 do roboty i przepisa\u0142em od zera kod mojego blogaska. Zupe\u0142nie nowy design, nowy framework, \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework.lite\u0022\u003E Micrus\u003C\/a\u003E, lepsze wsparcie dla wersji j\u0119zykowych, par\u0119 nowych ficzer\u00f3w w panelu administracyjnym, rzucenie w\u0142asnego systemu komentarzy na rzecz zajebisto\u015bci \u003Ca href=\u0022https:\/\/disqus.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Disqusa\u003C\/a\u003E, rzucenie TinyMCE dla pi\u0119kna i prostoty Markdownu. Zaj\u0119\u0142o to sporo pracy, ale zdecydowanie by\u0142o warto!\u003C\/p\u003E\n\u003Cp\u003EMam nadziej\u0119, \u017ce si\u0119 spodoba! :)\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/new-avris_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/new-avris_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022125.21739130435\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWreszcie. Wzi\u0105\u0142em si\u0119 do roboty i przepisa\u0142em od zera kod mojego blogaska. Zupe\u0142nie nowy design, nowy framework, \u003Ca href=\u0022\/projects\/micrus-tiny-yet-powerful-php-framework.lite\u0022\u003E Micrus\u003C\/a\u003E, lepsze wsparcie dla wersji j\u0119zykowych, par\u0119 nowych ficzer\u00f3w w panelu administracyjnym, rzucenie w\u0142asnego systemu komentarzy na rzecz zajebisto\u015bci \u003Ca href=\u0022https:\/\/disqus.com\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Disqusa\u003C\/a\u003E, rzucenie TinyMCE dla pi\u0119kna i prostoty Markdownu. Zaj\u0119\u0142o to sporo pracy, ale zdecydowanie by\u0142o warto!\u003C\/p\u003E\n\u003Cp\u003EMam nadziej\u0119, \u017ce si\u0119 spodoba! :)\u003C\/p\u003E","words":67,"readTime":null,"lang":"pl"}}},"projects\/micrus-tiny-yet-powerful-php-framework":{"key":"projects\/micrus-tiny-yet-powerful-php-framework","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:38+02:00","publishedAt":"2017-03-11T12:00:00+01:00","group":"micrus","links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/micrus.avris.it","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Micrus","displayUrl":null}],"category":"projects","subcategory":null,"slug":"micrus-tiny-yet-powerful-php-framework"},"content":{"en":{"slug":"micrus-tiny-yet-powerful-php-framework","title":"Micrus \u2022 Tiny, yet powerful, PHP framework","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGCUlEQVRIiU2Wy45dVxGGv6q19u2cvuC2u9NJhIkcCRSQAhKXF2DChAED3odX4Rl4A2CAxEWKlBBQiBOTqON0226f0+eyL6uqGOxj45L2YA\/W0r\/++uv\/S4DgUKKKdDXy1jH66BQi8PVA7Lakmx5dQULIIgy+R0RINJRfnOGdQwpw8PUAvRBjgSggExQHC\/S\/Ai5EBCICQEQQ4UQE+TUYAV0oehLYuMKvB\/SsI192lG9BXwgqUIkyeSEIQIhHS+qfLyk3e8KE6A3JDb4OxBNIS+wK4T2yBG0DGQJBEFE8HPeCWQBvAIpWMAIfHGol1oXYbLD6jvxOIl9m2h00wKqPw8tAukS+l4khE0XwBJ4SqYEoQuwnZKEQCT3N6EVG\/3gLIYDg4QdCBNAZkHSJyEAWIoNk5kvqhAC2Ekwm2p+1NI8z9s2AiOAXNeUE8mhIoyCQTjJ6lMCdGA0fBUkJ0QqpE\/J2xv5zBF+v8XDiAOhVZWQGQpORNkGtSJeQCkggR3mWmSurXWH\/CGTI+LogHx4RJbDbCW0zcpQAATPAASWiQTSBClILooL+6pTp93t0mnAgwl5rSaTOwf0WuoS0h29ZoccZaSCGQrihywQl8MFInRKPd\/DeKVIV6veWSFeRjjIkxfsRLEBnXNoK0iQkKZIzoAz\/2FL+dEO53mOr4bWoRe8tg+8ukEVGjzJ6kmZmMkgF2iraKbYx8CAmRxqhejtDmQgXdFEjbYO2EIMR4WiV53tUkLZC64RUig\/gm6C8HPHNRPQT9mTD9NmW8q87siwFvWzQ4wrtBO0CaQWpBEnz+Ekj5IsGUKarEXtpMMG7lxdUtzVX4wvMChDE3tB7NZIFyRldNogKYeA7JyaIcX4cEfgeLHXI+xX6dCTrsCXdTsjJEelsSXW+RLqM5ABxKDOV+IRo0H2wpP\/3yPjFlt+e\/5o7u+N29xy6gr5sqaj5g\/0Fe5CJKaNFMRuJsRBjzMD6iegN3zs+ODEEsVckZUSSRKqEtBDkoZJ\/sqT6wX2ah2dIlSAc0QB1iCAmQxcdu7+vOF59h4ePLlmvv+aoKzTpmOd75enxS8gxj73NbQsH74OwwDcTtjJsY\/jGsWeF2Bn62S0ChIigqmgSqnPQH2fSh6c033+HfHbvMLazBs6\/rGhTZlFqHq0u+d75gmfrFR\/ffEyjJ2w9UeiRSln5jqsHqxlE7\/hg2KrgeyN284D42rHribgbSFc78iv7BnALpmvl6K8JuV2zeznQfNBTvXUfbWtEMtfvBnko2Ljh0y8+5zcnP+XkvGJVNryonOtPrqgeNkRArit8PLDSF3w7EQNgc6L4OrAXE7Ebif1IhJPlVW4c3LLLFctSM3xaKN9swb9Fcqa6uEDqCrLgyxZNHT988oCPnnyF3I7Y04p7R8rZw\/f58vwlviuUYvid4bsCATEK4YFvA3tRsNVErAdivYfdNMtDZG6ZKFQ5s6xq8GDdj6BC\/Q7wy2OaH13OTFU1uqiBTJQRbQLvJ\/b\/XIGPhENYEDuIaRYtDt77bBsWxN7xreHPB\/zZFu4GohhqQRKR34mAKqQsFJz9WDCf80o3gtyO+JkhtaANs9tFgARSgy4r0ukCSQU08P0IGLE3oi\/4\/jBVdxN2M+I3A347zqz0ZbaB4oQdwlUlkWXWkIcRQK6F0KAUJ74S9M9rpM1IVlIYkgtp2RFlTn1tanRZ4\/2EZCEGA43ZMmJO8nBAAypgcMJmEPghaYJZ1HJI7lIABNFAMugicAe2wOfOeLFClw3aZGjBtk5aVERxCJujsU5om7E7A7dDDgIWiMwtwwKKHXakA9uHrSxHBCVsPgSoKqD4GPOO1AUsIHqIj0aG+2u0bcjnC0TAh5gzJhRM0LrG24K2gg2BJMF9dmjv47VTR5lBiR+MlxlUnn\/eBHNg2BUZYhZiciIFbKH8bcN0voScSYsKaUCbBFKwu4Kt9kiroDrHR5pZ8LtC7IIYHHYGQyEmP7D05vrxRv0fXCCiRAiYIAHhjlsQV4Xx8XaeNFESgk0FyQaSCRfsZgRRIBHms\/gbiBHoD9oZHSab23domTj8DzmtyTndp0MuAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/micrus_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHow big does a framework need to be to provide you with a quick, easy and comfortable way of creating neatly structured MVC websites? That can easily be extended and configured?\u003C\/p\u003E\n\u003Cp\u003EWell, not big at all. Just try Micrus! Its goal is to keep it as simple as possible, while offering all the most important features, as listed here:\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022535\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGCUlEQVRIiU2Wy45dVxGGv6q19u2cvuC2u9NJhIkcCRSQAhKXF2DChAED3odX4Rl4A2CAxEWKlBBQiBOTqON0226f0+eyL6uqGOxj45L2YA\/W0r\/++uv\/S4DgUKKKdDXy1jH66BQi8PVA7Lakmx5dQULIIgy+R0RINJRfnOGdQwpw8PUAvRBjgSggExQHC\/S\/Ai5EBCICQEQQ4UQE+TUYAV0oehLYuMKvB\/SsI192lG9BXwgqUIkyeSEIQIhHS+qfLyk3e8KE6A3JDb4OxBNIS+wK4T2yBG0DGQJBEFE8HPeCWQBvAIpWMAIfHGol1oXYbLD6jvxOIl9m2h00wKqPw8tAukS+l4khE0XwBJ4SqYEoQuwnZKEQCT3N6EVG\/3gLIYDg4QdCBNAZkHSJyEAWIoNk5kvqhAC2Ekwm2p+1NI8z9s2AiOAXNeUE8mhIoyCQTjJ6lMCdGA0fBUkJ0QqpE\/J2xv5zBF+v8XDiAOhVZWQGQpORNkGtSJeQCkggR3mWmSurXWH\/CGTI+LogHx4RJbDbCW0zcpQAATPAASWiQTSBClILooL+6pTp93t0mnAgwl5rSaTOwf0WuoS0h29ZoccZaSCGQrihywQl8MFInRKPd\/DeKVIV6veWSFeRjjIkxfsRLEBnXNoK0iQkKZIzoAz\/2FL+dEO53mOr4bWoRe8tg+8ukEVGjzJ6kmZmMkgF2iraKbYx8CAmRxqhejtDmQgXdFEjbYO2EIMR4WiV53tUkLZC64RUig\/gm6C8HPHNRPQT9mTD9NmW8q87siwFvWzQ4wrtBO0CaQWpBEnz+Ekj5IsGUKarEXtpMMG7lxdUtzVX4wvMChDE3tB7NZIFyRldNogKYeA7JyaIcX4cEfgeLHXI+xX6dCTrsCXdTsjJEelsSXW+RLqM5ABxKDOV+IRo0H2wpP\/3yPjFlt+e\/5o7u+N29xy6gr5sqaj5g\/0Fe5CJKaNFMRuJsRBjzMD6iegN3zs+ODEEsVckZUSSRKqEtBDkoZJ\/sqT6wX2ah2dIlSAc0QB1iCAmQxcdu7+vOF59h4ePLlmvv+aoKzTpmOd75enxS8gxj73NbQsH74OwwDcTtjJsY\/jGsWeF2Bn62S0ChIigqmgSqnPQH2fSh6c033+HfHbvMLazBs6\/rGhTZlFqHq0u+d75gmfrFR\/ffEyjJ2w9UeiRSln5jqsHqxlE7\/hg2KrgeyN284D42rHribgbSFc78iv7BnALpmvl6K8JuV2zeznQfNBTvXUfbWtEMtfvBnko2Ljh0y8+5zcnP+XkvGJVNryonOtPrqgeNkRArit8PLDSF3w7EQNgc6L4OrAXE7Ebif1IhJPlVW4c3LLLFctSM3xaKN9swb9Fcqa6uEDqCrLgyxZNHT988oCPnnyF3I7Y04p7R8rZw\/f58vwlviuUYvid4bsCATEK4YFvA3tRsNVErAdivYfdNMtDZG6ZKFQ5s6xq8GDdj6BC\/Q7wy2OaH13OTFU1uqiBTJQRbQLvJ\/b\/XIGPhENYEDuIaRYtDt77bBsWxN7xreHPB\/zZFu4GohhqQRKR34mAKqQsFJz9WDCf80o3gtyO+JkhtaANs9tFgARSgy4r0ukCSQU08P0IGLE3oi\/4\/jBVdxN2M+I3A347zqz0ZbaB4oQdwlUlkWXWkIcRQK6F0KAUJ74S9M9rpM1IVlIYkgtp2RFlTn1tanRZ4\/2EZCEGA43ZMmJO8nBAAypgcMJmEPghaYJZ1HJI7lIABNFAMugicAe2wOfOeLFClw3aZGjBtk5aVERxCJujsU5om7E7A7dDDgIWiMwtwwKKHXakA9uHrSxHBCVsPgSoKqD4GPOO1AUsIHqIj0aG+2u0bcjnC0TAh5gzJhRM0LrG24K2gg2BJMF9dmjv47VTR5lBiR+MlxlUnn\/eBHNg2BUZYhZiciIFbKH8bcN0voScSYsKaUCbBFKwu4Kt9kiroDrHR5pZ8LtC7IIYHHYGQyEmP7D05vrxRv0fXCCiRAiYIAHhjlsQV4Xx8XaeNFESgk0FyQaSCRfsZgRRIBHms\/gbiBHoD9oZHSab23domTj8DzmtyTndp0MuAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/micrus_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022535\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHow big does a framework need to be to provide you with a quick, easy and comfortable way of creating neatly structured MVC websites? That can easily be extended and configured?\u003C\/p\u003E\n\u003Cp\u003EWell, not big at all. Just try Micrus! Its goal is to keep it as simple as possible, while offering all the most important features, as listed here:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EObject oriented\u003C\/li\u003E\n\u003Cli\u003EVery clear MVC structure\u003C\/li\u003E\n\u003Cli\u003EDependency Injection Container\u003C\/li\u003E\n\u003Cli\u003EEvent dispatching\u003C\/li\u003E\n\u003Cli\u003EORM agnostic, support for Doctrine and RedBean\u003C\/li\u003E\n\u003Cli\u003EREST-ful routing\u003C\/li\u003E\n\u003Cli\u003ECRUD generator\u003C\/li\u003E\n\u003Cli\u003ETemplate engine agnostic, support for Twig and plain PHP\u003C\/li\u003E\n\u003Cli\u003EAuthentication and authorization\u003C\/li\u003E\n\u003Cli\u003EEasy configuration with YAML\u003C\/li\u003E\n\u003Cli\u003EConsole tasks using Symfony Console\u003C\/li\u003E\n\u003Cli\u003EForms\u003C\/li\u003E\n\u003Cli\u003ELocalization\u003C\/li\u003E\n\u003Cli\u003ELogging with Monolog (PSR-3 compliant)\u003C\/li\u003E\n\u003Cli\u003ECaching (PSR-6 compliant)\u003C\/li\u003E\n\u003Cli\u003EFlash messages\u003C\/li\u003E\n\u003C\/ul\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["extendable","framework","full-stack","php","plugins","programming"],"hasMore":true,"image":"https:\/\/avris.it\/image\/micrus_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/micrus_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.75\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHow big does a framework need to be to provide you with a quick, easy and comfortable way of creating neatly structured MVC websites? That can easily be extended and configured?\u003C\/p\u003E\n\u003Cp\u003EWell, not big at all. Just try Micrus! Its goal is to keep it as simple as possible, while offering all the most important features, as listed here:\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/micrus_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.75\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EHow big does a framework need to be to provide you with a quick, easy and comfortable way of creating neatly structured MVC websites? That can easily be extended and configured?\u003C\/p\u003E\n\u003Cp\u003EWell, not big at all. Just try Micrus! Its goal is to keep it as simple as possible, while offering all the most important features, as listed here:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EObject oriented\u003C\/li\u003E\n\u003Cli\u003EVery clear MVC structure\u003C\/li\u003E\n\u003Cli\u003EDependency Injection Container\u003C\/li\u003E\n\u003Cli\u003EEvent dispatching\u003C\/li\u003E\n\u003Cli\u003EORM agnostic, support for Doctrine and RedBean\u003C\/li\u003E\n\u003Cli\u003EREST-ful routing\u003C\/li\u003E\n\u003Cli\u003ECRUD generator\u003C\/li\u003E\n\u003Cli\u003ETemplate engine agnostic, support for Twig and plain PHP\u003C\/li\u003E\n\u003Cli\u003EAuthentication and authorization\u003C\/li\u003E\n\u003Cli\u003EEasy configuration with YAML\u003C\/li\u003E\n\u003Cli\u003EConsole tasks using Symfony Console\u003C\/li\u003E\n\u003Cli\u003EForms\u003C\/li\u003E\n\u003Cli\u003ELocalization\u003C\/li\u003E\n\u003Cli\u003ELogging with Monolog (PSR-3 compliant)\u003C\/li\u003E\n\u003Cli\u003ECaching (PSR-6 compliant)\u003C\/li\u003E\n\u003Cli\u003EFlash messages\u003C\/li\u003E\n\u003C\/ul\u003E","words":114,"readTime":null,"lang":"en"},"pl":{"slug":"micrus-drobny-lecz-skuteczny-framework-dla-php","title":"Micrus \u2022 Drobny, lecz skuteczny, framework dla PHP","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.5\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGCUlEQVRIiU2Wy45dVxGGv6q19u2cvuC2u9NJhIkcCRSQAhKXF2DChAED3odX4Rl4A2CAxEWKlBBQiBOTqON0226f0+eyL6uqGOxj45L2YA\/W0r\/++uv\/S4DgUKKKdDXy1jH66BQi8PVA7Lakmx5dQULIIgy+R0RINJRfnOGdQwpw8PUAvRBjgSggExQHC\/S\/Ai5EBCICQEQQ4UQE+TUYAV0oehLYuMKvB\/SsI192lG9BXwgqUIkyeSEIQIhHS+qfLyk3e8KE6A3JDb4OxBNIS+wK4T2yBG0DGQJBEFE8HPeCWQBvAIpWMAIfHGol1oXYbLD6jvxOIl9m2h00wKqPw8tAukS+l4khE0XwBJ4SqYEoQuwnZKEQCT3N6EVG\/3gLIYDg4QdCBNAZkHSJyEAWIoNk5kvqhAC2Ekwm2p+1NI8z9s2AiOAXNeUE8mhIoyCQTjJ6lMCdGA0fBUkJ0QqpE\/J2xv5zBF+v8XDiAOhVZWQGQpORNkGtSJeQCkggR3mWmSurXWH\/CGTI+LogHx4RJbDbCW0zcpQAATPAASWiQTSBClILooL+6pTp93t0mnAgwl5rSaTOwf0WuoS0h29ZoccZaSCGQrihywQl8MFInRKPd\/DeKVIV6veWSFeRjjIkxfsRLEBnXNoK0iQkKZIzoAz\/2FL+dEO53mOr4bWoRe8tg+8ukEVGjzJ6kmZmMkgF2iraKbYx8CAmRxqhejtDmQgXdFEjbYO2EIMR4WiV53tUkLZC64RUig\/gm6C8HPHNRPQT9mTD9NmW8q87siwFvWzQ4wrtBO0CaQWpBEnz+Ekj5IsGUKarEXtpMMG7lxdUtzVX4wvMChDE3tB7NZIFyRldNogKYeA7JyaIcX4cEfgeLHXI+xX6dCTrsCXdTsjJEelsSXW+RLqM5ABxKDOV+IRo0H2wpP\/3yPjFlt+e\/5o7u+N29xy6gr5sqaj5g\/0Fe5CJKaNFMRuJsRBjzMD6iegN3zs+ODEEsVckZUSSRKqEtBDkoZJ\/sqT6wX2ah2dIlSAc0QB1iCAmQxcdu7+vOF59h4ePLlmvv+aoKzTpmOd75enxS8gxj73NbQsH74OwwDcTtjJsY\/jGsWeF2Bn62S0ChIigqmgSqnPQH2fSh6c033+HfHbvMLazBs6\/rGhTZlFqHq0u+d75gmfrFR\/ffEyjJ2w9UeiRSln5jqsHqxlE7\/hg2KrgeyN284D42rHribgbSFc78iv7BnALpmvl6K8JuV2zeznQfNBTvXUfbWtEMtfvBnko2Ljh0y8+5zcnP+XkvGJVNryonOtPrqgeNkRArit8PLDSF3w7EQNgc6L4OrAXE7Ebif1IhJPlVW4c3LLLFctSM3xaKN9swb9Fcqa6uEDqCrLgyxZNHT988oCPnnyF3I7Y04p7R8rZw\/f58vwlviuUYvid4bsCATEK4YFvA3tRsNVErAdivYfdNMtDZG6ZKFQ5s6xq8GDdj6BC\/Q7wy2OaH13OTFU1uqiBTJQRbQLvJ\/b\/XIGPhENYEDuIaRYtDt77bBsWxN7xreHPB\/zZFu4GohhqQRKR34mAKqQsFJz9WDCf80o3gtyO+JkhtaANs9tFgARSgy4r0ukCSQU08P0IGLE3oi\/4\/jBVdxN2M+I3A347zqz0ZbaB4oQdwlUlkWXWkIcRQK6F0KAUJ74S9M9rpM1IVlIYkgtp2RFlTn1tanRZ4\/2EZCEGA43ZMmJO8nBAAypgcMJmEPghaYJZ1HJI7lIABNFAMugicAe2wOfOeLFClw3aZGjBtk5aVERxCJujsU5om7E7A7dDDgIWiMwtwwKKHXakA9uHrSxHBCVsPgSoKqD4GPOO1AUsIHqIj0aG+2u0bcjnC0TAh5gzJhRM0LrG24K2gg2BJMF9dmjv47VTR5lBiR+MlxlUnn\/eBHNg2BUZYhZiciIFbKH8bcN0voScSYsKaUCbBFKwu4Kt9kiroDrHR5pZ8LtC7IIYHHYGQyEmP7D05vrxRv0fXCCiRAiYIAHhjlsQV4Xx8XaeNFESgk0FyQaSCRfsZgRRIBHms\/gbiBHoD9oZHSab23domTj8DzmtyTndp0MuAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/micrus_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.5\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EJak du\u017cy musi by\u0107 framework, aby umo\u017cliwia\u0142 szybkie, \u0142atwe i wygodne tworzenie \u0142adnie uporz\u0105dkowanych stron MVC? Aby by\u0142 \u0142atwo rozszerzalny i konfigurowalny?\u003C\/p\u003E\n\u003Cp\u003ENo w\u0142a\u015bnie wcale nie tak du\u017cy. Wypr\u00f3buj Micrusa!  Jego celem jest bycie tak ma\u0142ym, jak to tylko mo\u017cliwe, lecz oferowa\u0107 wszytkie najwa\u017cniejsze funkcjonalno\u015bci:\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022535\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGCUlEQVRIiU2Wy45dVxGGv6q19u2cvuC2u9NJhIkcCRSQAhKXF2DChAED3odX4Rl4A2CAxEWKlBBQiBOTqON0226f0+eyL6uqGOxj45L2YA\/W0r\/++uv\/S4DgUKKKdDXy1jH66BQi8PVA7Lakmx5dQULIIgy+R0RINJRfnOGdQwpw8PUAvRBjgSggExQHC\/S\/Ai5EBCICQEQQ4UQE+TUYAV0oehLYuMKvB\/SsI192lG9BXwgqUIkyeSEIQIhHS+qfLyk3e8KE6A3JDb4OxBNIS+wK4T2yBG0DGQJBEFE8HPeCWQBvAIpWMAIfHGol1oXYbLD6jvxOIl9m2h00wKqPw8tAukS+l4khE0XwBJ4SqYEoQuwnZKEQCT3N6EVG\/3gLIYDg4QdCBNAZkHSJyEAWIoNk5kvqhAC2Ekwm2p+1NI8z9s2AiOAXNeUE8mhIoyCQTjJ6lMCdGA0fBUkJ0QqpE\/J2xv5zBF+v8XDiAOhVZWQGQpORNkGtSJeQCkggR3mWmSurXWH\/CGTI+LogHx4RJbDbCW0zcpQAATPAASWiQTSBClILooL+6pTp93t0mnAgwl5rSaTOwf0WuoS0h29ZoccZaSCGQrihywQl8MFInRKPd\/DeKVIV6veWSFeRjjIkxfsRLEBnXNoK0iQkKZIzoAz\/2FL+dEO53mOr4bWoRe8tg+8ukEVGjzJ6kmZmMkgF2iraKbYx8CAmRxqhejtDmQgXdFEjbYO2EIMR4WiV53tUkLZC64RUig\/gm6C8HPHNRPQT9mTD9NmW8q87siwFvWzQ4wrtBO0CaQWpBEnz+Ekj5IsGUKarEXtpMMG7lxdUtzVX4wvMChDE3tB7NZIFyRldNogKYeA7JyaIcX4cEfgeLHXI+xX6dCTrsCXdTsjJEelsSXW+RLqM5ABxKDOV+IRo0H2wpP\/3yPjFlt+e\/5o7u+N29xy6gr5sqaj5g\/0Fe5CJKaNFMRuJsRBjzMD6iegN3zs+ODEEsVckZUSSRKqEtBDkoZJ\/sqT6wX2ah2dIlSAc0QB1iCAmQxcdu7+vOF59h4ePLlmvv+aoKzTpmOd75enxS8gxj73NbQsH74OwwDcTtjJsY\/jGsWeF2Bn62S0ChIigqmgSqnPQH2fSh6c033+HfHbvMLazBs6\/rGhTZlFqHq0u+d75gmfrFR\/ffEyjJ2w9UeiRSln5jqsHqxlE7\/hg2KrgeyN284D42rHribgbSFc78iv7BnALpmvl6K8JuV2zeznQfNBTvXUfbWtEMtfvBnko2Ljh0y8+5zcnP+XkvGJVNryonOtPrqgeNkRArit8PLDSF3w7EQNgc6L4OrAXE7Ebif1IhJPlVW4c3LLLFctSM3xaKN9swb9Fcqa6uEDqCrLgyxZNHT988oCPnnyF3I7Y04p7R8rZw\/f58vwlviuUYvid4bsCATEK4YFvA3tRsNVErAdivYfdNMtDZG6ZKFQ5s6xq8GDdj6BC\/Q7wy2OaH13OTFU1uqiBTJQRbQLvJ\/b\/XIGPhENYEDuIaRYtDt77bBsWxN7xreHPB\/zZFu4GohhqQRKR34mAKqQsFJz9WDCf80o3gtyO+JkhtaANs9tFgARSgy4r0ukCSQU08P0IGLE3oi\/4\/jBVdxN2M+I3A347zqz0ZbaB4oQdwlUlkWXWkIcRQK6F0KAUJ74S9M9rpM1IVlIYkgtp2RFlTn1tanRZ4\/2EZCEGA43ZMmJO8nBAAypgcMJmEPghaYJZ1HJI7lIABNFAMugicAe2wOfOeLFClw3aZGjBtk5aVERxCJujsU5om7E7A7dDDgIWiMwtwwKKHXakA9uHrSxHBCVsPgSoKqD4GPOO1AUsIHqIj0aG+2u0bcjnC0TAh5gzJhRM0LrG24K2gg2BJMF9dmjv47VTR5lBiR+MlxlUnn\/eBHNg2BUZYhZiciIFbKH8bcN0voScSYsKaUCbBFKwu4Kt9kiroDrHR5pZ8LtC7IIYHHYGQyEmP7D05vrxRv0fXCCiRAiYIAHhjlsQV4Xx8XaeNFESgk0FyQaSCRfsZgRRIBHms\/gbiBHoD9oZHSab23domTj8DzmtyTndp0MuAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/micrus_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022535\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EJak du\u017cy musi by\u0107 framework, aby umo\u017cliwia\u0142 szybkie, \u0142atwe i wygodne tworzenie \u0142adnie uporz\u0105dkowanych stron MVC? Aby by\u0142 \u0142atwo rozszerzalny i konfigurowalny?\u003C\/p\u003E\n\u003Cp\u003ENo w\u0142a\u015bnie wcale nie tak du\u017cy. Wypr\u00f3buj Micrusa!  Jego celem jest bycie tak ma\u0142ym, jak to tylko mo\u017cliwe, lecz oferowa\u0107 wszytkie najwa\u017cniejsze funkcjonalno\u015bci:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EZorientowany obiektowo\u003C\/li\u003E\n\u003Cli\u003EPrzejrzysta struktura MVC\u003C\/li\u003E\n\u003Cli\u003EKontener DI\u003C\/li\u003E\n\u003Cli\u003EEvent dispatching\u003C\/li\u003E\n\u003Cli\u003EAgnotyczny wobec ORM, wsparcie dla Doctrine i RedBean\u003C\/li\u003E\n\u003Cli\u003EREST-ful routing\u003C\/li\u003E\n\u003Cli\u003EGenerator CRUD\u003C\/li\u003E\n\u003Cli\u003EAgnostyczny wobec silnik\u00f3w szablon\u00f3w, wsparcie dla Twiga i czystego PHP\u003C\/li\u003E\n\u003Cli\u003EAutentykacja i autoryzacja\u003C\/li\u003E\n\u003Cli\u003E\u0141atwa konfiguracja w YAML-u\u003C\/li\u003E\n\u003Cli\u003ETaki konsolowe dzi\u0119ki Symfony Console\u003C\/li\u003E\n\u003Cli\u003EFormularze\u003C\/li\u003E\n\u003Cli\u003ELocalizacja\u003C\/li\u003E\n\u003Cli\u003ELogowanie z u\u017cyciem Monolog (zgodne z PSR-3)\u003C\/li\u003E\n\u003Cli\u003ECaching (zgodne z PSR-6)\u003C\/li\u003E\n\u003Cli\u003EKomunikaty flash\u003C\/li\u003E\n\u003C\/ul\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["framework","full-stack","micrus","php","plugins","programowanie"],"hasMore":true,"image":"https:\/\/avris.it\/image\/micrus_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/micrus_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.75\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EJak du\u017cy musi by\u0107 framework, aby umo\u017cliwia\u0142 szybkie, \u0142atwe i wygodne tworzenie \u0142adnie uporz\u0105dkowanych stron MVC? Aby by\u0142 \u0142atwo rozszerzalny i konfigurowalny?\u003C\/p\u003E\n\u003Cp\u003ENo w\u0142a\u015bnie wcale nie tak du\u017cy. Wypr\u00f3buj Micrusa!  Jego celem jest bycie tak ma\u0142ym, jak to tylko mo\u017cliwe, lecz oferowa\u0107 wszytkie najwa\u017cniejsze funkcjonalno\u015bci:\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/micrus_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/micrus_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.75\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EJak du\u017cy musi by\u0107 framework, aby umo\u017cliwia\u0142 szybkie, \u0142atwe i wygodne tworzenie \u0142adnie uporz\u0105dkowanych stron MVC? Aby by\u0142 \u0142atwo rozszerzalny i konfigurowalny?\u003C\/p\u003E\n\u003Cp\u003ENo w\u0142a\u015bnie wcale nie tak du\u017cy. Wypr\u00f3buj Micrusa!  Jego celem jest bycie tak ma\u0142ym, jak to tylko mo\u017cliwe, lecz oferowa\u0107 wszytkie najwa\u017cniejsze funkcjonalno\u015bci:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EZorientowany obiektowo\u003C\/li\u003E\n\u003Cli\u003EPrzejrzysta struktura MVC\u003C\/li\u003E\n\u003Cli\u003EKontener DI\u003C\/li\u003E\n\u003Cli\u003EEvent dispatching\u003C\/li\u003E\n\u003Cli\u003EAgnotyczny wobec ORM, wsparcie dla Doctrine i RedBean\u003C\/li\u003E\n\u003Cli\u003EREST-ful routing\u003C\/li\u003E\n\u003Cli\u003EGenerator CRUD\u003C\/li\u003E\n\u003Cli\u003EAgnostyczny wobec silnik\u00f3w szablon\u00f3w, wsparcie dla Twiga i czystego PHP\u003C\/li\u003E\n\u003Cli\u003EAutentykacja i autoryzacja\u003C\/li\u003E\n\u003Cli\u003E\u0141atwa konfiguracja w YAML-u\u003C\/li\u003E\n\u003Cli\u003ETaki konsolowe dzi\u0119ki Symfony Console\u003C\/li\u003E\n\u003Cli\u003EFormularze\u003C\/li\u003E\n\u003Cli\u003ELocalizacja\u003C\/li\u003E\n\u003Cli\u003ELogowanie z u\u017cyciem Monolog (zgodne z PSR-3)\u003C\/li\u003E\n\u003Cli\u003ECaching (zgodne z PSR-6)\u003C\/li\u003E\n\u003Cli\u003EKomunikaty flash\u003C\/li\u003E\n\u003C\/ul\u003E","words":118,"readTime":null,"lang":"pl"}}},"projects\/dibsy-call-dibs-on-test-servers":{"key":"projects\/dibsy-call-dibs-on-test-servers","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:44:08+02:00","publishedAt":"2017-03-09T18:10:00+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/dibsy.avris.it","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Dibsy","displayUrl":null}],"category":"projects","subcategory":null,"slug":"dibsy-call-dibs-on-test-servers"},"content":{"en":{"slug":"dibsy-call-dibs-on-test-servers","title":"Dibsy \u2022 Call dibs on test servers!","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.99007444169\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFTUlEQVRIib2WW2wc1RnHf3NmZndmZ3Z39jKOTZx1HBtEgChpGhUkRNuIWxEICQnxgvpQcZEiHoN4ASEhbpVQHxDQqJCiAhJvKFLUh6goFbQWIQWa5iYShyhOYnvtXdu7Xq\/3Mjszpw\/ubHZrV9Cq8L3M\/8x85\/v++p\/\/6DtIKWUYhjKKc+fOyUplSc7MzMhOp9N935sT4d7nf4Pr9br0PE9+PvEXGQRBX44WhiEAUkoAlpeXGRoaIggC\/CBACEEUUU4v3ujdt+EgCAjDENnTO8pRPM+7lv1vm3+oUBSlizUpJYqidIlomsbk+fNsKRQQQhCGIWEYkkgkUFW1Szra07v3f8Ge5\/WJoG3EstFsUpqfR9V1Ws0muh5jaCjWJfR9haIoaBGRXpaDDgSrl9HkKjEtTsdPUCs3mZy6yldfHqVTrDJXrDN6xy6EKrDsJGJyjNmvS+uajN65hYN\/OrQhgT+8+xK6rvYfWS+7KCZe38\/mEZUQiS7iLM63aYcSOXIXk59+hu5YFFtNLh47Q9qzSW8aY\/iSw6VjRXY8eD2nD1\/o1optTnDoj18B8KOd46w2WkxemAbgnU6Arvervs5DAFdqcS5\/WWXbtgTzxXkGC2m8mVni4x22511OFuKM13XsWZ0pf5WVpVnSqTRuts3efbcyN1Ht1kqYNsJ0Afjp3tuZm6\/wzXQ7UmHd6WyokEj4KMLg9NkSO7YPYLZ9On5Iq2Pz9Q6bxeYSNU3SWpzGCASOnSdtOrSyIbqm42bzRC1M00Y18mt1NRtFD7pruNYz4rChhzI0aKgmm4eH+eIfU5iGSj7QcP0Gwdk25uxFpmshSSfGfFBjwPJJJiw8JwQFfvbiLooTVUrHqxiGiTCyAARKHCmurSOFvtVDx8oGjUqRhWIREYTU2yFBLMGjN9eY+tsZ4nmDgqiglDvk0wa18zOcvOk4ZWOBI7\/6CN8P1gpJ2F3bw9imBRqNJr89cKJPEWQAfAcP3fPAvVSWllBCn1CC0DWshEl2fA\/Lt2xltd1CqDr4bcIQ0qkc6WWDqgnDN7h9DQo7h7h79e41dt1jkoBCPBZbdzobKvT3T97E91rkNm2j1axRqxQBsI39vDx5iqxh4RgWV2oL+GHIVmeAwuEpJv76GVtHCwC0W22mZ4rs27efD983GByyKJcaKEIhkzEolxo8\/2yIaV4bTf\/RQ\/lcmszATu566DnarRWOHz3IlW8+RyYt1Hyet37xOItegxvTLvceeh01lUFQwjIGePKJfTz8yP289usDvPP2B2jCJGPlePrpW5n4dAY7qTO8Jcnv3jiFwnf0UC6fIu3YJOwMppXCyaRoummqSRvNdVHicZKqQlMVaK6LajkILFoNnVdeOMiP9+zi3bcPo5JeI2S7fHx4gV8+sR0zofHaC2fJ2G5fzyhE79SOcC6XxnGSeM0ylfnTjIzvxHUd4raF6rqIeJyjzTJSU1FdFzWbRVUsVCWFqiQB7V84haokyFoui1fjDA6mEDKGv2KTtdyuQlFvKeXGCl0sxqmdO8+JC+8RBh7V0hmkDDDzS4wsLvHepUNMlK5w2coj6mXCdAbLFoyOZZmZnuPZZ14llHUAAllDmCfQdZXfvHySymKT5WZnjQi7gH4Pda8fvdP+z0c+oNGo4bVbgIKmaShCYI\/t5lR1mSAIWPXa3UKZhEV+2qdcWmJlpU4Q\/fbAT27bzdxVH1VV8No+nud3vz321J0oQvaJse4+FE30Tz4+woe\/P0DcMHCyOe5\/6BFuu+PnCCH+79ePvimxkYcA9t5zH4Wx66mvNrhuZBQnm+P7il4P\/RPX9wASIcw\/nwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.99007444169\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EDibsy helps team members to coordinate, which qa\/staging server is currently being used by whom, and which ones are free to deploy to.\u003C\/p\u003E\n\u003Cp\u003EDeveloped as a 20% project @ \u003Ca href=\u0022https:\/\/www.rocket-internet.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Rocket Internet SE\u003C\/a\u003E\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022535.98014888337\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFTUlEQVRIib2WW2wc1RnHf3NmZndmZ3Z39jKOTZx1HBtEgChpGhUkRNuIWxEICQnxgvpQcZEiHoN4ASEhbpVQHxDQqJCiAhJvKFLUh6goFbQWIQWa5iYShyhOYnvtXdu7Xq\/3Mjszpw\/ubHZrV9Cq8L3M\/8x85\/v++p\/\/6DtIKWUYhjKKc+fOyUplSc7MzMhOp9N935sT4d7nf4Pr9br0PE9+PvEXGQRBX44WhiEAUkoAlpeXGRoaIggC\/CBACEEUUU4v3ujdt+EgCAjDENnTO8pRPM+7lv1vm3+oUBSlizUpJYqidIlomsbk+fNsKRQQQhCGIWEYkkgkUFW1Szra07v3f8Ge5\/WJoG3EstFsUpqfR9V1Ws0muh5jaCjWJfR9haIoaBGRXpaDDgSrl9HkKjEtTsdPUCs3mZy6yldfHqVTrDJXrDN6xy6EKrDsJGJyjNmvS+uajN65hYN\/OrQhgT+8+xK6rvYfWS+7KCZe38\/mEZUQiS7iLM63aYcSOXIXk59+hu5YFFtNLh47Q9qzSW8aY\/iSw6VjRXY8eD2nD1\/o1optTnDoj18B8KOd46w2WkxemAbgnU6Arvervs5DAFdqcS5\/WWXbtgTzxXkGC2m8mVni4x22511OFuKM13XsWZ0pf5WVpVnSqTRuts3efbcyN1Ht1kqYNsJ0Afjp3tuZm6\/wzXQ7UmHd6WyokEj4KMLg9NkSO7YPYLZ9On5Iq2Pz9Q6bxeYSNU3SWpzGCASOnSdtOrSyIbqm42bzRC1M00Y18mt1NRtFD7pruNYz4rChhzI0aKgmm4eH+eIfU5iGSj7QcP0Gwdk25uxFpmshSSfGfFBjwPJJJiw8JwQFfvbiLooTVUrHqxiGiTCyAARKHCmurSOFvtVDx8oGjUqRhWIREYTU2yFBLMGjN9eY+tsZ4nmDgqiglDvk0wa18zOcvOk4ZWOBI7\/6CN8P1gpJ2F3bw9imBRqNJr89cKJPEWQAfAcP3fPAvVSWllBCn1CC0DWshEl2fA\/Lt2xltd1CqDr4bcIQ0qkc6WWDqgnDN7h9DQo7h7h79e41dt1jkoBCPBZbdzobKvT3T97E91rkNm2j1axRqxQBsI39vDx5iqxh4RgWV2oL+GHIVmeAwuEpJv76GVtHCwC0W22mZ4rs27efD983GByyKJcaKEIhkzEolxo8\/2yIaV4bTf\/RQ\/lcmszATu566DnarRWOHz3IlW8+RyYt1Hyet37xOItegxvTLvceeh01lUFQwjIGePKJfTz8yP289usDvPP2B2jCJGPlePrpW5n4dAY7qTO8Jcnv3jiFwnf0UC6fIu3YJOwMppXCyaRoummqSRvNdVHicZKqQlMVaK6LajkILFoNnVdeOMiP9+zi3bcPo5JeI2S7fHx4gV8+sR0zofHaC2fJ2G5fzyhE79SOcC6XxnGSeM0ylfnTjIzvxHUd4raF6rqIeJyjzTJSU1FdFzWbRVUsVCWFqiQB7V84haokyFoui1fjDA6mEDKGv2KTtdyuQlFvKeXGCl0sxqmdO8+JC+8RBh7V0hmkDDDzS4wsLvHepUNMlK5w2coj6mXCdAbLFoyOZZmZnuPZZ14llHUAAllDmCfQdZXfvHySymKT5WZnjQi7gH4Pda8fvdP+z0c+oNGo4bVbgIKmaShCYI\/t5lR1mSAIWPXa3UKZhEV+2qdcWmJlpU4Q\/fbAT27bzdxVH1VV8No+nud3vz321J0oQvaJse4+FE30Tz4+woe\/P0DcMHCyOe5\/6BFuu+PnCCH+79ePvimxkYcA9t5zH4Wx66mvNrhuZBQnm+P7il4P\/RPX9wASIcw\/nwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022535.98014888337\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EDibsy helps team members to coordinate, which qa\/staging server is currently being used by whom, and which ones are free to deploy to.\u003C\/p\u003E\n\u003Cp\u003EDeveloped as a 20% project @ \u003Ca href=\u0022https:\/\/www.rocket-internet.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Rocket Internet SE\u003C\/a\u003E\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["cooperation","coordination","dibs","micrus","php","server","social login","websockets"],"hasMore":false,"image":"https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.99503722084\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EDibsy helps team members to coordinate, which qa\/staging server is currently being used by whom, and which ones are free to deploy to.\u003C\/p\u003E\n\u003Cp\u003EDeveloped as a 20% project @ \u003Ca href=\u0022https:\/\/www.rocket-internet.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Rocket Internet SE\u003C\/a\u003E\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/dibsy-call-dibs-on-test-servers_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.99503722084\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EDibsy helps team members to coordinate, which qa\/staging server is currently being used by whom, and which ones are free to deploy to.\u003C\/p\u003E\n\u003Cp\u003EDeveloped as a 20% project @ \u003Ca href=\u0022https:\/\/www.rocket-internet.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Rocket Internet SE\u003C\/a\u003E\u003C\/p\u003E","words":31,"readTime":null,"lang":"en"}}},"projects\/picco-golfy-php-framework":{"key":"projects\/picco-golfy-php-framework","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:56+02:00","publishedAt":"2016-07-13T21:16:00+02:00","group":"micrus","links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/picco.avris.it\/","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Picco","displayUrl":null}],"category":"projects","subcategory":null,"slug":"picco-golfy-php-framework"},"content":{"en":{"slug":"picco-golfy-php-framework","title":"Picco \u2013 Golfy PHP framework","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/picco-golfy-php-framework_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022274.28571428571\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHOklEQVRIiU2Vy2+dVxXFf\/uc8z3uw\/f6XtvXdh5OIE2TtrShDyraKqESA6RKVMyZIP4B5ogRY4ZM+BeQGDJACCQUQaXSVrQpCbiJ2sauE79i+9r33u\/7zmMzuB4w2LMjnbXXWnstKTZ+q7Zt6by0gh316T6\/ytlfHnD29VeY3GC7XWh1MZ0WJreUOTR1hRGBIoNWAWWOEWG1ZSliYGvrCB8TKo6ERb2CT5SlJcss9bhm8vUe9bN9iA0AKjlIhiMvyEYt7EIBvqH57xMWbi7jn+0TYwPWYEqHaWVIbvHqsUWGAFJk0M6hzEGEZ7VHpg0qgnEWzRxIhrqEZIm85bDOEOqIyS3GGcCRogcSAE6uX4bpEWoEaw1OEuZ4il3sISJoqDG5Q1oZJjeYBCbIOSAHZQadHASSJqgt65myPU3YpRUkK4mTU9K0ZnzmkZCQJhBjoDMYQAOz00N8iACYfLRMEwzhpMaY+UfTnWNi8Ej09F++Qb7YnbNUZhTtnEG\/xGaOvMy5OGrT6uXYToZtZ5jCceelEbZsMfj+LTbefx03WMKUGVI4kjF4nxBnSSlA8mhsEFVAcfXd\/+BeHTE7OcMfHmJJpPoUYxryjaucPfqK9XdvcTypiJMZGhQfwWSOXq\/g6qiDRNirIxoS6aTiB6+s8\/sHp6TGM50FJBdM4XBGyLs5yzeH+OM+s50DmmkkftMwPRwDigHBf7pFamakUQ9Z7CFWMEbQsyOGz22w\/2ALjCUb9bGFY7lfYl3GJAqb08CRM5h2hm1luLrm7dcuYSQx+\/IhJ5\/cB50hheXlG0v86N0NynZGnluuPL+KM4Z8oQvoXDJFEQGOqvmsL+IWu\/SXFjFNRZEL+XKP1FTQ1Jh+i71ZYnEhZ2lQcq1juVnPyDuOfq\/gjcs92q0MYw0iERGPyQTJLZ89HvPXj3ex1nD1Uo+6iaxfH5J3C0TMXDI0gAHJLIRI3NwhW1sBPcM4x\/gs0Hv924x3DzBOiaczOqq8UES+dyHjzesDPnx8zHG3wArc6Q4AEGsxmUVzi4pigjLsZAxajpPdCYeHY\/o9x3TS0Fob0BnXnG49w4kGEMAZcAaRRHi6hY76rF27xJPHTxh\/3HDnrUtc0cDbbwx5+9YFegslqoqI8JsH+6xeKJiOJ9x+YWVOvTEYZ0iZmV9kSFwedRCF5qyhPnNYASNgAFfmc4ZUAyIZZr2LvbCAaTw8ntAbH\/JOp8\/tn7\/GO6+ssbrSnW8ugupcb4Dj8YztIqPjDK2TKS9cu0lVeUQEsWY+ScEato9qhqVl\/2DGdOuIKxe7iII2gdnO03PJiMigi6x0kU5GvtFFCNRb2zzcGfPJH+7xqz9+ga50uXO5Tz6r2Nya8N6NIb\/46S3+ubnH8vURT6zhlX6JCGS55Zc\/vISUDjWCKpAUjcrJyYy\/hTEfHR6zfXCIhJqTgwOamQdZwAlpnrR5jpiMi2td9nbG1EZ4NvX43gK+20EXe2wO+nRtxq4FdQ6AneMZ7dUBK2p4a6UNQOYsP3v\/RUSEqg58+PkT7m4ecvfhMZ9uT4mhoBwOkMN9mukUjR40IaI4UJgmcAVYw9bnB7B1QKgqdnYTkgytpT7GGXZ2T1kpzDzEzmW7f9KQAuw+2uX2q6ukpNx78JS\/\/3uPfzw55aOxp+4toMmQTBtZzpHDM5wzBFU0RVLygKAoDhGQHH3mSAnC4QRzOsGkRPAeGxK1T5igIHC0P+XSc2sYO+XR4yP+1BjccUUxqfn1nx\/x2eYZTeWYJk8Y5eh6DxsgVvNqKHPLRAynkwadTQj1KaoRVM6DUQSKDix00JCjlc57LkWIEU2JGBMx6dwHIXLwaJ+ysNy9t4uUBamK+AtDPmh3mRTteXkChIRGRTn3kRiqKIjNWBwt0l8aYhA0xblkCgYxUBRwUaCdoWIgJTRGUMW1O7hegSbQqNiktGJFM6354NEx2kRSFfCTQG4dqfaE6ex8oYQmRZMCBjUZuIKlC0PeuX2NwcqIxeGF+VvS\/zHkLPSBzIAKGhPEiOAQsaw\/P5yfcYIrvYyffHfAqF9yf6\/CuYzkIdWRXqtEqwatG8LplOHGOhoBLNgMydtIawHb6fLwaUWKc9ZJ\/tyTigGdM7IPqVE0JTQmNHhidUqcTNn+dI\/URIhK1wg31rp89q+vmMwCnX6Pi99aIzXK7uM9UlWT6prUeMZ7x9iyAJtD0UW6bejkHBx4jnZOOTt6Rl563vzxe9y48yYiwvx2Q4Pugao\/BxPRGFCN+JMjzEEf6XYIKbF5eMrvvtxn7+EBfnWJ8TfHjK0j1onSlUxrj3iPW+yhRBY2hpwFi6aIMZHxUyXUgaf3t4k7X3DjO1cRsfTXV8jaOzhUwVfoeIK6iPo4BxQ8akC9J\/mICUqSxPikptof42yLMKsJO\/u4XkmxukYrKJV9grEVREjRUAfIRgaTCyfbEdOA8TW+rkih5vH9e1x6UTgZT2gmM\/4H\/a7DbmCi0BgAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/picco-golfy-php-framework_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022274.28571428571\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003ECode golf\u003C\/strong\u003E is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm. \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Code_golf\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E [source]\u003C\/a\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EPicco\u003C\/strong\u003E is a tiny PHP web framework that only takes \u003Cstrong\u003E~2,5 kB\u003C\/strong\u003E of space and has no dependencies on other libraries, while still providing quite a lot of features, being extensible and reasonably easy to use.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/picco-golfy-php-framework_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022700\u0022 height=\u0022400\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHOklEQVRIiU2Vy2+dVxXFf\/uc8z3uw\/f6XtvXdh5OIE2TtrShDyraKqESA6RKVMyZIP4B5ogRY4ZM+BeQGDJACCQUQaXSVrQpCbiJ2sauE79i+9r33u\/7zmMzuB4w2LMjnbXXWnstKTZ+q7Zt6by0gh316T6\/ytlfHnD29VeY3GC7XWh1MZ0WJreUOTR1hRGBIoNWAWWOEWG1ZSliYGvrCB8TKo6ERb2CT5SlJcss9bhm8vUe9bN9iA0AKjlIhiMvyEYt7EIBvqH57xMWbi7jn+0TYwPWYEqHaWVIbvHqsUWGAFJk0M6hzEGEZ7VHpg0qgnEWzRxIhrqEZIm85bDOEOqIyS3GGcCRogcSAE6uX4bpEWoEaw1OEuZ4il3sISJoqDG5Q1oZJjeYBCbIOSAHZQadHASSJqgt65myPU3YpRUkK4mTU9K0ZnzmkZCQJhBjoDMYQAOz00N8iACYfLRMEwzhpMaY+UfTnWNi8Ej09F++Qb7YnbNUZhTtnEG\/xGaOvMy5OGrT6uXYToZtZ5jCceelEbZsMfj+LTbefx03WMKUGVI4kjF4nxBnSSlA8mhsEFVAcfXd\/+BeHTE7OcMfHmJJpPoUYxryjaucPfqK9XdvcTypiJMZGhQfwWSOXq\/g6qiDRNirIxoS6aTiB6+s8\/sHp6TGM50FJBdM4XBGyLs5yzeH+OM+s50DmmkkftMwPRwDigHBf7pFamakUQ9Z7CFWMEbQsyOGz22w\/2ALjCUb9bGFY7lfYl3GJAqb08CRM5h2hm1luLrm7dcuYSQx+\/IhJ5\/cB50hheXlG0v86N0NynZGnluuPL+KM4Z8oQvoXDJFEQGOqvmsL+IWu\/SXFjFNRZEL+XKP1FTQ1Jh+i71ZYnEhZ2lQcq1juVnPyDuOfq\/gjcs92q0MYw0iERGPyQTJLZ89HvPXj3ex1nD1Uo+6iaxfH5J3C0TMXDI0gAHJLIRI3NwhW1sBPcM4x\/gs0Hv924x3DzBOiaczOqq8UES+dyHjzesDPnx8zHG3wArc6Q4AEGsxmUVzi4pigjLsZAxajpPdCYeHY\/o9x3TS0Fob0BnXnG49w4kGEMAZcAaRRHi6hY76rF27xJPHTxh\/3HDnrUtc0cDbbwx5+9YFegslqoqI8JsH+6xeKJiOJ9x+YWVOvTEYZ0iZmV9kSFwedRCF5qyhPnNYASNgAFfmc4ZUAyIZZr2LvbCAaTw8ntAbH\/JOp8\/tn7\/GO6+ssbrSnW8ugupcb4Dj8YztIqPjDK2TKS9cu0lVeUQEsWY+ScEato9qhqVl\/2DGdOuIKxe7iII2gdnO03PJiMigi6x0kU5GvtFFCNRb2zzcGfPJH+7xqz9+ga50uXO5Tz6r2Nya8N6NIb\/46S3+ubnH8vURT6zhlX6JCGS55Zc\/vISUDjWCKpAUjcrJyYy\/hTEfHR6zfXCIhJqTgwOamQdZwAlpnrR5jpiMi2td9nbG1EZ4NvX43gK+20EXe2wO+nRtxq4FdQ6AneMZ7dUBK2p4a6UNQOYsP3v\/RUSEqg58+PkT7m4ecvfhMZ9uT4mhoBwOkMN9mukUjR40IaI4UJgmcAVYw9bnB7B1QKgqdnYTkgytpT7GGXZ2T1kpzDzEzmW7f9KQAuw+2uX2q6ukpNx78JS\/\/3uPfzw55aOxp+4toMmQTBtZzpHDM5wzBFU0RVLygKAoDhGQHH3mSAnC4QRzOsGkRPAeGxK1T5igIHC0P+XSc2sYO+XR4yP+1BjccUUxqfn1nx\/x2eYZTeWYJk8Y5eh6DxsgVvNqKHPLRAynkwadTQj1KaoRVM6DUQSKDix00JCjlc57LkWIEU2JGBMx6dwHIXLwaJ+ysNy9t4uUBamK+AtDPmh3mRTteXkChIRGRTn3kRiqKIjNWBwt0l8aYhA0xblkCgYxUBRwUaCdoWIgJTRGUMW1O7hegSbQqNiktGJFM6354NEx2kRSFfCTQG4dqfaE6ex8oYQmRZMCBjUZuIKlC0PeuX2NwcqIxeGF+VvS\/zHkLPSBzIAKGhPEiOAQsaw\/P5yfcYIrvYyffHfAqF9yf6\/CuYzkIdWRXqtEqwatG8LplOHGOhoBLNgMydtIawHb6fLwaUWKc9ZJ\/tyTigGdM7IPqVE0JTQmNHhidUqcTNn+dI\/URIhK1wg31rp89q+vmMwCnX6Pi99aIzXK7uM9UlWT6prUeMZ7x9iyAJtD0UW6bejkHBx4jnZOOTt6Rl563vzxe9y48yYiwvx2Q4Pugao\/BxPRGFCN+JMjzEEf6XYIKbF5eMrvvtxn7+EBfnWJ8TfHjK0j1onSlUxrj3iPW+yhRBY2hpwFi6aIMZHxUyXUgaf3t4k7X3DjO1cRsfTXV8jaOzhUwVfoeIK6iPo4BxQ8akC9J\/mICUqSxPikptof42yLMKsJO\/u4XkmxukYrKJV9grEVREjRUAfIRgaTCyfbEdOA8TW+rkih5vH9e1x6UTgZT2gmM\/4H\/a7DbmCi0BgAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/picco-golfy-php-framework_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022700\u0022 height=\u0022400\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003ECode golf\u003C\/strong\u003E is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm. \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Code_golf\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E [source]\u003C\/a\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EPicco\u003C\/strong\u003E is a tiny PHP web framework that only takes \u003Cstrong\u003E~2,5 kB\u003C\/strong\u003E of space and has no dependencies on other libraries, while still providing quite a lot of features, being extensible and reasonably easy to use.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["code golf","framework","microframework","php","programming"],"hasMore":false,"image":"https:\/\/avris.it\/image\/picco-golfy-php-framework_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/picco-golfy-php-framework_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/picco-golfy-php-framework_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022137.14285714286\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003ECode golf\u003C\/strong\u003E is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm. \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Code_golf\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E [source]\u003C\/a\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EPicco\u003C\/strong\u003E is a tiny PHP web framework that only takes \u003Cstrong\u003E~2,5 kB\u003C\/strong\u003E of space and has no dependencies on other libraries, while still providing quite a lot of features, being extensible and reasonably easy to use.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/picco-golfy-php-framework_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/picco-golfy-php-framework_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022137.14285714286\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003ECode golf\u003C\/strong\u003E is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm. \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/Code_golf\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E [source]\u003C\/a\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EPicco\u003C\/strong\u003E is a tiny PHP web framework that only takes \u003Cstrong\u003E~2,5 kB\u003C\/strong\u003E of space and has no dependencies on other libraries, while still providing quite a lot of features, being extensible and reasonably easy to use.\u003C\/p\u003E","words":62,"readTime":null,"lang":"en"}}},"projects\/qc-quite-concise-programming-language":{"key":"projects\/qc-quite-concise-programming-language","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:45+02:00","publishedAt":"2015-12-23T00:53:00+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/qc.avris.it\/","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/QC","displayUrl":null}],"category":"projects","subcategory":null,"slug":"qc-quite-concise-programming-language"},"content":{"en":{"slug":"qc-quite-concise-programming-language","title":"QC \u2022 Quite Concise Programming Language","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/qc-quite-concise-programming-language_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022270.76923076923\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHaUlEQVRIiS2Vy64cZxVG13+pS1d1dZ+++PjENk5iOY6IcBKJSZiAEBEDJCaIB+AF4Bky4nV4AJCQEINMEhScxIljfD+XPqfvXd1V9V8ZnHzDNdmDvdf+xIOf\/S7KZIBIh3jnSNIeKu0T0oLx5AgrUwyaKDXDUZ+fvH\/Cn351j\/EgRyhJ4yJFqtBSIJXE+kjrAtYFAIwLNDZwMA7jA\/9+vuTxl09Zvb6kXe+w8zn4ltgswFyhPv3Dnz+z6Zh9C+geqrpBTEumb53wyS8f8mYPo35OJgUizThsWgZHBSejgl6myRNNABof2Znr4SGCC7A3gV3naU3g0bzh2abjpMq49ZMpk\/u3cbdu0lYTOJogygkkA8Rf\/vq3OF9s2TaO\/bbjeNpncmNMOqzQVUEdQWlJKRW71nE536MTRbdr+PDjO3x0e0gv0wgpCFHQhUDjAsZH9p3HW88X5zsendfcO+5xp58yzBSrxhJiYNUYVruOs7MVh0eP0aIq+PDBHZoQeXvS5\/a04mADWisSrYgIEiVxPtL5yHpv+O6ipl43nL7e8uibGTLTHN86YrE3FABlyovTLX6x4+i4Ii8SPj7pcVSmrK3n4mCZrVuEc0xKTXcwZIlG3L2NPqtbyhB4d1rRL3K01kx6iizV5KkmxIjzcDAeaT2BlPduVdTTAuMCb5YHNnvLctNQKcH2YHj5\/YxeT1P0FKE1vDlfU3rLf00gFjlOKVSe4q7WXC4X9MuEbFgiBejffvIeSmmqXkrVy0ArvJR4ITi4QKYknkiiJQiBDVBl0E81tfH0E0VPCX5YHvDG08bIw\/tj9KGl2dTcGaT849srTJ6QIvDNAYQiCInf7fH1nuXpEsweoRT6+KhkWGRordBakSca4yNaCfqZRgmBD5EQIokS1J2mNR4fIqebjidXV3xzeoXqDeg1az545x618ZxeSl4uWr56dAVKo6SiWWxI7Z7D9kAUAqklUimCc8QAuD161TlG\/ZxUSWyI5FpQZZpeqpBSEIEQr\/V1PpL\/yLeNo58IzON\/8bAoefb1K8Jb7\/N4ccykl1FWOfce3MTenbJc1jz955fI9QUH1xHTCqTAr3eIPEcojVLgjEG\/MykZlRlSSpRWZFoipcRGECHiQ8SFiPMRiNStx9hA3Xn21hNipLUWCZy\/WvL955+TFH2KG2P6gxJNZHW5JR0d4bIcZQ1aBryLMBwRtnO0FkjZI0SB+OL1ImZJgpKSALQuYkNEcB0bwLqA9YGD8bQ2EIkocc1PL2Zc\/O9b1Ml9frhc8fSZwbyakTiHcAaVJQzu3SXEgN3uiYcDyhl2sxnjk2N29YH9YolUikwZ9LpxZBb48VZ8hM5HXAQfIo0NbNYr3nz\/iLW7Xt84Txhnim1r2RjHpKoo7SWzbs9wdUq9X0C9I0sUrtXYHy4IR3d5+ME7tHXD4nyBM2MuX70mqyrSqsJ7T9d06CdXNWWaoJUkIggREilACFobeL3r+OZ8Q7ppmPYLrJB0xnJmHTsTuGojy90KrRNmy4a0zCmPMsgTknaDMzvSZsfVbMtXu5a86FHvG7I0g6RAtTuyVOCJGOnQf3+1YpAlVKlmmicsOserreGtMmWaJ+RK0DrNZX6f7zaO3emSNFFM5Bzn98gkZykUVSmZDnLODw6rBmxjRnQVYb9FRIfE0718zj7VxODpEAig83vq+TlCZoBAz2rL+daQJoq+EIQQMZ3n\/HSDaww6kaATmp2lO1\/inz7DbLdMTxyr5SVFohFHI4w3XJZ9dJkTREpoHHG3JtECLQTRB0y7xhw8SEXSH+JcQMkE9E2ynqDbzdC3BwnGRUIUTIuETeOotx2htSRS0i4O2OUOVx8I6y3oDJGXvHw1g2RCG0vePr7P+ZPvaN\/MkeUYqgyR9qBSDEYFRSKYn52jQkTIHYgItiVNJMSIyjO8rQneoN8tNKnSOAQekBH8pOQi0XSNIw4K4rXxyLJPWK2Jhw0UQ9RwTJJqRmHB8\/Ua0ZsQ2xa6+bWJeYke57x\/7yY3Jj109Lx5fcHF61O8a5EiR6U5g1HFdlaj0gydEBmnknGR8bK2nEVLS0QoiUwUItXEPIWiwC93iH0G\/SEizwlNQ7tc8J+zLWJwE5mkqGyK7ywST1873PKMemZJdE7dOm6fHLG6usQbT1ef4WvP1k7pT4\/J9BTFL\/74WVSSMlWYEAgIXi5b2sZhjMdzbRwICBFhHaQp0XkwFpwl2hZpDiQqkipIehm3TkY0reHmyQ2cSFDB4ro9aZ6z3x+QWLzURB\/x7RV33rpB2u+j68WGJz7w\/HJHL88ISvNgkpPekFztHRd7S2sjQUqs7xGEQK52iLIgFAVxn3H3+C6rFy+oFzNGpWM8SPB2A+2K+fmG3\/z651zMt7x49ozV14\/wXQMyASEoq4yurTCmBUBVg59+ZlEkVR\/\/4+\/xEeatI9EKlEBreV28VUo5KkiHJSJPkaM+cjSkPyz5\/acfEYsRtukYVBmLyyt6Gcxnc4z3HN864fTNFcEHbHdgND0hHU7pVWNQKTZK9nXN\/wECJSo73jElDwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/qc-quite-concise-programming-language_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022270.76923076923\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThe idea is to create a \u003Cstrong\u003Esimple\u003C\/strong\u003E (in terms of complexity, not ease of use) programming language, that will have quite a \u003Cstrong\u003Econcise\u003C\/strong\u003E \u003Cstrong\u003Esyntax\u003C\/strong\u003E, that will play with the power of \u003Cstrong\u003EUnicode\u003C\/strong\u003E and most importantly \u2013 will be \u003Cstrong\u003Efun\u003C\/strong\u003E to create ;)\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/qc-quite-concise-programming-language_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022741\u0022 height=\u0022418\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHaUlEQVRIiS2Vy64cZxVG13+pS1d1dZ+++PjENk5iOY6IcBKJSZiAEBEDJCaIB+AF4Bky4nV4AJCQEINMEhScxIljfD+XPqfvXd1V9V8ZnHzDNdmDvdf+xIOf\/S7KZIBIh3jnSNIeKu0T0oLx5AgrUwyaKDXDUZ+fvH\/Cn351j\/EgRyhJ4yJFqtBSIJXE+kjrAtYFAIwLNDZwMA7jA\/9+vuTxl09Zvb6kXe+w8zn4ltgswFyhPv3Dnz+z6Zh9C+geqrpBTEumb53wyS8f8mYPo35OJgUizThsWgZHBSejgl6myRNNABof2Znr4SGCC7A3gV3naU3g0bzh2abjpMq49ZMpk\/u3cbdu0lYTOJogygkkA8Rf\/vq3OF9s2TaO\/bbjeNpncmNMOqzQVUEdQWlJKRW71nE536MTRbdr+PDjO3x0e0gv0wgpCFHQhUDjAsZH9p3HW88X5zsendfcO+5xp58yzBSrxhJiYNUYVruOs7MVh0eP0aIq+PDBHZoQeXvS5\/a04mADWisSrYgIEiVxPtL5yHpv+O6ipl43nL7e8uibGTLTHN86YrE3FABlyovTLX6x4+i4Ii8SPj7pcVSmrK3n4mCZrVuEc0xKTXcwZIlG3L2NPqtbyhB4d1rRL3K01kx6iizV5KkmxIjzcDAeaT2BlPduVdTTAuMCb5YHNnvLctNQKcH2YHj5\/YxeT1P0FKE1vDlfU3rLf00gFjlOKVSe4q7WXC4X9MuEbFgiBejffvIeSmmqXkrVy0ArvJR4ITi4QKYknkiiJQiBDVBl0E81tfH0E0VPCX5YHvDG08bIw\/tj9KGl2dTcGaT849srTJ6QIvDNAYQiCInf7fH1nuXpEsweoRT6+KhkWGRordBakSca4yNaCfqZRgmBD5EQIokS1J2mNR4fIqebjidXV3xzeoXqDeg1az545x618ZxeSl4uWr56dAVKo6SiWWxI7Z7D9kAUAqklUimCc8QAuD161TlG\/ZxUSWyI5FpQZZpeqpBSEIEQr\/V1PpL\/yLeNo58IzON\/8bAoefb1K8Jb7\/N4ccykl1FWOfce3MTenbJc1jz955fI9QUH1xHTCqTAr3eIPEcojVLgjEG\/MykZlRlSSpRWZFoipcRGECHiQ8SFiPMRiNStx9hA3Xn21hNipLUWCZy\/WvL955+TFH2KG2P6gxJNZHW5JR0d4bIcZQ1aBryLMBwRtnO0FkjZI0SB+OL1ImZJgpKSALQuYkNEcB0bwLqA9YGD8bQ2EIkocc1PL2Zc\/O9b1Ml9frhc8fSZwbyakTiHcAaVJQzu3SXEgN3uiYcDyhl2sxnjk2N29YH9YolUikwZ9LpxZBb48VZ8hM5HXAQfIo0NbNYr3nz\/iLW7Xt84Txhnim1r2RjHpKoo7SWzbs9wdUq9X0C9I0sUrtXYHy4IR3d5+ME7tHXD4nyBM2MuX70mqyrSqsJ7T9d06CdXNWWaoJUkIggREilACFobeL3r+OZ8Q7ppmPYLrJB0xnJmHTsTuGojy90KrRNmy4a0zCmPMsgTknaDMzvSZsfVbMtXu5a86FHvG7I0g6RAtTuyVOCJGOnQf3+1YpAlVKlmmicsOserreGtMmWaJ+RK0DrNZX6f7zaO3emSNFFM5Bzn98gkZykUVSmZDnLODw6rBmxjRnQVYb9FRIfE0718zj7VxODpEAig83vq+TlCZoBAz2rL+daQJoq+EIQQMZ3n\/HSDaww6kaATmp2lO1\/inz7DbLdMTxyr5SVFohFHI4w3XJZ9dJkTREpoHHG3JtECLQTRB0y7xhw8SEXSH+JcQMkE9E2ynqDbzdC3BwnGRUIUTIuETeOotx2htSRS0i4O2OUOVx8I6y3oDJGXvHw1g2RCG0vePr7P+ZPvaN\/MkeUYqgyR9qBSDEYFRSKYn52jQkTIHYgItiVNJMSIyjO8rQneoN8tNKnSOAQekBH8pOQi0XSNIw4K4rXxyLJPWK2Jhw0UQ9RwTJJqRmHB8\/Ua0ZsQ2xa6+bWJeYke57x\/7yY3Jj109Lx5fcHF61O8a5EiR6U5g1HFdlaj0gydEBmnknGR8bK2nEVLS0QoiUwUItXEPIWiwC93iH0G\/SEizwlNQ7tc8J+zLWJwE5mkqGyK7ywST1873PKMemZJdE7dOm6fHLG6usQbT1ef4WvP1k7pT4\/J9BTFL\/74WVSSMlWYEAgIXi5b2sZhjMdzbRwICBFhHaQp0XkwFpwl2hZpDiQqkipIehm3TkY0reHmyQ2cSFDB4ro9aZ6z3x+QWLzURB\/x7RV33rpB2u+j68WGJz7w\/HJHL88ISvNgkpPekFztHRd7S2sjQUqs7xGEQK52iLIgFAVxn3H3+C6rFy+oFzNGpWM8SPB2A+2K+fmG3\/z651zMt7x49ozV14\/wXQMyASEoq4yurTCmBUBVg59+ZlEkVR\/\/4+\/xEeatI9EKlEBreV28VUo5KkiHJSJPkaM+cjSkPyz5\/acfEYsRtukYVBmLyyt6Gcxnc4z3HN864fTNFcEHbHdgND0hHU7pVWNQKTZK9nXN\/wECJSo73jElDwAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/qc-quite-concise-programming-language_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022741\u0022 height=\u0022418\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThe idea is to create a \u003Cstrong\u003Esimple\u003C\/strong\u003E (in terms of complexity, not ease of use) programming language, that will have quite a \u003Cstrong\u003Econcise\u003C\/strong\u003E \u003Cstrong\u003Esyntax\u003C\/strong\u003E, that will play with the power of \u003Cstrong\u003EUnicode\u003C\/strong\u003E and most importantly \u2013 will be \u003Cstrong\u003Efun\u003C\/strong\u003E to create ;)\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["consise","esoteric","j\u0119zyk","language","php","programming","programming language","programowanie","qc"],"hasMore":false,"image":"https:\/\/avris.it\/image\/qc-quite-concise-programming-language_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/qc-quite-concise-programming-language_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/qc-quite-concise-programming-language_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022135.38461538462\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThe idea is to create a \u003Cstrong\u003Esimple\u003C\/strong\u003E (in terms of complexity, not ease of use) programming language, that will have quite a \u003Cstrong\u003Econcise\u003C\/strong\u003E \u003Cstrong\u003Esyntax\u003C\/strong\u003E, that will play with the power of \u003Cstrong\u003EUnicode\u003C\/strong\u003E and most importantly \u2013 will be \u003Cstrong\u003Efun\u003C\/strong\u003E to create ;)\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/qc-quite-concise-programming-language_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/qc-quite-concise-programming-language_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022135.38461538462\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EThe idea is to create a \u003Cstrong\u003Esimple\u003C\/strong\u003E (in terms of complexity, not ease of use) programming language, that will have quite a \u003Cstrong\u003Econcise\u003C\/strong\u003E \u003Cstrong\u003Esyntax\u003C\/strong\u003E, that will play with the power of \u003Cstrong\u003EUnicode\u003C\/strong\u003E and most importantly \u2013 will be \u003Cstrong\u003Efun\u003C\/strong\u003E to create ;)\u003C\/p\u003E","words":40,"readTime":null,"lang":"en"}}},"projects\/can-i-go-home-now":{"key":"projects\/can-i-go-home-now","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:45+02:00","publishedAt":"2015-12-10T13:39:00+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/canigohome.avris.it\/","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/GoHome","displayUrl":null}],"category":"projects","subcategory":null,"slug":"can-i-go-home-now"},"content":{"en":{"slug":"can-i-go-home-now","title":"Can I go home now?","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/can-i-go-home-now_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022239.24882629108\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGbklEQVRIiV2WuW4TXxuHn5kzS+xxbMd2Fi8xTkgiBAWhAAmFCCgiKGhYGi6Aq6DJTXADFAiJkooOmoQKCMIgpKBAEi\/xJHa8jGc861cgRvp\/U56RzvZ7zvO+0rt37yLXdWk0Guzv73N4eEij0aBQKHDr1i10XefLly\/UajWCIKBcLjM\/P08ymcQwDCRJwjAMyuUyAJ1Oh0KhgBACgDAMiaIIWZYxDIMgCBgMBkiSxGQy4cePH+RyOXzfp9VqoaiqyqtXr+j3+xwdHXFycsLdu3fJZrO8fv2alZUV7t27R7PZxLIsjo6OODs7QwhBpVJhbm4Ox3EIw5B8Po9hGLiui6qqSJJEGIaEYUgmkwHAcZx4g0EQMB6PEUIghCAIApSPHz9Sr9dxXZd8Ps\/t27f59OkT79+\/J5\/Ps7y8jO\/7rK+vc3BwQBAEHB8fA1AsFomiCMdxME0zvi0ASZKQZZkoikgmkwghcF0Xx3Hicd\/3MQyDZDJJo9HAcRxEtVrd\/vz5M2tra2xsbPDy5Uva7TbXrl1jeXmZhYUFgiBgOBxSKpUAEEKgqipCCMIwpFQqMT09jaIoWJaFrusAFAoFoigiCAISiQTn5+eYpkkikUBRFPr9Pp1OJ57PdV2UbrfLs2fPaLVavHnzBs\/zWFpaIpfLsbu7C8DMzAySJHF2dkalUsFxHEajEZIkoaoq37594+rVqwCUSiXS6XQcSSqVQpIk\/v8bDAZ4nkcQBERRRLPZpFgsIjRN2378+DEvXrxACMFoNCKZTLK3t4dpmqytraGqKr7v4zgOtm2ztLREJpNBURT+\/PnD6ekppVIJWZYxTRMAXddRFIUgCPB9nyAIcF2XbreLpmlkMhnG4zG9Xg\/f97EsCwB5amqKTqdDLpej0+lgWRYHBwckEgkymQypVCqGLpvNIoTAtm2EEDFL09PT9Pt9PM\/D933a7Tau68ZRRFGEaZpYloUQgkwmQxiGDIdDbNtmMpkgyzKj0QhlcXGR58+f8\/TpU6Iool6vI4RgOBySy+XQdR3DMBBCkMvlkGWZbDaLJEmkUimCIIg3ValU4n+apiFJElEUoaoqxWKRTqeDruuEYYjv+zFfsiyjqiqmaSL29\/e3wzDk69ev3Lx5k3Q6TavVIpVK0e12WVlZIZ1Ok06nmZ2dpVKp\/Of1HB8fx4tOJhMGgwG9Xg9FUUgmk0wmE2zbJgxDxuMxh4eHNJtNRqMRg8EA3\/dJpVJEUfQ3CVVVt\/\/Jq16vU6vVWFlZod1uk0wmWV9fJ5PJkMlkYrgVRUFVVTzPo1QqMZlMGA6HDAYD2u02p6endLtdhBBEUYRlWfi+j+u61Ot1wjDEdV1s22ZqaoowDJmbm\/ubhKqq257nxQAeHR2haRqbm5t0Oh0uX77MhQsXmJ6exrZtHMchCAJmZmZQFOVv7opCuzuicXhAr9fDNE0cx0FRFDzPw7btWBPtdpswDAHQNC3G4t+YAqCqKqqqIssyruvy8+dPPM\/jwYMHzM\/P47oug8EAXdfxfT+ePJ\/PM5lMODw8pFKugjvC933m5uYoFAo0m02GwyGLi4sIIZBlGUVRSCQSWJaFpmn0ej263S6GYZDP51GEEExNTREEQfwioiji169f\/P79O2YhDEMKhQKzs7NIkkS328W2bTzP4\/z8HNd1WV1djQ28uroKwM7ODslkkmKxCMDe3h7VapVisYiu6\/R6PRKJBLlc7q8Yx+MxhmHgeR7pdBrf95FlmStXrnBwcMDi4iKqqsZ2\/ReX67qcnp7G9cuyLFRV5eTkBNM0GY\/HhGGI4zh0Oh16vR75fJ5yuYymaWiahq7rJJNJbNvm\/PycQqGABESqqsYykySJra0tTk5OODk5wbZtxuMxiUQCx3HQNI3JZEK5XMayLC5dukS9XscwDGq1GsPhkO\/fv8cHK5fLTE9Ps76+zo0bN5BlmX6\/T6lUiotyo9GgVqthGAayLMux0BYWFnjy5Ak7Ozvs7Ozgui6SJBEEAaPRCF3XUVWVfD4fl490Os2jR4+4ePEiHz58QJIkNjY2CMMQWZapVqtsbW3x8OFDNjc3Y5Zc1yWbzcbdwXg8\/lv579y5s22aJvfv30dRFN6+fctkMolbhX9Q+74fC83zPBzHYW5ujoWFBSqVCru7u\/T7fVqtFrlcjuvXr+N5HsvLy1SrVVZXVxmNRjHM\/3opx3GoVquxy\/4H69Nv42Fs0AsAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/can-i-go-home-now_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022239.24882629108\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EMissing your cosy home? Come in to find out, if you can stop working already and head back home!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/can-i-go-home-now_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022478.49765258216\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGbklEQVRIiV2WuW4TXxuHn5kzS+xxbMd2Fi8xTkgiBAWhAAmFCCgiKGhYGi6Aq6DJTXADFAiJkooOmoQKCMIgpKBAEi\/xJHa8jGc861cgRvp\/U56RzvZ7zvO+0rt37yLXdWk0Guzv73N4eEij0aBQKHDr1i10XefLly\/UajWCIKBcLjM\/P08ymcQwDCRJwjAMyuUyAJ1Oh0KhgBACgDAMiaIIWZYxDIMgCBgMBkiSxGQy4cePH+RyOXzfp9VqoaiqyqtXr+j3+xwdHXFycsLdu3fJZrO8fv2alZUV7t27R7PZxLIsjo6OODs7QwhBpVJhbm4Ox3EIw5B8Po9hGLiui6qqSJJEGIaEYUgmkwHAcZx4g0EQMB6PEUIghCAIApSPHz9Sr9dxXZd8Ps\/t27f59OkT79+\/J5\/Ps7y8jO\/7rK+vc3BwQBAEHB8fA1AsFomiCMdxME0zvi0ASZKQZZkoikgmkwghcF0Xx3Hicd\/3MQyDZDJJo9HAcRxEtVrd\/vz5M2tra2xsbPDy5Uva7TbXrl1jeXmZhYUFgiBgOBxSKpUAEEKgqipCCMIwpFQqMT09jaIoWJaFrusAFAoFoigiCAISiQTn5+eYpkkikUBRFPr9Pp1OJ57PdV2UbrfLs2fPaLVavHnzBs\/zWFpaIpfLsbu7C8DMzAySJHF2dkalUsFxHEajEZIkoaoq37594+rVqwCUSiXS6XQcSSqVQpIk\/v8bDAZ4nkcQBERRRLPZpFgsIjRN2378+DEvXrxACMFoNCKZTLK3t4dpmqytraGqKr7v4zgOtm2ztLREJpNBURT+\/PnD6ekppVIJWZYxTRMAXddRFIUgCPB9nyAIcF2XbreLpmlkMhnG4zG9Xg\/f97EsCwB5amqKTqdDLpej0+lgWRYHBwckEgkymQypVCqGLpvNIoTAtm2EEDFL09PT9Pt9PM\/D933a7Tau68ZRRFGEaZpYloUQgkwmQxiGDIdDbNtmMpkgyzKj0QhlcXGR58+f8\/TpU6Iool6vI4RgOBySy+XQdR3DMBBCkMvlkGWZbDaLJEmkUimCIIg3ValU4n+apiFJElEUoaoqxWKRTqeDruuEYYjv+zFfsiyjqiqmaSL29\/e3wzDk69ev3Lx5k3Q6TavVIpVK0e12WVlZIZ1Ok06nmZ2dpVKp\/Of1HB8fx4tOJhMGgwG9Xg9FUUgmk0wmE2zbJgxDxuMxh4eHNJtNRqMRg8EA3\/dJpVJEUfQ3CVVVt\/\/Jq16vU6vVWFlZod1uk0wmWV9fJ5PJkMlkYrgVRUFVVTzPo1QqMZlMGA6HDAYD2u02p6endLtdhBBEUYRlWfi+j+u61Ot1wjDEdV1s22ZqaoowDJmbm\/ubhKqq257nxQAeHR2haRqbm5t0Oh0uX77MhQsXmJ6exrZtHMchCAJmZmZQFOVv7opCuzuicXhAr9fDNE0cx0FRFDzPw7btWBPtdpswDAHQNC3G4t+YAqCqKqqqIssyruvy8+dPPM\/jwYMHzM\/P47oug8EAXdfxfT+ePJ\/PM5lMODw8pFKugjvC933m5uYoFAo0m02GwyGLi4sIIZBlGUVRSCQSWJaFpmn0ej263S6GYZDP51GEEExNTREEQfwioiji169f\/P79O2YhDEMKhQKzs7NIkkS328W2bTzP4\/z8HNd1WV1djQ28uroKwM7ODslkkmKxCMDe3h7VapVisYiu6\/R6PRKJBLlc7q8Yx+MxhmHgeR7pdBrf95FlmStXrnBwcMDi4iKqqsZ2\/ReX67qcnp7G9cuyLFRV5eTkBNM0GY\/HhGGI4zh0Oh16vR75fJ5yuYymaWiahq7rJJNJbNvm\/PycQqGABESqqsYykySJra0tTk5OODk5wbZtxuMxiUQCx3HQNI3JZEK5XMayLC5dukS9XscwDGq1GsPhkO\/fv8cHK5fLTE9Ps76+zo0bN5BlmX6\/T6lUiotyo9GgVqthGAayLMux0BYWFnjy5Ak7Ozvs7Ozgui6SJBEEAaPRCF3XUVWVfD4fl490Os2jR4+4ePEiHz58QJIkNjY2CMMQWZapVqtsbW3x8OFDNjc3Y5Zc1yWbzcbdwXg8\/lv579y5s22aJvfv30dRFN6+fctkMolbhX9Q+74fC83zPBzHYW5ujoWFBSqVCru7u\/T7fVqtFrlcjuvXr+N5HsvLy1SrVVZXVxmNRjHM\/3opx3GoVquxy\/4H69Nv42Fs0AsAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/can-i-go-home-now_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022478.49765258216\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EMissing your cosy home? Come in to find out, if you can stop working already and head back home!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["assetic","countdown","free time","javascript","job","js","micrus","php","programming","programowanie","weekend"],"hasMore":false,"image":"https:\/\/avris.it\/image\/can-i-go-home-now_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/can-i-go-home-now_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/can-i-go-home-now_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022119.62441314554\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EMissing your cosy home? Come in to find out, if you can stop working already and head back home!\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/can-i-go-home-now_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/can-i-go-home-now_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022119.62441314554\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EMissing your cosy home? Come in to find out, if you can stop working already and head back home!\u003C\/p\u003E","words":19,"readTime":null,"lang":"en"}}},"blog\/technology\/php-aspectmock-fa\u0142szowanie-czasu":{"key":"blog\/technology\/php-aspectmock-fa\u0142szowanie-czasu","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:40+02:00","publishedAt":"2015-07-30T09:25:00+02:00","group":"mocks","category":"blog","subcategory":"technology","slug":"php-aspectmock-fa\u0142szowanie-czasu"},"content":{"pl":{"slug":"php-aspectmock-fa\u0142szowanie-czasu","title":"PHP + AspectMock \u2013 fa\u0142szowanie czasu","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_small.jpeg\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022291.32075471698\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAVCAYAAAAuJkyQAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGw0lEQVRIic2Wy45kRxGGv8jMc6s6dev73DxjM5ZBBgkhIbAsC5Y8Fk\/FIyB5hWBjyx7beKZ7erqnq7su534yM1jUvAEsiH1Iv\/6IL+IXQPk\/KpdYhw8B6yxpkoIaxAgigggoiqCAIAhRQFAEi2oEFBEBBFRRAYNBAdWICIhw6I9KFEUVRAVEMSKItUBk7AfcMit46GqmecGTk1PmQ0s+LYkacRjqcaCdLCiGlqWziEQqD8NyhSiY4Jk93DJdHBFiZO0D9vgEo5HolWJzyzQriAG8RqqjY+Lde05dggI7Y8kfP0Fix7t3VxgVxYghhkAzdFR1D80D435D4XcsGQm+x8fIi2RPX+\/5OB+IwdNcX7HYvOU477mQW1z9Dt3f86vqFXr7lt9+MufYRZ4Pa769fM1YrVH1ZGPNWb\/mm+tL0maLH0ZMkpO6FBNi\/GA5VPua0FV8VozMQ8unhefXZU1dNZxKx1hV3N7cs3u4x6iFpmbJHtO2vLmtOEkDj01Dv69p+5Hb6x3PJLLZN4xtw4WJ\/PH357y0I5t9zVBVPE4CUSO+6xmHHhdVESBxjtw5vriYsvOe81nCq03HdlTIhWlmuX6IDEa48w4xiiQTsqzldSV8tjJc14AVHqKlSwpe3+84SzLSPCedwJtguP9uwy\/yCW82I8VkypvR4tWTmuSwv6vJRLvRk2U5F48e4axBfEASizEOjMEmDosBDP1Qs1yt6AcljCNp7jCA9540z7m+umK1XJDnU7bbLcdHpwxjTz4piRpw1jKOAY0BMYIVgyikNnJ78xaHQlQlSRLKyQxrDRoi1hqisQe+RLBWIEb8mLDdVgjgQ2AYhBgVJ4a2HZjkE8ZupGs3RInsdvcA9H2LNe4DtYKooubAb55kJKmiUXHNOOJjZDad4TcbynKCOksaA2Fo6b0SFwuy+zVZWTALPY1N0OmcqIprKqZJxCUZMQw0OGK5OIjfPXDhBygmhBCpdxvqxYqyqpjM5kQ\/sI2R8uiEodkTQ8T1PmCNIfiRne+RuUcHh7UGEWj7nqxP6GdKOvfstx3GwbQI7Ko92teEJynN\/XvKk4LdemCyLJhdNyyKhHc09GZPt+7J84zMGKpTofJb7NyiQ46xjnGsUVEMH0YSYqT8\/BQ+WTD9wznfXF4xPM354fYGgNukpn7pyf5SULuartnz49Ur+s8j3RcZ4U85w5cp7qucG3dJPLKk5wuql8L9ccv32xv0dzmzTwte2wdujmt+Mvf4J2AQQlQUcxB0OKVKsUhYnc7IEotzDrrA0IxYk\/D4y1Pe\/P2G\/Cjl7IsLJEmRRIj7yI9\/uyK1GU7h56\/fcvJ8zsPlhvXllunJBHFgjUHriI97zn9zxObnisWzKXf\/3KAaiUER6xAwmmcZZ6slRVkimSWOHqMOYywalfl8Sqc9BMUbJfSRIsloupYQI6IQ8WSzCbGPiI1M3JxyWhAN+MET\/EjiUlQVSQwaI9EoGSllOePh5g1N3+MAVqsj8mLKJC+x1hIkYKwlzVJi9ABMzBRjDf04MNpAnhWsFitCGBmD8nC\/RrrI0WIOCE1VY01BIgl5liKFQQU0xIMTqhhr8MEz9iNt1SAWHChN23C0WCJDj6KkenilcaiILgURDBCx5NbgsgSItF2Ljh2zEJlmOYjSGUeMkVlZ0lUV0yFgrCAmoZGIphlpWyNiiAJ+MmO5PCaqYjEHh4a+xYllpiPLskAx6BjwfUc1yRmCZ1ZvyPOSpm7ZTpdY4iG39CPWGoyFEDx+6BFVpgjqPVaVEAURf3BFDLYfsVlO7Fp8NiFGTzGdMA7jYamjKtv9hlm553hV8dGLgX+8\/YmPXni+\/enfoJ7584ifPvD97SVdu2f38J7u9hVPTmqePW9xxR2nZxXzeEm4\/5mnzyZIblm92CPFmm9ff8fzT0rKGYT0jsnqjkef9jx9DF3X0PUdyAfKVBVVpakDPgpf\/2vNzX3LD+8GRt\/x+NwRhxFrAn\/+6hwjhjJLWC5S3r7Z4YwwSS3TSUpbR375csn9ZUseYHM3EBQmuaPdbDjNe55eZITR8\/66ZbuNDEPPtCwxzmJB\/mqMYbVY8nSVsO89F6uMd5UyRpBkSjEtMUZ4+74HcWyblKjKvvZU3nJ1NzIMsGsCKo51FbhqA41JeXQ+5f1aISl5aCzbGk7Olly+E86eHnNzD30\/MnQN9oA9aq3l2aMnGGuIAD5inCGqkqYpqLDbbxErnByf0LQ9282G1XJJVhSs12suzs4JYSSEQ4IMwRNipK5rFvMZziUYsYx+JE1TQvAgMPQDfhzAe4ZhOAj6H8fi\/6r+A3A83c4qjf3rAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_small.jpeg\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022291.32075471698\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EW pewnym projekcie operuj\u0119 poprzez API na li\u015bcie wyda\u0144 produktu: zamykam wydania, kt\u00f3rych czas ju\u017c min\u0105\u0142, i tworz\u0119 nowe na osiem tygodni naprz\u00f3d. Moimi punktami odniesienia w czasie s\u0105:\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_big.jpeg\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022636\u0022 height=\u0022386\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAVCAYAAAAuJkyQAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGw0lEQVRIic2Wy45kRxGGv8jMc6s6dev73DxjM5ZBBgkhIbAsC5Y8Fk\/FIyB5hWBjyx7beKZ7erqnq7su534yM1jUvAEsiH1Iv\/6IL+IXQPk\/KpdYhw8B6yxpkoIaxAgigggoiqCAIAhRQFAEi2oEFBEBBFRRAYNBAdWICIhw6I9KFEUVRAVEMSKItUBk7AfcMit46GqmecGTk1PmQ0s+LYkacRjqcaCdLCiGlqWziEQqD8NyhSiY4Jk93DJdHBFiZO0D9vgEo5HolWJzyzQriAG8RqqjY+Lde05dggI7Y8kfP0Fix7t3VxgVxYghhkAzdFR1D80D435D4XcsGQm+x8fIi2RPX+\/5OB+IwdNcX7HYvOU477mQW1z9Dt3f86vqFXr7lt9+MufYRZ4Pa769fM1YrVH1ZGPNWb\/mm+tL0maLH0ZMkpO6FBNi\/GA5VPua0FV8VozMQ8unhefXZU1dNZxKx1hV3N7cs3u4x6iFpmbJHtO2vLmtOEkDj01Dv69p+5Hb6x3PJLLZN4xtw4WJ\/PH357y0I5t9zVBVPE4CUSO+6xmHHhdVESBxjtw5vriYsvOe81nCq03HdlTIhWlmuX6IDEa48w4xiiQTsqzldSV8tjJc14AVHqKlSwpe3+84SzLSPCedwJtguP9uwy\/yCW82I8VkypvR4tWTmuSwv6vJRLvRk2U5F48e4axBfEASizEOjMEmDosBDP1Qs1yt6AcljCNp7jCA9540z7m+umK1XJDnU7bbLcdHpwxjTz4piRpw1jKOAY0BMYIVgyikNnJ78xaHQlQlSRLKyQxrDRoi1hqisQe+RLBWIEb8mLDdVgjgQ2AYhBgVJ4a2HZjkE8ZupGs3RInsdvcA9H2LNe4DtYKooubAb55kJKmiUXHNOOJjZDad4TcbynKCOksaA2Fo6b0SFwuy+zVZWTALPY1N0OmcqIprKqZJxCUZMQw0OGK5OIjfPXDhBygmhBCpdxvqxYqyqpjM5kQ\/sI2R8uiEodkTQ8T1PmCNIfiRne+RuUcHh7UGEWj7nqxP6GdKOvfstx3GwbQI7Ko92teEJynN\/XvKk4LdemCyLJhdNyyKhHc09GZPt+7J84zMGKpTofJb7NyiQ46xjnGsUVEMH0YSYqT8\/BQ+WTD9wznfXF4xPM354fYGgNukpn7pyf5SULuartnz49Ur+s8j3RcZ4U85w5cp7qucG3dJPLKk5wuql8L9ccv32xv0dzmzTwte2wdujmt+Mvf4J2AQQlQUcxB0OKVKsUhYnc7IEotzDrrA0IxYk\/D4y1Pe\/P2G\/Cjl7IsLJEmRRIj7yI9\/uyK1GU7h56\/fcvJ8zsPlhvXllunJBHFgjUHriI97zn9zxObnisWzKXf\/3KAaiUER6xAwmmcZZ6slRVkimSWOHqMOYywalfl8Sqc9BMUbJfSRIsloupYQI6IQ8WSzCbGPiI1M3JxyWhAN+MET\/EjiUlQVSQwaI9EoGSllOePh5g1N3+MAVqsj8mLKJC+x1hIkYKwlzVJi9ABMzBRjDf04MNpAnhWsFitCGBmD8nC\/RrrI0WIOCE1VY01BIgl5liKFQQU0xIMTqhhr8MEz9iNt1SAWHChN23C0WCJDj6KkenilcaiILgURDBCx5NbgsgSItF2Ljh2zEJlmOYjSGUeMkVlZ0lUV0yFgrCAmoZGIphlpWyNiiAJ+MmO5PCaqYjEHh4a+xYllpiPLskAx6BjwfUc1yRmCZ1ZvyPOSpm7ZTpdY4iG39CPWGoyFEDx+6BFVpgjqPVaVEAURf3BFDLYfsVlO7Fp8NiFGTzGdMA7jYamjKtv9hlm553hV8dGLgX+8\/YmPXni+\/enfoJ7584ifPvD97SVdu2f38J7u9hVPTmqePW9xxR2nZxXzeEm4\/5mnzyZIblm92CPFmm9ff8fzT0rKGYT0jsnqjkef9jx9DF3X0PUdyAfKVBVVpakDPgpf\/2vNzX3LD+8GRt\/x+NwRhxFrAn\/+6hwjhjJLWC5S3r7Z4YwwSS3TSUpbR375csn9ZUseYHM3EBQmuaPdbDjNe55eZITR8\/66ZbuNDEPPtCwxzmJB\/mqMYbVY8nSVsO89F6uMd5UyRpBkSjEtMUZ4+74HcWyblKjKvvZU3nJ1NzIMsGsCKo51FbhqA41JeXQ+5f1aISl5aCzbGk7Olly+E86eHnNzD30\/MnQN9oA9aq3l2aMnGGuIAD5inCGqkqYpqLDbbxErnByf0LQ9282G1XJJVhSs12suzs4JYSSEQ4IMwRNipK5rFvMZziUYsYx+JE1TQvAgMPQDfhzAe4ZhOAj6H8fi\/6r+A3A83c4qjf3rAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_big.jpeg\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022636\u0022 height=\u0022386\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EW pewnym projekcie operuj\u0119 poprzez API na li\u015bcie wyda\u0144 produktu: zamykam wydania, kt\u00f3rych czas ju\u017c min\u0105\u0142, i tworz\u0119 nowe na osiem tygodni naprz\u00f3d. Moimi punktami odniesienia w czasie s\u0105:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Enew \\DateTime\u003C\/code\u003E \u2013 dzi\u015b,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003Enew \\DateTime(\u0027+8 weeks\u0027)\u003C\/code\u003E \u2013 koniec okresu, w kt\u00f3rym tworz\u0119 wydania,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003Enew \\DateTime(\u0027last friday\u0027)\u003C\/code\u003E \u2013 bo cykl ka\u017cdego wydania ko\u0144czy si\u0119 w pi\u0105tek, od tej daty iteruj\u0119 \u003Ccode\u003E+1 week\u003C\/code\u003E tworz\u0105c nowe wydania.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EDwie sprawy komplikuj\u0105 nam \u017cycie, je\u015bli chcemy stworzy\u0107 testy jednostkowe do tej klasy: po pierwsze nie powinni\u015bmy \u003Cem\u003Enaprawd\u0119\u003C\/em\u003E odwo\u0142ywa\u0107 si\u0119 do API, a jedynie uda\u0107 zapytania, a po drugie powinni\u015bmy uda\u0107 \u017ce \u003Cem\u003Edzisiaj\u003C\/em\u003E jest kt\u00f3ry\u015btam, jaka\u015b sta\u0142a data, do kt\u00f3rej b\u0119d\u0105 dostosowane nasze przypadki testowe.\u003C\/p\u003E\n\u003Cp\u003EMusimy zatem stworzy\u0107 par\u0119 dubli \u2013 klas, kt\u00f3re \u201cprzys\u0142oni\u0105\u201d swoje pierwowzory, podmieniaj\u0105c niekt\u00f3re ich metody na swoje. \u015awietnym narz\u0119dziem do tego celu jest biblioteka \u003Ca href=\u0022https:\/\/github.com\/Codeception\/AspectMock\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E AspectMock\u003C\/a\u003E, potrafi\u0105ca stworzy\u0107 takie duble, kt\u00f3re wszystkim innym nawet si\u0119 nie \u015bni\u0142y (np. duble metod statycznych czy funkcji systemowych).\u003C\/p\u003E\n\u003Cp\u003ENiestety, cho\u0107 AspectMock potrafi \u201cnadpisa\u0107\u201d funkcj\u0119 \u003Ccode\u003Etime()\u003C\/code\u003E, na nic to si\u0119 zda, je\u015bli u\u017cywamy \u003Ccode\u003EDateTime\u003C\/code\u003E (a kto nie u\u017cywa?), a j\u0105 nadpisa\u0107 jest ju\u017c ci\u0119\u017cej. Na szcz\u0119\u015bcie da si\u0119 to zrobi\u0107 za pomoc\u0105 pewnej sztuczki:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EVR\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003ERedmine\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EVersionsHandler\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/** \u003Cspan class=\u0022hljs-doctag\u0022\u003E@var\u003C\/span\u003E Api *\/\u003C\/span\u003E\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $api;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $now;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $until;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003E__construct\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E(Api $api)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;api = $api;\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;now = TimeProvider::getDateTimeFormatted();\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;until = TimeProvider::getDateTimeFormatted(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027+8 weeks\u0027\u003C\/span\u003E);\n    }\n\n    \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ (...)\u003C\/span\u003E\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetToBeCreated\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $current = TimeProvider::getDateTime(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027last friday\u0027\u003C\/span\u003E);\n        \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ (...)\u003C\/span\u003E\n    }\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EJak wida\u0107, we wszystkich miejscach, gdzie normalnie u\u017cy\u0142bym \u003Ccode\u003EDateTime\u003C\/code\u003E, ja korzystam z klasy \u003Ccode\u003ETimeProvider\u003C\/code\u003E. Jest ona bardzo pro\u015bciutka:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EVR\u003C\/span\u003E\\\u003Cspan class=\u0022hljs-title\u0022\u003ERedmine\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003ETimeProvider\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Estatic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetDateTime\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($time = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027now\u0027\u003C\/span\u003E)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\DateTime($time);\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Estatic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EgetDateTimeFormatted\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($time = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027now\u0027\u003C\/span\u003E)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $datetime = \u003Cspan class=\u0022hljs-keyword\u0022\u003Eself\u003C\/span\u003E::getDateTime($time);\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $datetime-\u0026gt;format(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027Y.\\WW\u0027\u003C\/span\u003E);\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EI istnieje tylko po to, by m\u00f3c j\u0105 zdublowa\u0107 w nast\u0119puj\u0105cy spos\u00f3b:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$timeProxy = Test::double(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027VR\\\\Redmine\\\\TimeProvider\u0027\u003C\/span\u003E, [\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027getDateTime\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($time)\u003C\/span\u003E \u003C\/span\u003E{\n    $dt = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E \\DateTime(\u003Cspan class=\u0022hljs-string\u0022\u003E\u00272015-01-14\u0027\u003C\/span\u003E);\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $dt-\u0026gt;modify($time);\n}]);\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EA zatem podczas normalnego uruchomienia aplikacji, \u003Ccode\u003ETimeProvider\u003C\/code\u003E dzia\u0142a po prostu jak nicnierobi\u0105ca fabryka \u003Ccode\u003EDateTime\u003C\/code\u003E\u2019\u00f3w. W czasie test\u00f3w jednak obiera ona za punkt odniesienia arbitraln\u0105 dat\u0119 \u003Ccode\u003E2015-01-04\u003C\/code\u003E, a potem modyfikuje j\u0105 o to, co podamy jej w parametrze, jak jakby dzi\u015b by\u0142 \u003Ccode\u003E2015-01-04\u003C\/code\u003E. Zawsze, niezale\u017cnie kiedy uruchomimy test.\u003C\/p\u003E\n\u003Cp\u003ETeraz wystarczy tylko zdublowa\u0107 funkcj\u0119 odwo\u0142uj\u0105c\u0105 si\u0119 do API i przeprowadzi\u0107 testy:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E$apiProxy = Test::double(\n  \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027VR\\\\Redmine\\\\Api\u0027\u003C\/span\u003E,\n  [\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027curl\u0027\u003C\/span\u003E =\u0026gt; \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($url, $method = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027GET\u0027\u003C\/span\u003E, $data = [])\u003C\/span\u003E \u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E ($method != \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027GET\u0027\u003C\/span\u003E) { \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Enull\u003C\/span\u003E; }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E json_encode(\u003Cspan class=\u0022hljs-comment\u0022\u003E\/* mocked api response *\/\u003C\/span\u003E);\n}]);\n\n$handler = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E VersionsHandler(\u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Api);\n\n\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;assertEquals(\u003Cspan class=\u0022hljs-comment\u0022\u003E\/* expected output *\/\u003C\/span\u003E, $handler-\u0026gt;handle());\n\u003C\/code\u003E\u003C\/pre\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["aop","aspectmock","czas","datetime","php","programowanie"],"hasMore":true,"image":"https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_small.jpeg","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_big.jpeg\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_mini.jpeg\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022145.66037735849\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EW pewnym projekcie operuj\u0119 poprzez API na li\u015bcie wyda\u0144 produktu: zamykam wydania, kt\u00f3rych czas ju\u017c min\u0105\u0142, i tworz\u0119 nowe na osiem tygodni naprz\u00f3d. Moimi punktami odniesienia w czasie s\u0105:\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_big.jpeg\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/php-aspectmock-fa\u0142szowanie-czasu_mini.jpeg\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022145.66037735849\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EW pewnym projekcie operuj\u0119 poprzez API na li\u015bcie wyda\u0144 produktu: zamykam wydania, kt\u00f3rych czas ju\u017c min\u0105\u0142, i tworz\u0119 nowe na osiem tygodni naprz\u00f3d. Moimi punktami odniesienia w czasie s\u0105:\u003C\/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Enew \\DateTime\u003C\/code\u003E \u2013 dzi\u015b,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003Enew \\DateTime(\u0027+8 weeks\u0027)\u003C\/code\u003E \u2013 koniec okresu, w kt\u00f3rym tworz\u0119 wydania,\u003C\/li\u003E\n\u003Cli\u003E\u003Ccode\u003Enew \\DateTime(\u0027last friday\u0027)\u003C\/code\u003E \u2013 bo cykl ka\u017cdego wydania ko\u0144czy si\u0119 w pi\u0105tek, od tej daty iteruj\u0119 \u003Ccode\u003E+1 week\u003C\/code\u003E tworz\u0105c nowe wydania.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cp\u003EDwie sprawy komplikuj\u0105 nam \u017cycie, je\u015bli chcemy stworzy\u0107 testy jednostkowe do tej klasy: po pierwsze nie powinni\u015bmy \u003Cem\u003Enaprawd\u0119\u003C\/em\u003E odwo\u0142ywa\u0107 si\u0119 do API, a jedynie uda\u0107 zapytania, a po drugie powinni\u015bmy uda\u0107 \u017ce \u003Cem\u003Edzisiaj\u003C\/em\u003E jest kt\u00f3ry\u015btam, jaka\u015b sta\u0142a data, do kt\u00f3rej b\u0119d\u0105 dostosowane nasze przypadki testowe.\u003C\/p\u003E\n\u003Cp\u003EMusimy zatem stworzy\u0107 par\u0119 dubli \u2013 klas, kt\u00f3re \u201cprzys\u0142oni\u0105\u201d swoje pierwowzory, podmieniaj\u0105c niekt\u00f3re ich metody na swoje. \u015awietnym narz\u0119dziem do tego celu jest biblioteka \u003Ca href=\u0022https:\/\/github.com\/Codeception\/AspectMock\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E AspectMock\u003C\/a\u003E, potrafi\u0105ca stworzy\u0107 takie duble, kt\u00f3re wszystkim innym nawet si\u0119 nie \u015bni\u0142y (np. duble metod statycznych czy funkcji systemowych).\u003C\/p\u003E\n\u003Cp\u003ENiestety, cho\u0107 AspectMock potrafi \u201cnadpisa\u0107\u201d funkcj\u0119 \u003Ccode\u003Etime()\u003C\/code\u003E, na nic to si\u0119 zda, je\u015bli u\u017cywamy \u003Ccode\u003EDateTime\u003C\/code\u003E (a kto nie u\u017cywa?), a j\u0105 nadpisa\u0107 jest ju\u017c ci\u0119\u017cej. Na szcz\u0119\u015bcie da si\u0119 to zrobi\u0107 za pomoc\u0105 pewnej sztuczki:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E\u0026lt;?php\nnamespace VR\\Redmine;\n\nclass VersionsHandler\n{\n    \/** @var Api *\/\n    private $api;\n\n    private $now;\n\n    private $until;\n\n    public function __construct(Api $api)\n    {\n        $this-\u0026gt;api = $api;\n\n        $this-\u0026gt;now = TimeProvider::getDateTimeFormatted();\n\n        $this-\u0026gt;until = TimeProvider::getDateTimeFormatted(\u0027+8 weeks\u0027);\n    }\n\n    \/\/ (...)\n\n    private function getToBeCreated()\n    {\n        $current = TimeProvider::getDateTime(\u0027last friday\u0027);\n        \/\/ (...)\n    }\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EJak wida\u0107, we wszystkich miejscach, gdzie normalnie u\u017cy\u0142bym \u003Ccode\u003EDateTime\u003C\/code\u003E, ja korzystam z klasy \u003Ccode\u003ETimeProvider\u003C\/code\u003E. Jest ona bardzo pro\u015bciutka:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E\u0026lt;?php\nnamespace VR\\Redmine;\n\nclass TimeProvider\n{\n    public static function getDateTime($time = \u0027now\u0027)\n    {\n        return new \\DateTime($time);\n    }\n\n    public static function getDateTimeFormatted($time = \u0027now\u0027)\n    {\n        $datetime = self::getDateTime($time);\n\n        return $datetime-\u0026gt;format(\u0027Y.\\WW\u0027);\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EI istnieje tylko po to, by m\u00f3c j\u0105 zdublowa\u0107 w nast\u0119puj\u0105cy spos\u00f3b:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E$timeProxy = Test::double(\u0027VR\\\\Redmine\\\\TimeProvider\u0027, [\u0027getDateTime\u0027 =\u0026gt; function($time) {\n    $dt = new \\DateTime(\u00272015-01-14\u0027);\n    return $dt-\u0026gt;modify($time);\n}]);\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EA zatem podczas normalnego uruchomienia aplikacji, \u003Ccode\u003ETimeProvider\u003C\/code\u003E dzia\u0142a po prostu jak nicnierobi\u0105ca fabryka \u003Ccode\u003EDateTime\u003C\/code\u003E\u2019\u00f3w. W czasie test\u00f3w jednak obiera ona za punkt odniesienia arbitraln\u0105 dat\u0119 \u003Ccode\u003E2015-01-04\u003C\/code\u003E, a potem modyfikuje j\u0105 o to, co podamy jej w parametrze, jak jakby dzi\u015b by\u0142 \u003Ccode\u003E2015-01-04\u003C\/code\u003E. Zawsze, niezale\u017cnie kiedy uruchomimy test.\u003C\/p\u003E\n\u003Cp\u003ETeraz wystarczy tylko zdublowa\u0107 funkcj\u0119 odwo\u0142uj\u0105c\u0105 si\u0119 do API i przeprowadzi\u0107 testy:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E$apiProxy = Test::double(\n  \u0027VR\\\\Redmine\\\\Api\u0027,\n  [\u0027curl\u0027 =\u0026gt; function($url, $method = \u0027GET\u0027, $data = []) {\n    if ($method != \u0027GET\u0027) { return null; }\n\n    return json_encode(\/* mocked api response *\/);\n}]);\n\n$handler = new VersionsHandler(new Api);\n\n$this-\u0026gt;assertEquals(\/* expected output *\/, $handler-\u0026gt;handle());\u003C\/code\u003E\u003C\/pre\u003E","words":455,"readTime":2,"lang":"pl"}}},"projects\/clavis-shared-password-storage":{"key":"projects\/clavis-shared-password-storage","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:38+02:00","publishedAt":"2015-07-16T11:31:00+02:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/clavis.avris.it\/","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Clavis","displayUrl":null}],"category":"projects","subcategory":null,"slug":"clavis-shared-password-storage"},"content":{"en":{"slug":"clavis-shared-password-storage","title":"Clavis \u2013 Shared password storage","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.17339667458\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEKElEQVRIiZ2Wv2\/bRhTHP3e8I0VSsiLHdl2oQeog3bIU6dr8DRnyJ3Tp2iVdAiND\/pEAyVhkDTLkr+iaIYCBoChgWxItUby714EiTVpx4uYt4o93d+99fzxRHR8fC8BkMkEpxbeE956zszMePHjAdDr9pvXv3r1Da40REQAeP36MtfZ\/b6aUYjab8fr1a46Ojrhz5weyLOf09JQ8z3HOobVum3XOAQqtFaPRDkrVBb19+xalFKaLiohQFAXWWpIkwXtPFEVbyD1\/\/pyjoyPev3\/PixcvGA6HbXH7+wcopUjTFBFhvV5jrf0i+g0oSqlLhACWyyUiwocPH7DWYoxhb2+vPbCJu3fvcnh4SFEUrNfr9nkInuVqRZamnJ+fUxQFy+WSLMvw3oMCayxVVRFCIE1TqqpiOBq1hRmlVFvhYDAghMD9+\/fRWrdJVVWhtQIUxhhevXrFcDjkyZMnVzqFal1BmpLnOSYyTCYT4jj+ohzKsrxEqLlofmuOa15FBK0jQPBeEMAYw7Nnz9om9vf36+6h1kPwLJdLjDEYaxgMBj1avhY9yrz3VFUFgKsqnA+k6QCUImwOnTvHLw8f4rxHa02WZczn801Bmt3JBAScd9g4vrYQEWkbCSG0z3qiDsFTliUhBKyJMMZQliXOeZIkBqAoCiJj8M4RRRHpIO0dcnZ6Rpql\/FOeMLs4J8\/zXiEKODB3cM7x6dMnRITd3d1LyrodaB0RgkcEsnzYc4bUJ7Y6M8bUayLd5kRRxK3JLQC+i6bspYdbyCjA6pg4jrl3717NxkYmW6JWSjEYpBhjWic0kHrvca5CKd26pqoqTEes3nuKiwvyLIMA1cohIoxGo5vbvrloYFCbjbXWKBQo2lmUJPEGxUAIAQmhtlZn49VyiTUGYyyjUXxtET3UOsX2KAsSWFcVIoK1htWqxBiD94EsS6EjRO9DjU5nM6014\/F4s8bf2FlN3paotdYbeGvHjMe3EBFqWunkXQe\/sF5XWGvRWrez7GvRaGhL1EBrexHh5OQE730NaZQQVExsI3768ftrOmVjCmlpvUn0bN9UBvXQy\/O8vX\/69E\/29m5TLBY476gqx87ODi9fvqQxQx9hRRInQK3D5WqFVqr9b7wuukiaq+rv3j969CuLxaL3\/uDgoDfZu+GdZzafE8eW2WzWUr2zM6Jcr2mykyQhjmvBf\/z4kfF43O7Xo0xE+HcBWQxB4Lff\/yDSEBuYryAxoBXMV4JWtZ6z+LKoyBhu366HXJZlLYJf0tJ0Ot2eQ1109kefX7ibX9JzlarPRRRFXFxcoLWmLEtEhDiOKcsV1sb89TcsyoAI\/Dy9RLxF6M2bN9\/8xdiIcotC73GuHo71CLFUlUMpTekiIqXQEfiN9kUEdXx8LJfW7qPQPeTqu25+k+u9v7GzrkbzefIfN0I9xLU5sOkAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.17339667458\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWe all have lots of lots of passwords and access keys... Keeping them in a web browser or a window application is a good idea, but only until you need to share them with somebody.\u003C\/p\u003E\n\u003Cp\u003EClavis can be installed on your PHP server (no 3rd party involved in storing your sensitive data). It lets you share your keys and be in control of what and with whom are you sharing.\u003C\/p\u003E\n\u003Cp\u003EIt\u2019s easy, safe, comfortable and totally free!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022534.34679334917\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEKElEQVRIiZ2Wv2\/bRhTHP3e8I0VSsiLHdl2oQeog3bIU6dr8DRnyJ3Tp2iVdAiND\/pEAyVhkDTLkr+iaIYCBoChgWxItUby714EiTVpx4uYt4o93d+99fzxRHR8fC8BkMkEpxbeE956zszMePHjAdDr9pvXv3r1Da40REQAeP36MtfZ\/b6aUYjab8fr1a46Ojrhz5weyLOf09JQ8z3HOobVum3XOAQqtFaPRDkrVBb19+xalFKaLiohQFAXWWpIkwXtPFEVbyD1\/\/pyjoyPev3\/PixcvGA6HbXH7+wcopUjTFBFhvV5jrf0i+g0oSqlLhACWyyUiwocPH7DWYoxhb2+vPbCJu3fvcnh4SFEUrNfr9nkInuVqRZamnJ+fUxQFy+WSLMvw3oMCayxVVRFCIE1TqqpiOBq1hRmlVFvhYDAghMD9+\/fRWrdJVVWhtQIUxhhevXrFcDjkyZMnVzqFal1BmpLnOSYyTCYT4jj+ohzKsrxEqLlofmuOa15FBK0jQPBeEMAYw7Nnz9om9vf36+6h1kPwLJdLjDEYaxgMBj1avhY9yrz3VFUFgKsqnA+k6QCUImwOnTvHLw8f4rxHa02WZczn801Bmt3JBAScd9g4vrYQEWkbCSG0z3qiDsFTliUhBKyJMMZQliXOeZIkBqAoCiJj8M4RRRHpIO0dcnZ6Rpql\/FOeMLs4J8\/zXiEKODB3cM7x6dMnRITd3d1LyrodaB0RgkcEsnzYc4bUJ7Y6M8bUayLd5kRRxK3JLQC+i6bspYdbyCjA6pg4jrl3717NxkYmW6JWSjEYpBhjWic0kHrvca5CKd26pqoqTEes3nuKiwvyLIMA1cohIoxGo5vbvrloYFCbjbXWKBQo2lmUJPEGxUAIAQmhtlZn49VyiTUGYyyjUXxtET3UOsX2KAsSWFcVIoK1htWqxBiD94EsS6EjRO9DjU5nM6014\/F4s8bf2FlN3paotdYbeGvHjMe3EBFqWunkXQe\/sF5XWGvRWrez7GvRaGhL1EBrexHh5OQE730NaZQQVExsI3768ftrOmVjCmlpvUn0bN9UBvXQy\/O8vX\/69E\/29m5TLBY476gqx87ODi9fvqQxQx9hRRInQK3D5WqFVqr9b7wuukiaq+rv3j969CuLxaL3\/uDgoDfZu+GdZzafE8eW2WzWUr2zM6Jcr2mykyQhjmvBf\/z4kfF43O7Xo0xE+HcBWQxB4Lff\/yDSEBuYryAxoBXMV4JWtZ6z+LKoyBhu366HXJZlLYJf0tJ0Ot2eQ1109kefX7ibX9JzlarPRRRFXFxcoLWmLEtEhDiOKcsV1sb89TcsyoAI\/Dy9RLxF6M2bN9\/8xdiIcotC73GuHo71CLFUlUMpTekiIqXQEfiN9kUEdXx8LJfW7qPQPeTqu25+k+u9v7GzrkbzefIfN0I9xLU5sOkAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022534.34679334917\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWe all have lots of lots of passwords and access keys... Keeping them in a web browser or a window application is a good idea, but only until you need to share them with somebody.\u003C\/p\u003E\n\u003Cp\u003EClavis can be installed on your PHP server (no 3rd party involved in storing your sensitive data). It lets you share your keys and be in control of what and with whom are you sharing.\u003C\/p\u003E\n\u003Cp\u003EIt\u2019s easy, safe, comfortable and totally free!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["company","micrus","password","php","security","share"],"hasMore":false,"image":"https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.58669833729\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWe all have lots of lots of passwords and access keys... Keeping them in a web browser or a window application is a good idea, but only until you need to share them with somebody.\u003C\/p\u003E\n\u003Cp\u003EClavis can be installed on your PHP server (no 3rd party involved in storing your sensitive data). It lets you share your keys and be in control of what and with whom are you sharing.\u003C\/p\u003E\n\u003Cp\u003EIt\u2019s easy, safe, comfortable and totally free!\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.58669833729\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWe all have lots of lots of passwords and access keys... Keeping them in a web browser or a window application is a good idea, but only until you need to share them with somebody.\u003C\/p\u003E\n\u003Cp\u003EClavis can be installed on your PHP server (no 3rd party involved in storing your sensitive data). It lets you share your keys and be in control of what and with whom are you sharing.\u003C\/p\u003E\n\u003Cp\u003EIt\u2019s easy, safe, comfortable and totally free!\u003C\/p\u003E","words":78,"readTime":null,"lang":"en"},"pl":{"slug":"clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a","title":"Clavis \u2013 wsp\u00f3\u0142dzielony schowek na has\u0142a","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.17339667458\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEKElEQVRIiZ2Wv2\/bRhTHP3e8I0VSsiLHdl2oQeog3bIU6dr8DRnyJ3Tp2iVdAiND\/pEAyVhkDTLkr+iaIYCBoChgWxItUby714EiTVpx4uYt4o93d+99fzxRHR8fC8BkMkEpxbeE956zszMePHjAdDr9pvXv3r1Da40REQAeP36MtfZ\/b6aUYjab8fr1a46Ojrhz5weyLOf09JQ8z3HOobVum3XOAQqtFaPRDkrVBb19+xalFKaLiohQFAXWWpIkwXtPFEVbyD1\/\/pyjoyPev3\/PixcvGA6HbXH7+wcopUjTFBFhvV5jrf0i+g0oSqlLhACWyyUiwocPH7DWYoxhb2+vPbCJu3fvcnh4SFEUrNfr9nkInuVqRZamnJ+fUxQFy+WSLMvw3oMCayxVVRFCIE1TqqpiOBq1hRmlVFvhYDAghMD9+\/fRWrdJVVWhtQIUxhhevXrFcDjkyZMnVzqFal1BmpLnOSYyTCYT4jj+ohzKsrxEqLlofmuOa15FBK0jQPBeEMAYw7Nnz9om9vf36+6h1kPwLJdLjDEYaxgMBj1avhY9yrz3VFUFgKsqnA+k6QCUImwOnTvHLw8f4rxHa02WZczn801Bmt3JBAScd9g4vrYQEWkbCSG0z3qiDsFTliUhBKyJMMZQliXOeZIkBqAoCiJj8M4RRRHpIO0dcnZ6Rpql\/FOeMLs4J8\/zXiEKODB3cM7x6dMnRITd3d1LyrodaB0RgkcEsnzYc4bUJ7Y6M8bUayLd5kRRxK3JLQC+i6bspYdbyCjA6pg4jrl3717NxkYmW6JWSjEYpBhjWic0kHrvca5CKd26pqoqTEes3nuKiwvyLIMA1cohIoxGo5vbvrloYFCbjbXWKBQo2lmUJPEGxUAIAQmhtlZn49VyiTUGYyyjUXxtET3UOsX2KAsSWFcVIoK1htWqxBiD94EsS6EjRO9DjU5nM6014\/F4s8bf2FlN3paotdYbeGvHjMe3EBFqWunkXQe\/sF5XWGvRWrez7GvRaGhL1EBrexHh5OQE730NaZQQVExsI3768ftrOmVjCmlpvUn0bN9UBvXQy\/O8vX\/69E\/29m5TLBY476gqx87ODi9fvqQxQx9hRRInQK3D5WqFVqr9b7wuukiaq+rv3j969CuLxaL3\/uDgoDfZu+GdZzafE8eW2WzWUr2zM6Jcr2mykyQhjmvBf\/z4kfF43O7Xo0xE+HcBWQxB4Lff\/yDSEBuYryAxoBXMV4JWtZ6z+LKoyBhu366HXJZlLYJf0tJ0Ot2eQ1109kefX7ibX9JzlarPRRRFXFxcoLWmLEtEhDiOKcsV1sb89TcsyoAI\/Dy9RLxF6M2bN9\/8xdiIcotC73GuHo71CLFUlUMpTekiIqXQEfiN9kUEdXx8LJfW7qPQPeTqu25+k+u9v7GzrkbzefIfN0I9xLU5sOkAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022267.17339667458\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWszyscy mamy wiele hase\u0142 i kluczy dost\u0119pu. Pami\u0119tanie ich w przegl\u0105darce internetowej albo w aplikacji okienkowej jest dobrym pomys\u0142em, ale tylko dop\u00f3ki b\u0119dziesz potrzebowa\u0142 je z kim\u015b wsp\u00f3\u0142dzieli\u0107.\u003C\/p\u003E\n\u003Cp\u003EClavis mo\u017ce by\u0107 zainstalowany na Twoim serwerze PHP (\u017cadna trzecia strona nie jest zaanga\u017cowana w przechowywanie Twoich danych wra\u017cliwych). Pozwala Ci przechowywa\u0107 Twoje klucze i kontrolowa\u0107 komu, co i kiedy udost\u0119pniasz.\u003C\/p\u003E\n\u003Cp\u003EClavis jest prosty, bezpieczny, wygodny i ca\u0142kowicie darmowy!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022534.34679334917\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEKElEQVRIiZ2Wv2\/bRhTHP3e8I0VSsiLHdl2oQeog3bIU6dr8DRnyJ3Tp2iVdAiND\/pEAyVhkDTLkr+iaIYCBoChgWxItUby714EiTVpx4uYt4o93d+99fzxRHR8fC8BkMkEpxbeE956zszMePHjAdDr9pvXv3r1Da40REQAeP36MtfZ\/b6aUYjab8fr1a46Ojrhz5weyLOf09JQ8z3HOobVum3XOAQqtFaPRDkrVBb19+xalFKaLiohQFAXWWpIkwXtPFEVbyD1\/\/pyjoyPev3\/PixcvGA6HbXH7+wcopUjTFBFhvV5jrf0i+g0oSqlLhACWyyUiwocPH7DWYoxhb2+vPbCJu3fvcnh4SFEUrNfr9nkInuVqRZamnJ+fUxQFy+WSLMvw3oMCayxVVRFCIE1TqqpiOBq1hRmlVFvhYDAghMD9+\/fRWrdJVVWhtQIUxhhevXrFcDjkyZMnVzqFal1BmpLnOSYyTCYT4jj+ohzKsrxEqLlofmuOa15FBK0jQPBeEMAYw7Nnz9om9vf36+6h1kPwLJdLjDEYaxgMBj1avhY9yrz3VFUFgKsqnA+k6QCUImwOnTvHLw8f4rxHa02WZczn801Bmt3JBAScd9g4vrYQEWkbCSG0z3qiDsFTliUhBKyJMMZQliXOeZIkBqAoCiJj8M4RRRHpIO0dcnZ6Rpql\/FOeMLs4J8\/zXiEKODB3cM7x6dMnRITd3d1LyrodaB0RgkcEsnzYc4bUJ7Y6M8bUayLd5kRRxK3JLQC+i6bspYdbyCjA6pg4jrl3717NxkYmW6JWSjEYpBhjWic0kHrvca5CKd26pqoqTEes3nuKiwvyLIMA1cohIoxGo5vbvrloYFCbjbXWKBQo2lmUJPEGxUAIAQmhtlZn49VyiTUGYyyjUXxtET3UOsX2KAsSWFcVIoK1htWqxBiD94EsS6EjRO9DjU5nM6014\/F4s8bf2FlN3paotdYbeGvHjMe3EBFqWunkXQe\/sF5XWGvRWrez7GvRaGhL1EBrexHh5OQE730NaZQQVExsI3768ftrOmVjCmlpvUn0bN9UBvXQy\/O8vX\/69E\/29m5TLBY476gqx87ODi9fvqQxQx9hRRInQK3D5WqFVqr9b7wuukiaq+rv3j969CuLxaL3\/uDgoDfZu+GdZzafE8eW2WzWUr2zM6Jcr2mykyQhjmvBf\/z4kfF43O7Xo0xE+HcBWQxB4Lff\/yDSEBuYryAxoBXMV4JWtZ6z+LKoyBhu366HXJZlLYJf0tJ0Ot2eQ1109kefX7ibX9JzlarPRRRFXFxcoLWmLEtEhDiOKcsV1sb89TcsyoAI\/Dy9RLxF6M2bN9\/8xdiIcotC73GuHo71CLFUlUMpTekiIqXQEfiN9kUEdXx8LJfW7qPQPeTqu25+k+u9v7GzrkbzefIfN0I9xLU5sOkAAAAASUVORK5CYII=\u0022 data-src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022534.34679334917\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWszyscy mamy wiele hase\u0142 i kluczy dost\u0119pu. Pami\u0119tanie ich w przegl\u0105darce internetowej albo w aplikacji okienkowej jest dobrym pomys\u0142em, ale tylko dop\u00f3ki b\u0119dziesz potrzebowa\u0142 je z kim\u015b wsp\u00f3\u0142dzieli\u0107.\u003C\/p\u003E\n\u003Cp\u003EClavis mo\u017ce by\u0107 zainstalowany na Twoim serwerze PHP (\u017cadna trzecia strona nie jest zaanga\u017cowana w przechowywanie Twoich danych wra\u017cliwych). Pozwala Ci przechowywa\u0107 Twoje klucze i kontrolowa\u0107 komu, co i kiedy udost\u0119pniasz.\u003C\/p\u003E\n\u003Cp\u003EClavis jest prosty, bezpieczny, wygodny i ca\u0142kowicie darmowy!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["bezpiecze\u0144stwo","firma","has\u0142o","micrus","php","wsp\u00f3\u0142dzielenie"],"hasMore":false,"image":"https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.58669833729\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWszyscy mamy wiele hase\u0142 i kluczy dost\u0119pu. Pami\u0119tanie ich w przegl\u0105darce internetowej albo w aplikacji okienkowej jest dobrym pomys\u0142em, ale tylko dop\u00f3ki b\u0119dziesz potrzebowa\u0142 je z kim\u015b wsp\u00f3\u0142dzieli\u0107.\u003C\/p\u003E\n\u003Cp\u003EClavis mo\u017ce by\u0107 zainstalowany na Twoim serwerze PHP (\u017cadna trzecia strona nie jest zaanga\u017cowana w przechowywanie Twoich danych wra\u017cliwych). Pozwala Ci przechowywa\u0107 Twoje klucze i kontrolowa\u0107 komu, co i kiedy udost\u0119pniasz.\u003C\/p\u003E\n\u003Cp\u003EClavis jest prosty, bezpieczny, wygodny i ca\u0142kowicie darmowy!\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/clavis-wsp\u00f3\u0142dzielony-schowek-na-has\u0142a_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022133.58669833729\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EWszyscy mamy wiele hase\u0142 i kluczy dost\u0119pu. Pami\u0119tanie ich w przegl\u0105darce internetowej albo w aplikacji okienkowej jest dobrym pomys\u0142em, ale tylko dop\u00f3ki b\u0119dziesz potrzebowa\u0142 je z kim\u015b wsp\u00f3\u0142dzieli\u0107.\u003C\/p\u003E\n\u003Cp\u003EClavis mo\u017ce by\u0107 zainstalowany na Twoim serwerze PHP (\u017cadna trzecia strona nie jest zaanga\u017cowana w przechowywanie Twoich danych wra\u017cliwych). Pozwala Ci przechowywa\u0107 Twoje klucze i kontrolowa\u0107 komu, co i kiedy udost\u0119pniasz.\u003C\/p\u003E\n\u003Cp\u003EClavis jest prosty, bezpieczny, wygodny i ca\u0142kowicie darmowy!\u003C\/p\u003E","words":79,"readTime":null,"lang":"pl"}}},"projects\/avris-stringer-old":{"key":"projects\/avris-stringer-old","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:32+02:00","publishedAt":"2015-05-09T17:59:00+02:00","group":null,"links":[{"icon":"brands gitlab","colour":"primary","url":"https:\/\/gitlab.com\/Avris\/Stringer","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Stringer-Micrus","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Stringer-Symfony","displayUrl":null}],"category":"projects","subcategory":null,"slug":"avris-stringer-old"},"content":{"pl":{"slug":"avris-stringer-old","title":"Avris Stringer","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-stringer-old_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022270.42253521127\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAH1UlEQVRIiS3U249cdQHA8e\/vci5z5ro77e6WbksprQVB0FLwnmjQKGi8PRrDH2Di\/+CDz0bDm4nGB2Mwmhh50MTEKCTwooKKQC2ltLPbdi8zO3Pm3M\/5XXzQP+LzET9+6RW\/WqX8+vU3+eLVp\/je1z\/D7vYAEOA8ZdVyvMy4v1ixyHJiFZBWJfOyRnrH4TLj6CRFCMnsaIEHDpdrmrol7xoO52taZ9FBzCCJufTAFuemEwpjODrJOEgz0rLGW4+ToH7ywx99\/\/RkwEbS45V3\/sPhqubcxibjQYQUgrJuKduOZV6xv1gyO0kpOssqK0iLio1hgjGeUZKwPZ2w2QuYDAY0naG1niiOQGpaY+msozaWIOqhpSBvDZ2Hznl0FJDEPXTSCzizOebaoxc5SDNe\/fd1tIDnn36MC6fHjIcxw86AsKR1y735Ei0USgm61tA1LY2xLPOSfj\/m2atPINqGl177O8N+n3VdkxUttxdLqtZgpCKQiiQM2R4JtAoY9Ps4Y9kcD9GR1vQGCceLNR95+ByLrOTO8QnHyzUbccjOmQE6TNhe9tmMQo61om46lJN0zlNVlqbr6LyHsmaU9OgNenz2kUssioqqbbhzkjKdDMhrw1OPXOTLVx9lFGjeur3PrcWaum6xUvHMlQfRAFZJNsYJ6kDQ78VkRcfeIuXx8zussxbTGTohQUsCpWlcS+s8rXM0TUtpDAhJP4rZP1nzxIUzXLtygfcO57z53ozjrOLUZMyXLj\/EtUtnmQ4STm0M2JokXM0b8rrhoCjohyH63XsrTFVgrCcOAwKt6HxNg2OR5TRdy7qqOMpyFnnJqqioOksSSwYupzq5y+bkNF5JsoMZ13uW28cLJI778xMO1zXOCUb9hEWWcbAq+PD5HUY9RaJH9Hst1\/ePwMNsmaFv3D0mLwsqa4m1Yme6waDXw1vPe4dzgkDRdi1Hq5w7h0uWRUU\/ibj56m+oy4ydrdP85Q+\/xHvPx57+BH\/652uEcZ8zn\/gWkQqwUjAZDSnalo\/unOfTV86xcSpCSoFLYZUVZFXD3bRgmdXoVdMgHAzDgJOsZDoe89DpCcdZwb00xxuD8bAuctxqn7d\/\/ysEnjxdMj21xe264ZlvfZfOOT54\/WUwDfN7M5R\/iePjOc9+4zv40YN88zNXuXZpmyhRCCXwjWOxyrl3knJc1gzjCO1BvPjbv\/nNXsBRuqbqHGVZsbU5QYiOddHQjwKysiYrKqJAMu5J7l\/\/K+Ots\/jhDm\/NFtw6WlG1HWcmYz5+eYfq7rsczW7y7Ndf4KRoOb+9w1eevkxvECDwmNqyXGS8f7iiNIZlWXO4riiMR1sv8N7SWkNeNrTGkeYV01HCoKdQEqYjTWsd870b\/PHPLyNxHOzd5tyFixR5waXPfo0msMxe\/RmvvTHg7uw2Tz71DD9\/8Qd87mvfZvPyRbK8QgswrWGZlhykOYMkgkqwFi3Ow6qs0PfmKcMHNpj2+3QGTNmQVg1hqIijgFBrxpFiVTfUyTa7n38Bi2B681Xu3XiL3bO7\/P13P0VKySOPP8He3h6PffJZplef44xUVFHAv2YH4DwXTk8YxZLSWM5vbWCsoers\/8WCNR799uEc4x270yFhHBA7kIB3UBtDqAL6ccww1MRBiI0Ed7OSbucZLl\/+FHU65+qTz+E8NNmSh5\/8KmkF87xhGIV4IclqQ4UgCRXeeTrnmC3WOO\/ZX6TMlgWLoqV2Hj3PKoRYklU1\/UBhvEdLySiJmUQRQjhaa1i3HSdVy6Jo6DzUCG6khlBu4NYO7z3OjlCZQwO26wikZDLsE4ch67ojbQymqTha1zjnWVvDcdVRtQ68Z9jroS2eVVVhrGEQxYTCI7QCpUiigH4ckVYNN+8vWTeWqSoooxFXzm4g25LD0rOlSnIbkvRCdNhjXhoCrUjiEIdnXjQ8JGGxzunajlXd4YUiUIqe1oxGCX0nqDuDjgOFhP+tawwq0FRNS6QjtieSzjne2j\/i5qJgNyg4XdzCO8tGeJa9G2\/z5LUvkJQpeVdx6\/o9HtzeZDsImM326A8HqIc\/TS1C1pVhdxBhcbTeM+xpJoGisp6h9txdFsyWJTqIIjAWL6DpOpz3KCUJBHjn2JuvuHGcIVVAHm+RxVsYZ\/FOEl25yJ3DBqF2UQja3csstGJIhb18hTQaIqwicoa883QIgjBgQwhwlv3MkNaGm\/M1J5XDCYV+YGNMWhTUxtEYi7WOvlJ4Bau65R\/3F5TGE2pNi8BZT209EkfVWYwH2xgMAglUxpPKEKUUgTdsJAHT8YCtcZ\/pKKGrWualZVVbDvOa+2lFayRxpAgDhT496PP8Y+f54zsfcHtV4gGvNcILPlisWdUW5QXWe0IESkMiIwQeLwTOOzrncc7jBAghCZUkDkMmSY8HJkM+tDPhwWnCsmy5fphyP+9YVg15Y1FSoUKBkILGCXQrNFmn+OrjD\/GLN26yqg0OxaxsyOoWqQMAnJR4JEpKlBKAwElP50Fbi\/PgASklSRByZXvCIAwZ9yO2BxF3TjKuH+dkraVxMB4k9HuetOlonSeJQgIl0HfLmvTWfXZHMRaN1pLSOZz3oENCPAiBxGGUQgqBlYJQejQSYaFBIBUIJQmEZGsyYtCLSSLFtB\/x\/sGS\/brj1KDP7qkem9qzfWqMrAr+8EFK6SWRlhgL\/wU7BW9dIEbl2gAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-stringer-old_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022270.42253521127\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EUwaga, projekt przeniesiony: \u003Ca href=\u0022\/projects\/avris-stringer\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Avris Stringer\u003C\/a\u003E\u003C\/strong\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EAvris Stringer\u003C\/strong\u003E to otwarta biblioteka PHP upraszczaj\u0105ca wiele operacji na stringach zwi\u0105zanych z tworzeniem interfejsu u\u017cytkownika, a przede wszystkim z dostosowywaniem form gramatycznych \u003Cstrong\u003Ej\u0119zyka polskiego\u003C\/strong\u003E do dynamicznych danych.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-stringer-old_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022710\u0022 height=\u0022400\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAH1UlEQVRIiS3U249cdQHA8e\/vci5z5ro77e6WbksprQVB0FLwnmjQKGi8PRrDH2Di\/+CDz0bDm4nGB2Mwmhh50MTEKCTwooKKQC2ltLPbdi8zO3Pm3M\/5XXzQP+LzET9+6RW\/WqX8+vU3+eLVp\/je1z\/D7vYAEOA8ZdVyvMy4v1ixyHJiFZBWJfOyRnrH4TLj6CRFCMnsaIEHDpdrmrol7xoO52taZ9FBzCCJufTAFuemEwpjODrJOEgz0rLGW4+ToH7ywx99\/\/RkwEbS45V3\/sPhqubcxibjQYQUgrJuKduOZV6xv1gyO0kpOssqK0iLio1hgjGeUZKwPZ2w2QuYDAY0naG1niiOQGpaY+msozaWIOqhpSBvDZ2Hznl0FJDEPXTSCzizOebaoxc5SDNe\/fd1tIDnn36MC6fHjIcxw86AsKR1y735Ei0USgm61tA1LY2xLPOSfj\/m2atPINqGl177O8N+n3VdkxUttxdLqtZgpCKQiiQM2R4JtAoY9Ps4Y9kcD9GR1vQGCceLNR95+ByLrOTO8QnHyzUbccjOmQE6TNhe9tmMQo61om46lJN0zlNVlqbr6LyHsmaU9OgNenz2kUssioqqbbhzkjKdDMhrw1OPXOTLVx9lFGjeur3PrcWaum6xUvHMlQfRAFZJNsYJ6kDQ78VkRcfeIuXx8zussxbTGTohQUsCpWlcS+s8rXM0TUtpDAhJP4rZP1nzxIUzXLtygfcO57z53ozjrOLUZMyXLj\/EtUtnmQ4STm0M2JokXM0b8rrhoCjohyH63XsrTFVgrCcOAwKt6HxNg2OR5TRdy7qqOMpyFnnJqqioOksSSwYupzq5y+bkNF5JsoMZ13uW28cLJI778xMO1zXOCUb9hEWWcbAq+PD5HUY9RaJH9Hst1\/ePwMNsmaFv3D0mLwsqa4m1Yme6waDXw1vPe4dzgkDRdi1Hq5w7h0uWRUU\/ibj56m+oy4ydrdP85Q+\/xHvPx57+BH\/652uEcZ8zn\/gWkQqwUjAZDSnalo\/unOfTV86xcSpCSoFLYZUVZFXD3bRgmdXoVdMgHAzDgJOsZDoe89DpCcdZwb00xxuD8bAuctxqn7d\/\/ysEnjxdMj21xe264ZlvfZfOOT54\/WUwDfN7M5R\/iePjOc9+4zv40YN88zNXuXZpmyhRCCXwjWOxyrl3knJc1gzjCO1BvPjbv\/nNXsBRuqbqHGVZsbU5QYiOddHQjwKysiYrKqJAMu5J7l\/\/K+Ots\/jhDm\/NFtw6WlG1HWcmYz5+eYfq7rsczW7y7Ndf4KRoOb+9w1eevkxvECDwmNqyXGS8f7iiNIZlWXO4riiMR1sv8N7SWkNeNrTGkeYV01HCoKdQEqYjTWsd870b\/PHPLyNxHOzd5tyFixR5waXPfo0msMxe\/RmvvTHg7uw2Tz71DD9\/8Qd87mvfZvPyRbK8QgswrWGZlhykOYMkgkqwFi3Ow6qs0PfmKcMHNpj2+3QGTNmQVg1hqIijgFBrxpFiVTfUyTa7n38Bi2B681Xu3XiL3bO7\/P13P0VKySOPP8He3h6PffJZplef44xUVFHAv2YH4DwXTk8YxZLSWM5vbWCsoers\/8WCNR799uEc4x270yFhHBA7kIB3UBtDqAL6ccww1MRBiI0Ed7OSbucZLl\/+FHU65+qTz+E8NNmSh5\/8KmkF87xhGIV4IclqQ4UgCRXeeTrnmC3WOO\/ZX6TMlgWLoqV2Hj3PKoRYklU1\/UBhvEdLySiJmUQRQjhaa1i3HSdVy6Jo6DzUCG6khlBu4NYO7z3OjlCZQwO26wikZDLsE4ch67ojbQymqTha1zjnWVvDcdVRtQ68Z9jroS2eVVVhrGEQxYTCI7QCpUiigH4ckVYNN+8vWTeWqSoooxFXzm4g25LD0rOlSnIbkvRCdNhjXhoCrUjiEIdnXjQ8JGGxzunajlXd4YUiUIqe1oxGCX0nqDuDjgOFhP+tawwq0FRNS6QjtieSzjne2j\/i5qJgNyg4XdzCO8tGeJa9G2\/z5LUvkJQpeVdx6\/o9HtzeZDsImM326A8HqIc\/TS1C1pVhdxBhcbTeM+xpJoGisp6h9txdFsyWJTqIIjAWL6DpOpz3KCUJBHjn2JuvuHGcIVVAHm+RxVsYZ\/FOEl25yJ3DBqF2UQja3csstGJIhb18hTQaIqwicoa883QIgjBgQwhwlv3MkNaGm\/M1J5XDCYV+YGNMWhTUxtEYi7WOvlJ4Bau65R\/3F5TGE2pNi8BZT209EkfVWYwH2xgMAglUxpPKEKUUgTdsJAHT8YCtcZ\/pKKGrWualZVVbDvOa+2lFayRxpAgDhT496PP8Y+f54zsfcHtV4gGvNcILPlisWdUW5QXWe0IESkMiIwQeLwTOOzrncc7jBAghCZUkDkMmSY8HJkM+tDPhwWnCsmy5fphyP+9YVg15Y1FSoUKBkILGCXQrNFmn+OrjD\/GLN26yqg0OxaxsyOoWqQMAnJR4JEpKlBKAwElP50Fbi\/PgASklSRByZXvCIAwZ9yO2BxF3TjKuH+dkraVxMB4k9HuetOlonSeJQgIl0HfLmvTWfXZHMRaN1pLSOZz3oENCPAiBxGGUQgqBlYJQejQSYaFBIBUIJQmEZGsyYtCLSSLFtB\/x\/sGS\/brj1KDP7qkem9qzfWqMrAr+8EFK6SWRlhgL\/wU7BW9dIEbl2gAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/avris-stringer-old_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022710\u0022 height=\u0022400\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EUwaga, projekt przeniesiony: \u003Ca href=\u0022\/projects\/avris-stringer\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-cogs\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Avris Stringer\u003C\/a\u003E\u003C\/strong\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EAvris Stringer\u003C\/strong\u003E to otwarta biblioteka PHP upraszczaj\u0105ca wiele operacji na stringach zwi\u0105zanych z tworzeniem interfejsu u\u017cytkownika, a przede wszystkim z dostosowywaniem form gramatycznych \u003Cstrong\u003Ej\u0119zyka polskiego\u003C\/strong\u003E do dynamicznych danych.\u003C\/p\u003E\n\u003Cp\u003EJe\u015bli na przyk\u0142ad u\u017cytkownik strony zdoby\u0142 X punkt\u00f3w, to w zale\u017cno\u015bci od X powinni\u015bmy mu powiedzie\u0107 np.: \u201czdoby\u0142e\u015b 1 punkt\u201d, \u201c2 punkt\u003Cstrong\u003Ey\u003C\/strong\u003E\u201d albo \u201c5 punkt\u003Cstrong\u003E\u00f3w\u003C\/strong\u003E\u201d.Albo i \u201czdoby\u0142\u003Cstrong\u003Ea\u015b\u003C\/strong\u003E\u201d, je\u015bli to kobieta. Mo\u017cemy te\u017c chcie\u0107 pokazywa\u0107 daty w jakim\u015b przyjemniejszym formacie ni\u017c liczbowe, na przyk\u0142ad zamiast \u201c2015-05-09 17:15\u201d, po prostu \u201cgodzin\u0119 temu\u201d. Stringer zatroszczy si\u0119 o to wszystko, i o wiele wi\u0119cej!\u003C\/p\u003E\n\u003Cp\u003EOpr\u00f3cz samej biblioteki stworzy\u0142em tak\u017ce bundle integruj\u0105ce j\u0105 z \u003Ca href=\u0022http:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Symfony\u003C\/a\u003E oraz z \u003Ca href=\u0022http:\/\/micrus.avris.it\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Micrusem\u003C\/a\u003E, dzi\u0119ki kt\u00f3rym wszystkie jej funkcje s\u0105 dost\u0119pne tak\u017ce jako serwisy oraz jako filtry \u003Ca href=\u0022http:\/\/twig.sensiolabs.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Twiga\u003C\/a\u003E. Wszystko mo\u017cna zainstalowa\u0107 jedn\u0105 linijk\u0105 w konsoli za pomoc\u0105 \u003Ca href=\u0022https:\/\/getcomposer.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Composera\u003C\/a\u003E.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["biblioteka","composer","framework","interfejs u\u017cytkownika","j\u0119zyk polski","micrus","odmiana","php","string","symfony2","tekst"],"hasMore":true,"image":"https:\/\/avris.it\/image\/avris-stringer-old_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-stringer-old_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-stringer-old_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022135.21126760563\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EUwaga, projekt przeniesiony: \u003Ca href=\u0022\/projects\/avris-stringer.lite\u0022\u003E Avris Stringer\u003C\/a\u003E\u003C\/strong\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EAvris Stringer\u003C\/strong\u003E to otwarta biblioteka PHP upraszczaj\u0105ca wiele operacji na stringach zwi\u0105zanych z tworzeniem interfejsu u\u017cytkownika, a przede wszystkim z dostosowywaniem form gramatycznych \u003Cstrong\u003Ej\u0119zyka polskiego\u003C\/strong\u003E do dynamicznych danych.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/avris-stringer-old_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/avris-stringer-old_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022135.21126760563\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EUwaga, projekt przeniesiony: \u003Ca href=\u0022\/projects\/avris-stringer.lite\u0022\u003E Avris Stringer\u003C\/a\u003E\u003C\/strong\u003E\u003C\/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EAvris Stringer\u003C\/strong\u003E to otwarta biblioteka PHP upraszczaj\u0105ca wiele operacji na stringach zwi\u0105zanych z tworzeniem interfejsu u\u017cytkownika, a przede wszystkim z dostosowywaniem form gramatycznych \u003Cstrong\u003Ej\u0119zyka polskiego\u003C\/strong\u003E do dynamicznych danych.\u003C\/p\u003E\n\u003Cp\u003EJe\u015bli na przyk\u0142ad u\u017cytkownik strony zdoby\u0142 X punkt\u00f3w, to w zale\u017cno\u015bci od X powinni\u015bmy mu powiedzie\u0107 np.: \u201czdoby\u0142e\u015b 1 punkt\u201d, \u201c2 punkt\u003Cstrong\u003Ey\u003C\/strong\u003E\u201d albo \u201c5 punkt\u003Cstrong\u003E\u00f3w\u003C\/strong\u003E\u201d.Albo i \u201czdoby\u0142\u003Cstrong\u003Ea\u015b\u003C\/strong\u003E\u201d, je\u015bli to kobieta. Mo\u017cemy te\u017c chcie\u0107 pokazywa\u0107 daty w jakim\u015b przyjemniejszym formacie ni\u017c liczbowe, na przyk\u0142ad zamiast \u201c2015-05-09 17:15\u201d, po prostu \u201cgodzin\u0119 temu\u201d. Stringer zatroszczy si\u0119 o to wszystko, i o wiele wi\u0119cej!\u003C\/p\u003E\n\u003Cp\u003EOpr\u00f3cz samej biblioteki stworzy\u0142em tak\u017ce bundle integruj\u0105ce j\u0105 z \u003Ca href=\u0022http:\/\/symfony.com\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Symfony\u003C\/a\u003E oraz z \u003Ca href=\u0022http:\/\/micrus.avris.it\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Micrusem\u003C\/a\u003E, dzi\u0119ki kt\u00f3rym wszystkie jej funkcje s\u0105 dost\u0119pne tak\u017ce jako serwisy oraz jako filtry \u003Ca href=\u0022http:\/\/twig.sensiolabs.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Twiga\u003C\/a\u003E. Wszystko mo\u017cna zainstalowa\u0107 jedn\u0105 linijk\u0105 w konsoli za pomoc\u0105 \u003Ca href=\u0022https:\/\/getcomposer.org\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Composera\u003C\/a\u003E.\u003C\/p\u003E","words":155,"readTime":null,"lang":"pl"}}},"blog\/technology\/jest\u0119-admin\u0119":{"key":"blog\/technology\/jest\u0119-admin\u0119","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:32+02:00","publishedAt":"2015-05-08T13:39:00+02:00","group":null,"category":"blog","subcategory":"technology","slug":"jest\u0119-admin\u0119"},"content":{"pl":{"slug":"jest\u0119-admin\u0119","title":"Jest\u0119 Admin\u0119!","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/jest\u0119-admin\u0119_small.jpeg\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022480\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAKJ0lEQVRYhZWXaWxc1RXHf\/fNe7PPvLE99jh2vIzHS0xCHEjACSIFCm2Blg8tUUMrylKp\/VAhVULql35pqSr1UwuqQKioaoWgKmRpUakqFhVaiRRKEoizeImdOONMbMcejz2LZ\/N77\/bDLB57nIiepzvv3aN7z\/nfs9xzRiweHpWN306DkCABISjR5nmFLUFA+aeyEuvp3agprYYv1\/eIylSU9ABIUbdmOXQCEVOuyvN\/PInLrRGbmKJjZz\/ToxP0DO0kOnqRzr4eAMZGzjE4dCuzIxO07R5gZX4Bm8eFz+8jNh1lu2cX6h9sdJzsYPJnk5ifmGQ6MzRtC7LwxgItT7Ygj1jk9+Zx7nBx7XgMY4dBR7iDK2NX6Ni1nfDrn6IkZII7H7kXEdLJhrxYfhdruouMzSKrWJiaDVNVCA30MPTlu4laGZQWndseeYDZ+AKubUGaI50sNC0wG5kl6o9y+4\/3oj6uEvpuK\/m78iw8s8DQj4YYefAs+399gIWuBfI\/LBD+fg93PHsn9m84iCkTeBU7SsVioVCI5XiC7p4eJs+P0tTURFdnJ0rBQCkYNLeGGB8fx9nop729nfm5OYKqi\/xqlrnPxhgcHMTX7IcwjI+P4x\/QMTDQNR0tozE2NoZ\/wI\/NZuP9l99n\/rN5wuFw1WP9+24lZ62tAxofH6e5tYVCNsvQ8D4KhQL+thaktFhZTRNoDuLxeOjt62N0dJT27dtJpJMA9AztZGlpCc9eD\/SA3++nJ9IDAvI\/zTGY24HX66W\/v590Os1Tv3wKvU9nZGSkCkjJmeRUE5ESK3LkwAjZVA7WJJ6Qh6RjDt9hgadgIRRBrlDAsCvk06soDjuWYeB0u\/BbKhlp0OrwMvtOgMaPGlFUhVnfLM6gk3wuT++lXhZbFlmRK9gb7chViWgTMDNFviuGx+8lu5zC06TTOyYRUjFrQr1EprBIPTqFfjhVl2RbkVXOMi1t\/wJrLZKhj\/E1n8eGKCelLKWZlCiS+keRAv\/xXpJv+kscWcJc90bWJO5WkjY+G8FQlS0lpesEUAWiLLb0RXlukwr6sV6STKE\/ltz6uBL+OXaKF949wg7HPlqVbTydeYImswmQiJq7ah3MOWxCVC0iROU+Ks+3ctlGE0uShybRD5dACSFKFhLw0odv8dwbryAUDc3poJjP01+I8E7mH\/gsf80VabES+hh\/8zkUoawLL+NYn9\/AZbWPgJKljuhVxyAEn8cu86vjryIkbGttwSwUyaUznCmc4UXnyxtcuBL6D\/6W89XDVAc132WzKBshUgNZ1HAEgaN9rBzxV2\/7v5z8gGKhyP4776Ap4MdlL0WFaZq8Jl+rRtdy6AT+lgsl90lRKkWVQXkIUVWnCm7ksXp+w9F+lplEP5xi\/HoM1e4kbUJ8JU1HdzeGYZJMrRI1p5nT5nE1TqO3XChddtUauPn8NTEEqHLrVTcACYGjfSTlJJrbjQ2L2\/oirLaFkBZMTUzg8TpIp03ebniBHwQjCClKdbRyxk01tVrAy3jV+pUbEW\/cLRAI9GO9zPcuY0MSDoZI2jV0n4\/P\/R6WMhlSQnDVPoYQkZJlbpo2G9Uqoqpmq3flW1R5IMnYM8SMGbL5PKrTTjgSYXbiIm5NkFrN0a7pPKvfT1Ex64wtZClT60Z5ocL\/QVLCnDbPLwLPYcoiprT44MS\/wa6xYuSYSaRI59Z42L+Ty7vhtOca6QaFj4cLvLr3En8PT7MqisiyUWpHhVRDGF8YUFxbYrdzN6plo9EVJJvJcvnSBG\/99SjjU9PEFldwuz0kG2CkO8We5h7e3JakmF\/FsFxk7BmMGbAsc2sFAtTszlduEsJly5TfMWuR\/HQBp+pEWhZ2TcEmLWYuT3Px8lVUVcNut5Pt9ZHR4BhTyFmLoNuG1+Nn10f7sU0eIHcTjaoXlc2BWx+FJV5OMbAsC8uyyOdz2BUbTiHImxYIgaZpOBx2XC4PK5k8PrebSDhMcXSFg28+RNfl8IZyUn9ygSo29cfrADaT4OLadYQQWJZJPrdKZ5NOS2uQiehcOT3AMtdoa2\/n9j1DLIxfpeOtdva8\/01cRXc5YDfUijpdNWl\/MwuVyOwOYoupGIaBKe0sXc9QWFpjPpNEU1SQkly+QHNTkIWl63T\/7hbuun7vZjNsgrNRt1Kq\/KJmbJ6vjxZHgAbdj9PpoLV3ALsvwJph4vQ4sNs1kGAZkkikh9iVGYaSe6m9ROSmUc8DpT4By0PU874W1bmluZ9QqJVAY5Dt+\/YhXS70liCapmKz2QhHenA6PczHZuCRBFSr2uYE3+yZsstEtWvbgmr4UkLxej+\/yb\/Ef5s+p\/ezCG1mK\/lclifs30NRbNgdCvfd\/wCrmRTCMGj6TpplJgkc7UP5giVK3TqfNn1LSC8N4J07yCAKg8v9G4C+nf8bxovncHg1TntyRKcnyaXSICFwOElSTqIfuxGojVTKDVnxYLnuVOcVMP345g6ilPOk1glCQIsRpNXfiOrQyBYKTE1OEG7uqBZN\/XCS5KFJLKzqvs1yKm+lpJz1sGF9bklJemkA39w95cCUNULkhjlAsphjdnaWmeg0j9xxX1WWEAL9sSTJQ1NYwrqpnHL7UZ\/2UkAmvgPv3EGErPlPfhMK2dz02Lz0DX+doY4BKv15pR3UD6dIMoX\/eC+K3Np9W16MkhIY39zBUtKKuua3bg+AS9V4qH0Q2msa\/E3\/o2pB2aSok6OsKyibTkrSFTCyNgxljXk37qmlSstyIxKiBCr16BTWFjVfNaXFp+4FXFlJ1gOrSS\/NSZ3Vzk\/oi0VoMBs42XyKPYtDfL7tDL60l4wrAwL6lvu45r2GZmqEMmkmMjHEmgGVfyaqjWanl8VsCo\/mIFss4LE7yRRyyGFBbn4M12gQ1bRh2AwGUv3YHu7Y+3Pv7w9xvtfgoeefYXGvjam7LxH5SYQY11j7dI3ce3nO\/uksX7p2D38WbzD82\/20PBNi4p0JCs8XKdxVZNGM4z7Qx9lEjO6De5lX17B3h\/hgcoR7Hv8WJ6LjDH71bi6krxPYFaHv3mFWdmdJfCWB9qRG\/P44a2fWUAyXjUQiQUNDA\/F4nK6uLvSATjQaJRa\/WnWQ2W0QjUbp7O\/E6XQiFEGCBEV3kUJbkeRiHMuyiE\/N4PP5OPvhCXw+H+FwmGQyicfnZXZ2Fq\/XS1dXF4uLiwBYlsXw8DCpVApSoHozkLc7uHDyApZloSgKM+Mz7LlrD8sPrnDq01PoiQBWG8SuxmhoaOD06dMoikLMjHGb53ayIkeGLIqiEGxo5NTJU+y5ZReWZdHQHORaLEYgEMDr9XJpcpLTpkkqlcLj8RCPx3nv3Xe5cuUKfUYvItH+gYzPtdMoG8mRBcCNm0UWSTWk6Ux1UDTX0PGTIUOBImq5h2qkkQQJLGERen2BS4tXCIfaSKRTNPr8XFmYxe\/2IhAUjCICgdfporBWamNNy+JaOoGmqggJ9n918D+7cfvl9qsBqQAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/jest\u0119-admin\u0119_small.jpeg\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022480\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EMo\u017ce nie napisz\u0119 tu nic ciekawego, ale po prostu musz\u0119 si\u0119 pochwali\u0107 :D Kupi\u0142em sobie wirtualny serwer prywatny. Czy\u015bciutki Linux na nim, do zainstalowania i skonfigurowania wszystko: Apache, PHP, MySQL, SFTP, domeny, maile i ca\u0142a masa innych pierd\u00f3\u0142. W wielu z nich grzeba\u0142em pierwszy raz w \u017cyciu. Ale chyba dogrzeba\u0142em si\u0119 do wszystkiego co trzeba, bo wygl\u0105da na \u015bmigaj\u0105ce \u015blicznie.\u003C\/p\u003E\n\u003Cp\u003EWreszcie mog\u0119 deployowa\u0107 po ludzku, z gitem i composerem, pisa\u0107 w nowszej wersji PHP, skonfigurowa\u0107 sobie, co mi si\u0119 tylko \u017cywnie podoba, nie robi\u0107 r\u0119cznie eksport\u00f3w rozk\u0142adu na \u003Ca href=\u0022http:\/\/bus.avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Busa\u003C\/a\u003E, kt\u00f3remu nie starcza\u0142o pami\u0119ci na wsp\u00f3\u0142dzielonym hostingu... Miodzio! :3\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/jest\u0119-admin\u0119_big.jpeg\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022625\u0022 height=\u0022625\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAKJ0lEQVRYhZWXaWxc1RXHf\/fNe7PPvLE99jh2vIzHS0xCHEjACSIFCm2Blg8tUUMrylKp\/VAhVULql35pqSr1UwuqQKioaoWgKmRpUakqFhVaiRRKEoizeImdOONMbMcejz2LZ\/N77\/bDLB57nIiepzvv3aN7z\/nfs9xzRiweHpWN306DkCABISjR5nmFLUFA+aeyEuvp3agprYYv1\/eIylSU9ABIUbdmOXQCEVOuyvN\/PInLrRGbmKJjZz\/ToxP0DO0kOnqRzr4eAMZGzjE4dCuzIxO07R5gZX4Bm8eFz+8jNh1lu2cX6h9sdJzsYPJnk5ifmGQ6MzRtC7LwxgItT7Ygj1jk9+Zx7nBx7XgMY4dBR7iDK2NX6Ni1nfDrn6IkZII7H7kXEdLJhrxYfhdruouMzSKrWJiaDVNVCA30MPTlu4laGZQWndseeYDZ+AKubUGaI50sNC0wG5kl6o9y+4\/3oj6uEvpuK\/m78iw8s8DQj4YYefAs+399gIWuBfI\/LBD+fg93PHsn9m84iCkTeBU7SsVioVCI5XiC7p4eJs+P0tTURFdnJ0rBQCkYNLeGGB8fx9nop729nfm5OYKqi\/xqlrnPxhgcHMTX7IcwjI+P4x\/QMTDQNR0tozE2NoZ\/wI\/NZuP9l99n\/rN5wuFw1WP9+24lZ62tAxofH6e5tYVCNsvQ8D4KhQL+thaktFhZTRNoDuLxeOjt62N0dJT27dtJpJMA9AztZGlpCc9eD\/SA3++nJ9IDAvI\/zTGY24HX66W\/v590Os1Tv3wKvU9nZGSkCkjJmeRUE5ESK3LkwAjZVA7WJJ6Qh6RjDt9hgadgIRRBrlDAsCvk06soDjuWYeB0u\/BbKhlp0OrwMvtOgMaPGlFUhVnfLM6gk3wuT++lXhZbFlmRK9gb7chViWgTMDNFviuGx+8lu5zC06TTOyYRUjFrQr1EprBIPTqFfjhVl2RbkVXOMi1t\/wJrLZKhj\/E1n8eGKCelLKWZlCiS+keRAv\/xXpJv+kscWcJc90bWJO5WkjY+G8FQlS0lpesEUAWiLLb0RXlukwr6sV6STKE\/ltz6uBL+OXaKF949wg7HPlqVbTydeYImswmQiJq7ah3MOWxCVC0iROU+Ks+3ctlGE0uShybRD5dACSFKFhLw0odv8dwbryAUDc3poJjP01+I8E7mH\/gsf80VabES+hh\/8zkUoawLL+NYn9\/AZbWPgJKljuhVxyAEn8cu86vjryIkbGttwSwUyaUznCmc4UXnyxtcuBL6D\/6W89XDVAc132WzKBshUgNZ1HAEgaN9rBzxV2\/7v5z8gGKhyP4776Ap4MdlL0WFaZq8Jl+rRtdy6AT+lgsl90lRKkWVQXkIUVWnCm7ksXp+w9F+lplEP5xi\/HoM1e4kbUJ8JU1HdzeGYZJMrRI1p5nT5nE1TqO3XChddtUauPn8NTEEqHLrVTcACYGjfSTlJJrbjQ2L2\/oirLaFkBZMTUzg8TpIp03ebniBHwQjCClKdbRyxk01tVrAy3jV+pUbEW\/cLRAI9GO9zPcuY0MSDoZI2jV0n4\/P\/R6WMhlSQnDVPoYQkZJlbpo2G9Uqoqpmq3flW1R5IMnYM8SMGbL5PKrTTjgSYXbiIm5NkFrN0a7pPKvfT1Ex64wtZClT60Z5ocL\/QVLCnDbPLwLPYcoiprT44MS\/wa6xYuSYSaRI59Z42L+Ty7vhtOca6QaFj4cLvLr3En8PT7MqisiyUWpHhVRDGF8YUFxbYrdzN6plo9EVJJvJcvnSBG\/99SjjU9PEFldwuz0kG2CkO8We5h7e3JakmF\/FsFxk7BmMGbAsc2sFAtTszlduEsJly5TfMWuR\/HQBp+pEWhZ2TcEmLWYuT3Px8lVUVcNut5Pt9ZHR4BhTyFmLoNuG1+Nn10f7sU0eIHcTjaoXlc2BWx+FJV5OMbAsC8uyyOdz2BUbTiHImxYIgaZpOBx2XC4PK5k8PrebSDhMcXSFg28+RNfl8IZyUn9ygSo29cfrADaT4OLadYQQWJZJPrdKZ5NOS2uQiehcOT3AMtdoa2\/n9j1DLIxfpeOtdva8\/01cRXc5YDfUijpdNWl\/MwuVyOwOYoupGIaBKe0sXc9QWFpjPpNEU1SQkly+QHNTkIWl63T\/7hbuun7vZjNsgrNRt1Kq\/KJmbJ6vjxZHgAbdj9PpoLV3ALsvwJph4vQ4sNs1kGAZkkikh9iVGYaSe6m9ROSmUc8DpT4By0PU874W1bmluZ9QqJVAY5Dt+\/YhXS70liCapmKz2QhHenA6PczHZuCRBFSr2uYE3+yZsstEtWvbgmr4UkLxej+\/yb\/Ef5s+p\/ezCG1mK\/lclifs30NRbNgdCvfd\/wCrmRTCMGj6TpplJgkc7UP5giVK3TqfNn1LSC8N4J07yCAKg8v9G4C+nf8bxovncHg1TntyRKcnyaXSICFwOElSTqIfuxGojVTKDVnxYLnuVOcVMP345g6ilPOk1glCQIsRpNXfiOrQyBYKTE1OEG7uqBZN\/XCS5KFJLKzqvs1yKm+lpJz1sGF9bklJemkA39w95cCUNULkhjlAsphjdnaWmeg0j9xxX1WWEAL9sSTJQ1NYwrqpnHL7UZ\/2UkAmvgPv3EGErPlPfhMK2dz02Lz0DX+doY4BKv15pR3UD6dIMoX\/eC+K3Np9W16MkhIY39zBUtKKuua3bg+AS9V4qH0Q2msa\/E3\/o2pB2aSok6OsKyibTkrSFTCyNgxljXk37qmlSstyIxKiBCr16BTWFjVfNaXFp+4FXFlJ1gOrSS\/NSZ3Vzk\/oi0VoMBs42XyKPYtDfL7tDL60l4wrAwL6lvu45r2GZmqEMmkmMjHEmgGVfyaqjWanl8VsCo\/mIFss4LE7yRRyyGFBbn4M12gQ1bRh2AwGUv3YHu7Y+3Pv7w9xvtfgoeefYXGvjam7LxH5SYQY11j7dI3ce3nO\/uksX7p2D38WbzD82\/20PBNi4p0JCs8XKdxVZNGM4z7Qx9lEjO6De5lX17B3h\/hgcoR7Hv8WJ6LjDH71bi6krxPYFaHv3mFWdmdJfCWB9qRG\/P44a2fWUAyXjUQiQUNDA\/F4nK6uLvSATjQaJRa\/WnWQ2W0QjUbp7O\/E6XQiFEGCBEV3kUJbkeRiHMuyiE\/N4PP5OPvhCXw+H+FwmGQyicfnZXZ2Fq\/XS1dXF4uLiwBYlsXw8DCpVApSoHozkLc7uHDyApZloSgKM+Mz7LlrD8sPrnDq01PoiQBWG8SuxmhoaOD06dMoikLMjHGb53ayIkeGLIqiEGxo5NTJU+y5ZReWZdHQHORaLEYgEMDr9XJpcpLTpkkqlcLj8RCPx3nv3Xe5cuUKfUYvItH+gYzPtdMoG8mRBcCNm0UWSTWk6Ux1UDTX0PGTIUOBImq5h2qkkQQJLGERen2BS4tXCIfaSKRTNPr8XFmYxe\/2IhAUjCICgdfporBWamNNy+JaOoGmqggJ9n918D+7cfvl9qsBqQAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/jest\u0119-admin\u0119_big.jpeg\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022625\u0022 height=\u0022625\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EMo\u017ce nie napisz\u0119 tu nic ciekawego, ale po prostu musz\u0119 si\u0119 pochwali\u0107 :D Kupi\u0142em sobie wirtualny serwer prywatny. Czy\u015bciutki Linux na nim, do zainstalowania i skonfigurowania wszystko: Apache, PHP, MySQL, SFTP, domeny, maile i ca\u0142a masa innych pierd\u00f3\u0142. W wielu z nich grzeba\u0142em pierwszy raz w \u017cyciu. Ale chyba dogrzeba\u0142em si\u0119 do wszystkiego co trzeba, bo wygl\u0105da na \u015bmigaj\u0105ce \u015blicznie.\u003C\/p\u003E\n\u003Cp\u003EWreszcie mog\u0119 deployowa\u0107 po ludzku, z gitem i composerem, pisa\u0107 w nowszej wersji PHP, skonfigurowa\u0107 sobie, co mi si\u0119 tylko \u017cywnie podoba, nie robi\u0107 r\u0119cznie eksport\u00f3w rozk\u0142adu na \u003Ca href=\u0022http:\/\/bus.avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E Busa\u003C\/a\u003E, kt\u00f3remu nie starcza\u0142o pami\u0119ci na wsp\u00f3\u0142dzielonym hostingu... Miodzio! :3\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["administracja","apache","konfiguracja","linux","php","serwer","vps"],"hasMore":false,"image":"https:\/\/avris.it\/image\/jest\u0119-admin\u0119_small.jpeg","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/jest\u0119-admin\u0119_big.jpeg\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/jest\u0119-admin\u0119_mini.jpeg\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022240\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EMo\u017ce nie napisz\u0119 tu nic ciekawego, ale po prostu musz\u0119 si\u0119 pochwali\u0107 :D Kupi\u0142em sobie wirtualny serwer prywatny. Czy\u015bciutki Linux na nim, do zainstalowania i skonfigurowania wszystko: Apache, PHP, MySQL, SFTP, domeny, maile i ca\u0142a masa innych pierd\u00f3\u0142. W wielu z nich grzeba\u0142em pierwszy raz w \u017cyciu. Ale chyba dogrzeba\u0142em si\u0119 do wszystkiego co trzeba, bo wygl\u0105da na \u015bmigaj\u0105ce \u015blicznie.\u003C\/p\u003E\n\u003Cp\u003EWreszcie mog\u0119 deployowa\u0107 po ludzku, z gitem i composerem, pisa\u0107 w nowszej wersji PHP, skonfigurowa\u0107 sobie, co mi si\u0119 tylko \u017cywnie podoba, nie robi\u0107 r\u0119cznie eksport\u00f3w rozk\u0142adu na \u003Ca href=\u0022http:\/\/bus.avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Busa\u003C\/a\u003E, kt\u00f3remu nie starcza\u0142o pami\u0119ci na wsp\u00f3\u0142dzielonym hostingu... Miodzio! :3\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/jest\u0119-admin\u0119_big.jpeg\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/jest\u0119-admin\u0119_mini.jpeg\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022240\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EMo\u017ce nie napisz\u0119 tu nic ciekawego, ale po prostu musz\u0119 si\u0119 pochwali\u0107 :D Kupi\u0142em sobie wirtualny serwer prywatny. Czy\u015bciutki Linux na nim, do zainstalowania i skonfigurowania wszystko: Apache, PHP, MySQL, SFTP, domeny, maile i ca\u0142a masa innych pierd\u00f3\u0142. W wielu z nich grzeba\u0142em pierwszy raz w \u017cyciu. Ale chyba dogrzeba\u0142em si\u0119 do wszystkiego co trzeba, bo wygl\u0105da na \u015bmigaj\u0105ce \u015blicznie.\u003C\/p\u003E\n\u003Cp\u003EWreszcie mog\u0119 deployowa\u0107 po ludzku, z gitem i composerem, pisa\u0107 w nowszej wersji PHP, skonfigurowa\u0107 sobie, co mi si\u0119 tylko \u017cywnie podoba, nie robi\u0107 r\u0119cznie eksport\u00f3w rozk\u0142adu na \u003Ca href=\u0022http:\/\/bus.avris.it\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E Busa\u003C\/a\u003E, kt\u00f3remu nie starcza\u0142o pami\u0119ci na wsp\u00f3\u0142dzielonym hostingu... Miodzio! :3\u003C\/p\u003E","words":112,"readTime":null,"lang":"pl"}}},"blog\/technology\/czysty-kod-w-praktyce-autoloader-w-php":{"key":"blog\/technology\/czysty-kod-w-praktyce-autoloader-w-php","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:30+02:00","publishedAt":"2015-04-06T19:40:00+02:00","group":null,"category":"blog","subcategory":"technology","slug":"czysty-kod-w-praktyce-autoloader-w-php"},"content":{"pl":{"slug":"czysty-kod-w-praktyce-autoloader-w-php","title":"\u201cCzysty kod\u201d w praktyce \u2013 Autoloader w PHP","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022328\u0022 height=\u0022467\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAkCAYAAAB8DZEQAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHFUlEQVRIibWWy49dRxHGf9Xd55z7vnfGM\/Z4cCY2xA62ZBCWsyCOxQKRVaygOJHYskAsEH8AOzbAEikII3ZsvGHBBqRIYYEwSBYQSHBisLHj1\/jaM+N53vd5dBeLM7Fn\/AhCiE86On1Kp+urqq6uKnHfeFt1bQkzfxB\/7zbUWojPkFobTSpgE0gzUAURGG5Sfe8c4jPqtRpplpEXBc4YojgiiWI2+30qSUy6vc\/p\/euEpEkoBDnyEhiLOodmOYigkxSptAihJJHWDNmRV4mv\/IajR16gWqmQZhmD0Yg7i3c5+\/oZ7i8tAdC9v8Sly\/9A3LfPq8Yt1FoIigSPisEYwYtgfMCrlp4AxhjwGebBDbQUIKqAAApphsQRr37xAFfvbXH7+m2cDlK0mqG2VEgcEbWraJbjJxmSROyzMC4C3lqsKhEVsr2HUe8ZiAFguuKw1qJZjrOGcXMGpnr4juDoLyNBMTN7UVNu8MMx6gNUYqoGXOyYqQpYQ+6VucQyzDz9UcqgPwExNCJHM7aMKxH1imNrbcDKMMM0azgabUJrD7VKzELFEDYHdG3MkdkW66mnHRuSakwalPupJzfKnSB4DCGK6DSF4ANDHxjnQjCG9VFBOslx1hJZg1NiJMuZiuvkvTG16RbzCHFkmbEGDcok8wRnEQVnDWmAQoSaM4wzaMSOXh4woUzAQVZgnaXICsQ5XNxukC4u0vU59dlpkt6EOLIspgGngbo15AK9AM0kouospvBsjQscSiQQO0MQgxhhmHmMCM5ZPKCbE0x26QLR84eQ6SmGmccHJbOWPARGQRn7gPdKoeBDQADvA5rm6CRHjGGEYK2h4gyRAQ0BP5ogaY5WEwyHThKiGIwgkxStJhRFwOcFVaCKoqoQFFXwWUHIPSioM6gRYiDNCjaGGUXmCapIHKGALq\/iSDOCgq71kHaDce6ZFaVai7F5wUaudKoRFcBZYeADMcpsxZIAq5knUmXWCRpgrLA5yrGRo3AG027iIEdvfowe+hzGe4os455XNPEYlCCG8SiDOEJ9gYiAArkHVQRY8xAmGRoUC9gkIss9OhzB+jqOtbtw9BSSe0LwSF5AFCFrGzDV5qUDDb51aoEPV0a8MFOjGlsu3esTGeFQp8LaOGc4zjn92Sku3+vxzrV1vnKow48u3EGLgOl0MCR1zGgC1sBwBJ1WaWnICCFwtO04\/5cuP7l4l6FX3v79LU4caNFJLO2q43K3x7kLN7m9PuIHv7vFD197kSvdHloENMvQfh\/HaJNggdX7aKONpiniU3ARMpqg6YTaMOd5zZm2cHV9zB9vbPCvpT7v3drg3Nlj\/LO7hRQFk5VNLl9b5td\/uIqiUIxhsoWVhRPf5+b7EEUYiSFLYfVOWeKDZ2VpjTMn9rPQdARjaPiCq0s9ltf6vPGFOcIk5d3371DHs7K6ii0mXOmu47Mhpr8K6QZijn9dw\/Jt5NhpJICONiEdwtxhJB2BceAq6KQPU\/NQiaAINOtVvnzyGNPTbaxA4hQxMXc\/vk67EfPLn\/4Mam2kGOL0yp9gahb92ztoow3DHtRbsLaI5ilUalDkpYd79kEI4BxUEw6ffYVrf77I9NwcqRUa07Nc+tUvePnNN9H71yF4KFIcGmB9pewXw37ZEwY9SnzSJ7ax9eDhUmf3cO3Cb5man2d6bh93\/v5Xqo0Gh459HmdtaSyAAZFon6rKkwrLlrRDtqMxAcY52rN7sVHEzMIC436fraUlOvPzrC0u0l9dLXeL4ngm5Bnf5bsSOb526ksYEbZ6PUyzw6AV0W61eBApFx+sPtzinlSyG85arBVAMEYIIeCDUqtVGA4GLCx8hjxPmZmZpttdYmZPh\/F4vFvH7tA8idOvnOS73\/km\/X4fYyzLy8t8+NEVTp9+mU67Sbfb5fjxo9RrVfI854MPPqLeqLNT7\/aZPDtoRgwilJMKoFpWY4SyjsHDcUmAELSsvqqAIvLQk6cdfGlJUN02KOyQCw+ZdkXi8SQp9bptf0BMeQeeCN3TyXe\/H5fvXpfjyVffglOvlWS6bYHuXPPoe6d818Nj60eelWey8CJsrUJrqrSgUod0BHkOSRWyCRhTeitSPiGAi8B72HgAvU2eBhHdJgnC1FST2ZkOaZpRr1VJ07Ts5aokSYL3Aecsw9GYdqvB+kaPpeW1MpgiqAaedka7SA4e3M\/CgX0kSYwq5HlOUXji2JHEMS5yDPpDKtUKRVEwmUyo1+uEEOh2V5ikOSLC4t0lsrx4uidiSgOsNYRQDlDGSJlEqrjI4QtP0IBsW6zbqWutoch9OT+H3cnwiORT7sn\/CpFPsus\/\/\/pfK3\/uwH7arSbwkGR38XsyFR8n\/JSUFYsA3\/vxOc688TrwRO161iXbiadfOADiGsw8h6YD3j3\/c65fu1FSm3huu5\/8fyCi\/BsCrKXMv6hFYQAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022328\u0022 height=\u0022467\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EKsi\u0105\u017cka Roberta C. Martina \u003Ca href=\u0022http:\/\/helion.pl\/ksiazki\/czysty-kod-podrecznik-dobrego-programisty-robert-c-martin,czykov.htm\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E \u201cCzysty kod\u201d\u003C\/a\u003E bije rekordy sprzeda\u017cy w\u015br\u00f3d pozycji dotycz\u0105cych szeroko poj\u0119tej informatyki. Wstyd wi\u0119c \u017cebym jej nie przeczyta\u0142, no nie? I zdecydowanie polecam j\u0105 ka\u017cdemu programi\u015bcie, kt\u00f3ry chcia\u0142by by\u0107 jak najlepszy w tym, co robi.\u003C\/p\u003E\n\u003Cp\u003EChcia\u0142bym tutaj pokaza\u0107 na konkretnym fragmencie kodu, jak wiele mo\u017ce zmieni\u0107 stosowanie si\u0119 do zasad przedstawionych przez Martina. Na przyk\u0142adzie autoloadera.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022328\u0022 height=\u0022467\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAkCAYAAAB8DZEQAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAHFUlEQVRIibWWy49dRxHGf9Xd55z7vnfGM\/Z4cCY2xA62ZBCWsyCOxQKRVaygOJHYskAsEH8AOzbAEikII3ZsvGHBBqRIYYEwSBYQSHBisLHj1\/jaM+N53vd5dBeLM7Fn\/AhCiE86On1Kp+urqq6uKnHfeFt1bQkzfxB\/7zbUWojPkFobTSpgE0gzUAURGG5Sfe8c4jPqtRpplpEXBc4YojgiiWI2+30qSUy6vc\/p\/euEpEkoBDnyEhiLOodmOYigkxSptAihJJHWDNmRV4mv\/IajR16gWqmQZhmD0Yg7i3c5+\/oZ7i8tAdC9v8Sly\/9A3LfPq8Yt1FoIigSPisEYwYtgfMCrlp4AxhjwGebBDbQUIKqAAApphsQRr37xAFfvbXH7+m2cDlK0mqG2VEgcEbWraJbjJxmSROyzMC4C3lqsKhEVsr2HUe8ZiAFguuKw1qJZjrOGcXMGpnr4juDoLyNBMTN7UVNu8MMx6gNUYqoGXOyYqQpYQ+6VucQyzDz9UcqgPwExNCJHM7aMKxH1imNrbcDKMMM0azgabUJrD7VKzELFEDYHdG3MkdkW66mnHRuSakwalPupJzfKnSB4DCGK6DSF4ANDHxjnQjCG9VFBOslx1hJZg1NiJMuZiuvkvTG16RbzCHFkmbEGDcok8wRnEQVnDWmAQoSaM4wzaMSOXh4woUzAQVZgnaXICsQ5XNxukC4u0vU59dlpkt6EOLIspgGngbo15AK9AM0kouospvBsjQscSiQQO0MQgxhhmHmMCM5ZPKCbE0x26QLR84eQ6SmGmccHJbOWPARGQRn7gPdKoeBDQADvA5rm6CRHjGGEYK2h4gyRAQ0BP5ogaY5WEwyHThKiGIwgkxStJhRFwOcFVaCKoqoQFFXwWUHIPSioM6gRYiDNCjaGGUXmCapIHKGALq\/iSDOCgq71kHaDce6ZFaVai7F5wUaudKoRFcBZYeADMcpsxZIAq5knUmXWCRpgrLA5yrGRo3AG027iIEdvfowe+hzGe4os455XNPEYlCCG8SiDOEJ9gYiAArkHVQRY8xAmGRoUC9gkIss9OhzB+jqOtbtw9BSSe0LwSF5AFCFrGzDV5qUDDb51aoEPV0a8MFOjGlsu3esTGeFQp8LaOGc4zjn92Sku3+vxzrV1vnKow48u3EGLgOl0MCR1zGgC1sBwBJ1WaWnICCFwtO04\/5cuP7l4l6FX3v79LU4caNFJLO2q43K3x7kLN7m9PuIHv7vFD197kSvdHloENMvQfh\/HaJNggdX7aKONpiniU3ARMpqg6YTaMOd5zZm2cHV9zB9vbPCvpT7v3drg3Nlj\/LO7hRQFk5VNLl9b5td\/uIqiUIxhsoWVhRPf5+b7EEUYiSFLYfVOWeKDZ2VpjTMn9rPQdARjaPiCq0s9ltf6vPGFOcIk5d3371DHs7K6ii0mXOmu47Mhpr8K6QZijn9dw\/Jt5NhpJICONiEdwtxhJB2BceAq6KQPU\/NQiaAINOtVvnzyGNPTbaxA4hQxMXc\/vk67EfPLn\/4Mam2kGOL0yp9gahb92ztoow3DHtRbsLaI5ilUalDkpYd79kEI4BxUEw6ffYVrf77I9NwcqRUa07Nc+tUvePnNN9H71yF4KFIcGmB9pewXw37ZEwY9SnzSJ7ax9eDhUmf3cO3Cb5man2d6bh93\/v5Xqo0Gh459HmdtaSyAAZFon6rKkwrLlrRDtqMxAcY52rN7sVHEzMIC436fraUlOvPzrC0u0l9dLXeL4ngm5Bnf5bsSOb526ksYEbZ6PUyzw6AV0W61eBApFx+sPtzinlSyG85arBVAMEYIIeCDUqtVGA4GLCx8hjxPmZmZpttdYmZPh\/F4vFvH7tA8idOvnOS73\/km\/X4fYyzLy8t8+NEVTp9+mU67Sbfb5fjxo9RrVfI854MPPqLeqLNT7\/aZPDtoRgwilJMKoFpWY4SyjsHDcUmAELSsvqqAIvLQk6cdfGlJUN02KOyQCw+ZdkXi8SQp9bptf0BMeQeeCN3TyXe\/H5fvXpfjyVffglOvlWS6bYHuXPPoe6d818Nj60eelWey8CJsrUJrqrSgUod0BHkOSRWyCRhTeitSPiGAi8B72HgAvU2eBhHdJgnC1FST2ZkOaZpRr1VJ07Ts5aokSYL3Aecsw9GYdqvB+kaPpeW1MpgiqAaedka7SA4e3M\/CgX0kSYwq5HlOUXji2JHEMS5yDPpDKtUKRVEwmUyo1+uEEOh2V5ikOSLC4t0lsrx4uidiSgOsNYRQDlDGSJlEqrjI4QtP0IBsW6zbqWutoch9OT+H3cnwiORT7sn\/CpFPsus\/\/\/pfK3\/uwH7arSbwkGR38XsyFR8n\/JSUFYsA3\/vxOc688TrwRO161iXbiadfOADiGsw8h6YD3j3\/c65fu1FSm3huu5\/8fyCi\/BsCrKXMv6hFYQAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022328\u0022 height=\u0022467\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EKsi\u0105\u017cka Roberta C. Martina \u003Ca href=\u0022http:\/\/helion.pl\/ksiazki\/czysty-kod-podrecznik-dobrego-programisty-robert-c-martin,czykov.htm\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E \u201cCzysty kod\u201d\u003C\/a\u003E bije rekordy sprzeda\u017cy w\u015br\u00f3d pozycji dotycz\u0105cych szeroko poj\u0119tej informatyki. Wstyd wi\u0119c \u017cebym jej nie przeczyta\u0142, no nie? I zdecydowanie polecam j\u0105 ka\u017cdemu programi\u015bcie, kt\u00f3ry chcia\u0142by by\u0107 jak najlepszy w tym, co robi.\u003C\/p\u003E\n\u003Cp\u003EChcia\u0142bym tutaj pokaza\u0107 na konkretnym fragmencie kodu, jak wiele mo\u017ce zmieni\u0107 stosowanie si\u0119 do zasad przedstawionych przez Martina. Na przyk\u0142adzie autoloadera.\u003C\/p\u003E\n\u003Cp\u003ETylko najpierw kr\u00f3tki wst\u0119p, co to w og\u00f3le jest \u201cautoloader\u201d. Ot\u00f3\u017c: przy ka\u017cdym \u017c\u0105daniu do serwera uruchamiana jest nowa instancja interpretera PHP. Aby nie musia\u0142 on \u0142adowa\u0107 wszystkich plik\u00f3w .php obecnych w projekcie (a potrafi\u0105 ich by\u0107 tysi\u0105ce), lecz tylko te, z kt\u00f3rych akurat w tym \u017c\u0105daniu skorzysta, u\u017cywany jest mechanizm auto\u0142adowania. Dzia\u0142a to tak, \u017ce kiedy interpreter PHP trafi na u\u017cycie klasy, kt\u00f3rej nie zna, odpyta o ni\u0105 po kolei autoloadery, kt\u00f3re ma zarejestrowane. Je\u015bli kt\u00f3ry\u015b z nich zwr\u00f3ci true, oznacza to, \u017ce znalaz\u0142 on plik, w kt\u00f3rym dana klasa powinna si\u0119 znajdowa\u0107, i do\u0142\u0105czy\u0142 go. W przeciwnym razie interpreter pyta kolejnego autoloadera w kolejce, a dopiero je\u015bli wszystkie zawiod\u0105 \u2013 wyrzuca fatal error \u201cClass not found\u201d.\u003C\/p\u003E\n\u003Cp\u003ENapisa\u0142em sobie kiedy\u015b ma\u0142\u0105 klas\u0119 autoloadera, a raczej poprzerabia\u0142em jak\u0105\u015b znalezion\u0105 na necie. Wygl\u0105da\u0142a tak:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EAutoloader\u003C\/span\u003E \u003C\/span\u003E{\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $namespace;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $path;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $valid = \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003E__construct\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($namespace, $path = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\u0027\u003C\/span\u003E)\u003C\/span\u003E \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;namespace = $namespace;\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;path = $path;\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;valid = \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Eautoload\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($className)\u003C\/span\u003E \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ is this autoloader responsible for that particular class?\u003C\/span\u003E\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (substr($className,\u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E,strlen(\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;namespace)) === \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;namespace) {\n            $path = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;path . str_replace(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E,\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/\u0027\u003C\/span\u003E, ltrim( $className, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E)) . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027.php\u0027\u003C\/span\u003E;\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (file_exists($path)) {\n                \u003Cspan class=\u0022hljs-keyword\u0022\u003Erequire_once\u003C\/span\u003E($path);\n                \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E;\n            } \u003Cspan class=\u0022hljs-keyword\u0022\u003Eelse\u003C\/span\u003E {\n                \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ if not straightforward, maybe try \u0022common\u0022 class files\u003C\/span\u003E\n                \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ eg. \u0022Exceptions\u0022 for \u0022NotFoundException\u0022;\u003C\/span\u003E\n                $slashPos = strrpos($className,\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E,\u003Cspan class=\u0022hljs-number\u0022\u003E-1\u003C\/span\u003E) + \u003Cspan class=\u0022hljs-number\u0022\u003E1\u003C\/span\u003E;\n                $base = substr($className,\u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E,$slashPos);\n                $classPure = substr($className,$slashPos);\n                preg_match_all(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/(?:^|[A-Z])[a-z]+\/\u0027\u003C\/span\u003E,$classPure,$matches);\n                $path = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;path . str_replace(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E,\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/\u0027\u003C\/span\u003E, ltrim( $base . end($matches[\u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E]) . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027s\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E)) . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027.php\u0027\u003C\/span\u003E;\n                \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (file_exists($path)) {\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Erequire_once\u003C\/span\u003E($path);\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E;\n                } \u003Cspan class=\u0022hljs-keyword\u0022\u003Eelse\u003C\/span\u003E {\n                    \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E; \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ damn, maybe other autoloaders will find something\u003C\/span\u003E\n                }\n            }\n        } \u003Cspan class=\u0022hljs-keyword\u0022\u003Eelse\u003C\/span\u003E {\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E; \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/ no, let the other autoloaders handle that\u003C\/span\u003E\n        }\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Eregister\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;valid) {\n            spl_autoload_register(\u003Cspan class=\u0022hljs-keyword\u0022\u003Earray\u003C\/span\u003E(\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027autoload\u0027\u003C\/span\u003E));\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;valid = \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E; \u003Cspan class=\u0022hljs-comment\u0022\u003E\/\/in order not to register it twice\u003C\/span\u003E\n        }\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EPrzyk\u0142adowe u\u017cycie mojego autoloadera wygl\u0105da nast\u0119puj\u0105co:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n$autoloader = \u003Cspan class=\u0022hljs-keyword\u0022\u003Enew\u003C\/span\u003E Autoloader(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027Micrus\\\\\u0027\u003C\/span\u003E, \u003Cspan class=\u0022hljs-keyword\u0022\u003E__DIR__\u003C\/span\u003E . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/vendor\/\u0027\u003C\/span\u003E);\n$autoloader-\u0026gt;register();\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ETworzymy nowy autoloader maj\u0105cy obs\u0142ugiwa\u0107 wszystkie klasy w przestrzeni nazw \u201cMicrus\u201d i szukaj\u0105cy ich w katalogu \u201cvendor\u201d, gdzie klasy s\u0105 umieszczone w podfolderach zgodnie ze swoj\u0105 przestrzeni\u0105 nazw. Oznacza to, \u017ce je\u015bli gdzie\u015b w kodzie b\u0119dziemy chcieli stworzy\u0107 nowy obiekt klasy \u003Ccode\u003EMicrus\\Foo\\Bar\\MyClass\u003C\/code\u003E, to powinna si\u0119 ona znajdowa\u0107 w pliku \u003Ccode\u003E\/vendor\/Micrus\/Foo\/Bar\/MyClass.php\u003C\/code\u003E.\u003C\/p\u003E\n\u003Cp\u003EDo tego dochodzi jeszcze pewien ma\u0142y feature (niezgodny z \u003Ca href=\u0022http:\/\/www.php-fig.org\/psr\/psr-4\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E PSR-4\u003C\/a\u003E). Ot\u00f3\u017c je\u015bli chcemy zgrupowa\u0107 kilka podobnych klas w jednym pliku, mo\u017cemy zrobi\u0107 to w bardzo prosty spos\u00f3b. Mamy na przyk\u0142ad klasy wyj\u0105tk\u00f3w, kt\u00f3re potrafi\u0105 by\u0107 naprawd\u0119 malutkie i czasem niewygodnie jest rozbija\u0107 je na osobne pliki. Nazywamy je wszystkie u\u017cywaj\u0105c \u003Ca href=\u0022http:\/\/pl.wikipedia.org\/wiki\/PascalCase\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Csvg class=\u0022icon\u0022\u003E\u003Cuse xlink:href=\u0022#light-link\u0022\u003E\u003C\/use\u003E\u003C\/svg\u003E PascalCase\u003C\/a\u003E z \u201cException\u201d jako ostatnim s\u0142owem i umieszczamy w pliku \u003Ccode\u003EExceptions.php\u003C\/code\u003E (z dodatkowym -s). M\u00f3j autoloader obs\u0142u\u017cy taki przypadek.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\u003Cspan class=\u0022hljs-keyword\u0022\u003Enamespace\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EMicrus\u003C\/span\u003E;\n\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EConfigFileException\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EException\u003C\/span\u003E \u003C\/span\u003E{ }\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003ENotFoundException\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EException\u003C\/span\u003E \u003C\/span\u003E{ \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprotected\u003C\/span\u003E $code = \u003Cspan class=\u0022hljs-number\u0022\u003E404\u003C\/span\u003E; }\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003ECacheException\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EException\u003C\/span\u003E \u003C\/span\u003E{ }\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003ERouteNotFoundException\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EException\u003C\/span\u003E \u003C\/span\u003E{ \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprotected\u003C\/span\u003E $code = \u003Cspan class=\u0022hljs-number\u0022\u003E404\u003C\/span\u003E; }\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EInvalidArgumentException\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EException\u003C\/span\u003E \u003C\/span\u003E{ }\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EUnauthorisedException\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EException\u003C\/span\u003E \u003C\/span\u003E{ \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprotected\u003C\/span\u003E $code = \u003Cspan class=\u0022hljs-number\u0022\u003E403\u003C\/span\u003E; }\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EArgvException\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EException\u003C\/span\u003E \u003C\/span\u003E{ }\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EDatabaseException\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Eextends\u003C\/span\u003E \\\u003Cspan class=\u0022hljs-title\u0022\u003EException\u003C\/span\u003E \u003C\/span\u003E{ }\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EKod autoloadera nie wygl\u0105da \u017ale (a przynajmniej widzia\u0142em gorsze). Ale pierwsze spojrzenie na metod\u0119 \u201cautoload\u201d potrafi przerazi\u0107. Jest okropnie nieczytelna, mimo \u017ce komentarze obja\u015bniaj\u0105 jej dzia\u0142anie. Potrzeba troch\u0119 wysi\u0142ku, \u017ceby dowiedzie\u0107 si\u0119, co konkretnie robi.\u003C\/p\u003E\n\u003Cp\u003EWszyscy ucz\u0105, zeby pisa\u0107 komentarze do kodu. Du\u017co komentarzy. Nawet je\u015bli wydaje ci si\u0119 to bezsensowne, bo przecie\u017c rozumiesz, co napisa\u0142e\u015b, to musisz bra\u0107 pod uwag\u0119, \u017ce je\u015bli kod trafi w r\u0119ce kogo innego, albo i ty sam zajrzysz do niego rok czy dwa p\u00f3\u017aniej, to rozszyfrowanie go mo\u017ce ju\u017c nie by\u0107 takie proste.\u003C\/p\u003E\n\u003Cp\u003EMartin natomiast uczy czego innego. Dobry kod wymaga tylko znikomej liczby komentarzy. Je\u015bli ich potrzebujesz, by zrozumie\u0107, co si\u0119 dzieje, znaczy \u017ce kod jest nieczytelny. No i komentarze s\u0105 w pewnym sensie \u003Cem\u003Eniebezpieczne\u003C\/em\u003E: zapominamy je pisa\u0107, zapominamy je aktualizowa\u0107, boimy si\u0119 je usuwa\u0107, szybko trac\u0105 na wa\u017cno\u015bci...\u003C\/p\u003E\n\u003Cp\u003ESp\u00f3jrzcie na kod autoloadera po serii poprawek:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022hljs php border\u0022\u003E\u003Cspan class=\u0022hljs-meta\u0022\u003E\u0026lt;?php\u003C\/span\u003E\n\n\u003Cspan class=\u0022hljs-class\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Eclass\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EAutoloader\u003C\/span\u003E\n\u003C\/span\u003E{\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $namespace;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $path;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E $registered = \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E;\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003E__construct\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($namespace, $path = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027.\u0027\u003C\/span\u003E)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;namespace = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;addEndingSlash($namespace, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E);\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;path = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;addEndingSlash($path, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/\u0027\u003C\/span\u003E);\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EaddEndingSlash\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($word, $slash = \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/\u0027\u003C\/span\u003E)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $lastCharacter = substr($word, \u003Cspan class=\u0022hljs-number\u0022\u003E-1\u003C\/span\u003E);\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $lastCharacter == $slash ? $word : $word . $slash;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Eautoload\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($className)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;isNotResponsibleForClass($className)) {\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E;\n        }\n\n        $path = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;classNameToFilePath($className);\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (file_exists($path)) {\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003Erequire_once\u003C\/span\u003E($path);\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E;\n        }\n\n        $groupName = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;classNameToGroupName($className);\n        $path = \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;classNameToFilePath($groupName);\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (file_exists($path)) {\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003Erequire_once\u003C\/span\u003E($path);\n            \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E;\n        }\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EisNotResponsibleForClass\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($className)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E substr($className, \u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E, strlen(\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;namespace)) !== \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;namespace;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EclassNameToFilePath\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($className)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;path . str_replace(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E,\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/\u0027\u003C\/span\u003E, ltrim( $className, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E)) . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027.php\u0027\u003C\/span\u003E;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Eprivate\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003EclassNameToGroupName\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E($className)\u003C\/span\u003E\n    \u003C\/span\u003E{\n        $slashPos = strrpos($className,\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\\\\\u0027\u003C\/span\u003E,\u003Cspan class=\u0022hljs-number\u0022\u003E-1\u003C\/span\u003E) + \u003Cspan class=\u0022hljs-number\u0022\u003E1\u003C\/span\u003E;\n        $base = substr($className,\u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E,$slashPos);\n        $classPure = substr($className,$slashPos);\n        preg_match_all(\u003Cspan class=\u0022hljs-string\u0022\u003E\u0027\/(?:^|[A-Z])[a-z]+\/\u0027\u003C\/span\u003E,$classPure,$matches);\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E $base . end($matches[\u003Cspan class=\u0022hljs-number\u0022\u003E0\u003C\/span\u003E]) . \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027s\u0027\u003C\/span\u003E;\n    }\n\n    \u003Cspan class=\u0022hljs-keyword\u0022\u003Epublic\u003C\/span\u003E \u003Cspan class=\u0022hljs-function\u0022\u003E\u003Cspan class=\u0022hljs-keyword\u0022\u003Efunction\u003C\/span\u003E \u003Cspan class=\u0022hljs-title\u0022\u003Eregister\u003C\/span\u003E\u003Cspan class=\u0022hljs-params\u0022\u003E()\u003C\/span\u003E\n    \u003C\/span\u003E{\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Eif\u003C\/span\u003E (\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;registered) { \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Efalse\u003C\/span\u003E; }\n        spl_autoload_register(\u003Cspan class=\u0022hljs-keyword\u0022\u003Earray\u003C\/span\u003E(\u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E, \u003Cspan class=\u0022hljs-string\u0022\u003E\u0027autoload\u0027\u003C\/span\u003E));\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003E$this\u003C\/span\u003E-\u0026gt;registered = \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E;\n\n        \u003Cspan class=\u0022hljs-keyword\u0022\u003Ereturn\u003C\/span\u003E \u003Cspan class=\u0022hljs-keyword\u0022\u003Etrue\u003C\/span\u003E;\n    }\n}\n\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EWystarczy zerkn\u0105\u0107 na funkcj\u0119 autoload, \u017ceby wiedzie\u0107, co robi. Ba, nawet nie trzeba zna\u0107 \u017cadnego j\u0119zyka programowania! To\u017c to niemal czysty angielski: \u201cif this is not responsible for class \u2018class name\u2019, return false\u201d. Nie mamy poj\u0119cia \u003Cem\u003Ejak\u003C\/em\u003E funkcja to robi, ale wida\u0107 jak na d\u0142oni \u003Cem\u003Eco\u003C\/em\u003E takiego wykonuje.\u003C\/p\u003E\n\u003Cp\u003E\u003Cem\u003E\u201cJak\u201d\u003C\/em\u003E natomiast znajduje si\u0119 w osobnych, prywatnych metodach. Rozdzielili\u015bmy zatem od siebie r\u00f3\u017cne \u003Cem\u003Ewarstwy abstrakcji\u003C\/em\u003E, kt\u00f3re wcze\u015bniej by\u0142y zlane w jedn\u0105, brzydk\u0105 funkcj\u0119. Bo je\u015bli kto\u015b zajrzy do naszego kodu, to najprawdopodobniej nie b\u0119dzie potrzebowa\u0142 znajomo\u015bci wszystkiego, co tam naskrobali\u015bmy, lecz przyjdzie z jakim\u015b jednym konkretnym zadaniem: \u201cogarn\u0105\u0107 jak mniej wi\u0119cej dzia\u0142a ten autoloader\u201d, \u201czmieni\u0107 format nazwy \u2018zbiorowego pliku\u2019\u201d, \u201czmieni\u0107 kolejno\u015b\u0107 sprawdzania potencjalnych plik\u00f3w\u201d itp. I dlatego rozdzielenie warstw abstrakcji tak bardzo upro\u015bci mu prac\u0119.\u003C\/p\u003E\n\u003Cp\u003ENa pierwszy rzut oka wydawa\u0142oby si\u0119 sensowniejsze stworzenie metody isResponsibleForClass zamiast jej negacji \u2013 isNotResponsibleForClass. Kto normalny pyta\u0142by klas\u0119, czy \u003Cem\u003Enie jest\u003C\/em\u003E za co\u015b odpowiedzialna? Odpowied\u017a \u201ctak, nie jestem\u201d tylko niepotrzebnie by wszystko zagmatwa\u0142a, prawda? No w\u0142a\u015bnie niekoniecznie. U\u017cycie tej metody jest tylko jedno i sprowadza si\u0119 do \u201cje\u015bli nie jeste\u015b za to odpowiedzialna, to daj sobie spok\u00f3j\u201d. Zapisanie tego warunku jako \u003Ccode\u003Eif (!$this-\u0026gt;isResponsibleForClass($className))\u003C\/code\u003E zmniejsza czytelno\u015b\u0107, poniewa\u017c tego wykrzyknika prawie nie wida\u0107. Zawsze zapominamy o wykrzyknikach...\u003C\/p\u003E\n\u003Cp\u003EZanegowanie dopad\u0142o te\u017c zmienn\u0105 \u003Ccode\u003Evalid\u003C\/code\u003E. Zawsze gdy j\u0105 widzia\u0142em, musia\u0142em si\u0119 zastanowi\u0107, po co ona komu. Kiedy niby autoloader staje si\u0119 \u201cinvalid\u201d? A chodzi przecie\u017c o to, \u017ce autoloader mo\u017ce by\u0107 albo \u003Cem\u003Ejeszcze nie zarejestrowany\u003C\/em\u003E, albo \u003Cem\u003Eju\u017c zarejestrowany\u003C\/em\u003E. Czemu wi\u0119c nie zmieni\u0107 nazwy tej w\u0142a\u015bciwo\u015bci na \u003Ccode\u003Eregistered\u003C\/code\u003E? Teraz metoda \u003Ccode\u003Eregister()\u003C\/code\u003E staje si\u0119 bardzo czytelna: \u201cje\u015bli jeste\u015b zarejestrowany, to ju\u017c wi\u0119cej si\u0119 nie rejestruj\u201d.\u003C\/p\u003E\n\u003Cp\u003EBazowy namespace powinien ko\u0144czy\u0107 si\u0119 backslashem, aby unikn\u0105\u0107 takich sytuacji, gdy chcemy na przyk\u0142ad przechwyci\u0107 klasy \u201cJava\/...\u201d, a zgarniemy tak\u017ce i \u201cJavascript\/...\u201d. Dla zachowania jednorodno\u015bci, to samo powinno dotyczy\u0107 drugiego parametru konstruktora (\u015bcie\u017cki do folderu). Ale po co cz\u0142owiekowi, kt\u00f3ry chce u\u017cy\u0107 naszego autoloadera, kaza\u0107 si\u0119 zastanawia\u0107 nad tym, czy wymagamy slasha czy nie? Mo\u017ce lepiej po prostu sprawd\u017amy, czy slash jest na ko\u0144cu parametru, i doklejmy go, je\u015bli nie? Dodanie metody \u003Ccode\u003EaddEndingSlash\u003C\/code\u003E sprawi\u0142o, \u017ce nie tylko sama klasa jest prostsza, ale tak\u017ce i jej u\u017cycie na zewn\u0105trz.\u003C\/p\u003E\n\u003Cp\u003ETym oto sposobem w nowym kodzie jest zdecydowanie mniej zagnie\u017cd\u017ce\u0144, metody s\u0105 kr\u00f3tsze, a ka\u017cda zajmuje si\u0119 dok\u0142adnie jedn\u0105 rzecz\u0105 na jednym poziomie abstrakcji.\u003C\/p\u003E\n\u003Cp\u003EI jak? Kt\u00f3r\u0105 wersj\u0119 wolicie? Kt\u00f3ra nie marnuje czasu programisty pr\u00f3buj\u0105cego j\u0105 zrozumie\u0107?\u003C\/p\u003E\n\u003Cp\u003E\u201cCzysty kod\u201d warto przeczyta\u0107, warto do niego wraca\u0107 i \u2013 przede wszystkim \u2013 warto stosowa\u0107 si\u0119 do zawartych w nim rad.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["czytelno\u015b\u0107","informatyka","kod","ksi\u0105\u017cka","php","programowanie"],"hasMore":true,"image":"https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022341.70731707317\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EKsi\u0105\u017cka Roberta C. Martina \u003Ca href=\u0022http:\/\/helion.pl\/ksiazki\/czysty-kod-podrecznik-dobrego-programisty-robert-c-martin,czykov.htm\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E \u201cCzysty kod\u201d\u003C\/a\u003E bije rekordy sprzeda\u017cy w\u015br\u00f3d pozycji dotycz\u0105cych szeroko poj\u0119tej informatyki. Wstyd wi\u0119c \u017cebym jej nie przeczyta\u0142, no nie? I zdecydowanie polecam j\u0105 ka\u017cdemu programi\u015bcie, kt\u00f3ry chcia\u0142by by\u0107 jak najlepszy w tym, co robi.\u003C\/p\u003E\n\u003Cp\u003EChcia\u0142bym tutaj pokaza\u0107 na konkretnym fragmencie kodu, jak wiele mo\u017ce zmieni\u0107 stosowanie si\u0119 do zasad przedstawionych przez Martina. Na przyk\u0142adzie autoloadera.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/czysty-kod-w-praktyce-autoloader-w-php_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022341.70731707317\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EKsi\u0105\u017cka Roberta C. Martina \u003Ca href=\u0022http:\/\/helion.pl\/ksiazki\/czysty-kod-podrecznik-dobrego-programisty-robert-c-martin,czykov.htm\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E \u201cCzysty kod\u201d\u003C\/a\u003E bije rekordy sprzeda\u017cy w\u015br\u00f3d pozycji dotycz\u0105cych szeroko poj\u0119tej informatyki. Wstyd wi\u0119c \u017cebym jej nie przeczyta\u0142, no nie? I zdecydowanie polecam j\u0105 ka\u017cdemu programi\u015bcie, kt\u00f3ry chcia\u0142by by\u0107 jak najlepszy w tym, co robi.\u003C\/p\u003E\n\u003Cp\u003EChcia\u0142bym tutaj pokaza\u0107 na konkretnym fragmencie kodu, jak wiele mo\u017ce zmieni\u0107 stosowanie si\u0119 do zasad przedstawionych przez Martina. Na przyk\u0142adzie autoloadera.\u003C\/p\u003E\n\u003Cp\u003ETylko najpierw kr\u00f3tki wst\u0119p, co to w og\u00f3le jest \u201cautoloader\u201d. Ot\u00f3\u017c: przy ka\u017cdym \u017c\u0105daniu do serwera uruchamiana jest nowa instancja interpretera PHP. Aby nie musia\u0142 on \u0142adowa\u0107 wszystkich plik\u00f3w .php obecnych w projekcie (a potrafi\u0105 ich by\u0107 tysi\u0105ce), lecz tylko te, z kt\u00f3rych akurat w tym \u017c\u0105daniu skorzysta, u\u017cywany jest mechanizm auto\u0142adowania. Dzia\u0142a to tak, \u017ce kiedy interpreter PHP trafi na u\u017cycie klasy, kt\u00f3rej nie zna, odpyta o ni\u0105 po kolei autoloadery, kt\u00f3re ma zarejestrowane. Je\u015bli kt\u00f3ry\u015b z nich zwr\u00f3ci true, oznacza to, \u017ce znalaz\u0142 on plik, w kt\u00f3rym dana klasa powinna si\u0119 znajdowa\u0107, i do\u0142\u0105czy\u0142 go. W przeciwnym razie interpreter pyta kolejnego autoloadera w kolejce, a dopiero je\u015bli wszystkie zawiod\u0105 \u2013 wyrzuca fatal error \u201cClass not found\u201d.\u003C\/p\u003E\n\u003Cp\u003ENapisa\u0142em sobie kiedy\u015b ma\u0142\u0105 klas\u0119 autoloadera, a raczej poprzerabia\u0142em jak\u0105\u015b znalezion\u0105 na necie. Wygl\u0105da\u0142a tak:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E\u0026lt;?php\n\nclass Autoloader {\n\n    private $namespace;\n\n    private $path;\n\n    private $valid = false;\n\n    public function __construct($namespace, $path = \u0027\u0027) {\n        $this-\u0026gt;namespace = $namespace;\n        $this-\u0026gt;path = $path;\n        $this-\u0026gt;valid = true;\n    }\n\n    public function autoload($className) {\n        \/\/ is this autoloader responsible for that particular class?\n        if (substr($className,0,strlen($this-\u0026gt;namespace)) === $this-\u0026gt;namespace) {\n            $path = $this-\u0026gt;path . str_replace(\u0027\\\\\u0027,\u0027\/\u0027, ltrim( $className, \u0027\\\\\u0027)) . \u0027.php\u0027;\n            if (file_exists($path)) {\n                require_once($path);\n                return true;\n            } else {\n                \/\/ if not straightforward, maybe try \u0022common\u0022 class files\n                \/\/ eg. \u0022Exceptions\u0022 for \u0022NotFoundException\u0022;\n                $slashPos = strrpos($className,\u0027\\\\\u0027,-1) + 1;\n                $base = substr($className,0,$slashPos);\n                $classPure = substr($className,$slashPos);\n                preg_match_all(\u0027\/(?:^|[A-Z])[a-z]+\/\u0027,$classPure,$matches);\n                $path = $this-\u0026gt;path . str_replace(\u0027\\\\\u0027,\u0027\/\u0027, ltrim( $base . end($matches[0]) . \u0027s\u0027, \u0027\\\\\u0027)) . \u0027.php\u0027;\n                if (file_exists($path)) {\n                    require_once($path);\n                    return true;\n                } else {\n                    return false; \/\/ damn, maybe other autoloaders will find something\n                }\n            }\n        } else {\n            return false; \/\/ no, let the other autoloaders handle that\n        }\n    }\n\n    public function register() {\n        if ($this-\u0026gt;valid) {\n            spl_autoload_register(array($this, \u0027autoload\u0027));\n            $this-\u0026gt;valid = false; \/\/in order not to register it twice\n        }\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EPrzyk\u0142adowe u\u017cycie mojego autoloadera wygl\u0105da nast\u0119puj\u0105co:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E\u0026lt;?php\n$autoloader = new Autoloader(\u0027Micrus\\\\\u0027, __DIR__ . \u0027\/vendor\/\u0027);\n$autoloader-\u0026gt;register();\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003ETworzymy nowy autoloader maj\u0105cy obs\u0142ugiwa\u0107 wszystkie klasy w przestrzeni nazw \u201cMicrus\u201d i szukaj\u0105cy ich w katalogu \u201cvendor\u201d, gdzie klasy s\u0105 umieszczone w podfolderach zgodnie ze swoj\u0105 przestrzeni\u0105 nazw. Oznacza to, \u017ce je\u015bli gdzie\u015b w kodzie b\u0119dziemy chcieli stworzy\u0107 nowy obiekt klasy \u003Ccode\u003EMicrus\\Foo\\Bar\\MyClass\u003C\/code\u003E, to powinna si\u0119 ona znajdowa\u0107 w pliku \u003Ccode\u003E\/vendor\/Micrus\/Foo\/Bar\/MyClass.php\u003C\/code\u003E.\u003C\/p\u003E\n\u003Cp\u003EDo tego dochodzi jeszcze pewien ma\u0142y feature (niezgodny z \u003Ca href=\u0022http:\/\/www.php-fig.org\/psr\/psr-4\/\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E PSR-4\u003C\/a\u003E). Ot\u00f3\u017c je\u015bli chcemy zgrupowa\u0107 kilka podobnych klas w jednym pliku, mo\u017cemy zrobi\u0107 to w bardzo prosty spos\u00f3b. Mamy na przyk\u0142ad klasy wyj\u0105tk\u00f3w, kt\u00f3re potrafi\u0105 by\u0107 naprawd\u0119 malutkie i czasem niewygodnie jest rozbija\u0107 je na osobne pliki. Nazywamy je wszystkie u\u017cywaj\u0105c \u003Ca href=\u0022http:\/\/pl.wikipedia.org\/wiki\/PascalCase\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E PascalCase\u003C\/a\u003E z \u201cException\u201d jako ostatnim s\u0142owem i umieszczamy w pliku \u003Ccode\u003EExceptions.php\u003C\/code\u003E (z dodatkowym -s). M\u00f3j autoloader obs\u0142u\u017cy taki przypadek.\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\nnamespace Micrus;\n\nclass ConfigFileException extends \\Exception { }\nclass NotFoundException extends \\Exception { protected $code = 404; }\nclass CacheException extends \\Exception { }\nclass RouteNotFoundException extends \\Exception { protected $code = 404; }\nclass InvalidArgumentException extends \\Exception { }\nclass UnauthorisedException extends \\Exception { protected $code = 403; }\nclass ArgvException extends \\Exception { }\nclass DatabaseException extends \\Exception { }\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EKod autoloadera nie wygl\u0105da \u017ale (a przynajmniej widzia\u0142em gorsze). Ale pierwsze spojrzenie na metod\u0119 \u201cautoload\u201d potrafi przerazi\u0107. Jest okropnie nieczytelna, mimo \u017ce komentarze obja\u015bniaj\u0105 jej dzia\u0142anie. Potrzeba troch\u0119 wysi\u0142ku, \u017ceby dowiedzie\u0107 si\u0119, co konkretnie robi.\u003C\/p\u003E\n\u003Cp\u003EWszyscy ucz\u0105, zeby pisa\u0107 komentarze do kodu. Du\u017co komentarzy. Nawet je\u015bli wydaje ci si\u0119 to bezsensowne, bo przecie\u017c rozumiesz, co napisa\u0142e\u015b, to musisz bra\u0107 pod uwag\u0119, \u017ce je\u015bli kod trafi w r\u0119ce kogo innego, albo i ty sam zajrzysz do niego rok czy dwa p\u00f3\u017aniej, to rozszyfrowanie go mo\u017ce ju\u017c nie by\u0107 takie proste.\u003C\/p\u003E\n\u003Cp\u003EMartin natomiast uczy czego innego. Dobry kod wymaga tylko znikomej liczby komentarzy. Je\u015bli ich potrzebujesz, by zrozumie\u0107, co si\u0119 dzieje, znaczy \u017ce kod jest nieczytelny. No i komentarze s\u0105 w pewnym sensie \u003Cem\u003Eniebezpieczne\u003C\/em\u003E: zapominamy je pisa\u0107, zapominamy je aktualizowa\u0107, boimy si\u0119 je usuwa\u0107, szybko trac\u0105 na wa\u017cno\u015bci...\u003C\/p\u003E\n\u003Cp\u003ESp\u00f3jrzcie na kod autoloadera po serii poprawek:\u003C\/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-php\u0022\u003E\u0026lt;?php\n\nclass Autoloader\n{\n    private $namespace;\n\n    private $path;\n\n    private $registered = false;\n\n    public function __construct($namespace, $path = \u0027.\u0027)\n    {\n        $this-\u0026gt;namespace = $this-\u0026gt;addEndingSlash($namespace, \u0027\\\\\u0027);\n        $this-\u0026gt;path = $this-\u0026gt;addEndingSlash($path, \u0027\/\u0027);\n    }\n\n    private function addEndingSlash($word, $slash = \u0027\/\u0027)\n    {\n        $lastCharacter = substr($word, -1);\n        return $lastCharacter == $slash ? $word : $word . $slash;\n    }\n\n    public function autoload($className)\n    {\n        if ($this-\u0026gt;isNotResponsibleForClass($className)) {\n            return false;\n        }\n\n        $path = $this-\u0026gt;classNameToFilePath($className);\n        if (file_exists($path)) {\n            require_once($path);\n            return true;\n        }\n\n        $groupName = $this-\u0026gt;classNameToGroupName($className);\n        $path = $this-\u0026gt;classNameToFilePath($groupName);\n        if (file_exists($path)) {\n            require_once($path);\n            return true;\n        }\n\n        return false;\n    }\n\n    private function isNotResponsibleForClass($className)\n    {\n        return substr($className, 0, strlen($this-\u0026gt;namespace)) !== $this-\u0026gt;namespace;\n    }\n\n    private function classNameToFilePath($className)\n    {\n        return $this-\u0026gt;path . str_replace(\u0027\\\\\u0027,\u0027\/\u0027, ltrim( $className, \u0027\\\\\u0027)) . \u0027.php\u0027;\n    }\n\n    private function classNameToGroupName($className)\n    {\n        $slashPos = strrpos($className,\u0027\\\\\u0027,-1) + 1;\n        $base = substr($className,0,$slashPos);\n        $classPure = substr($className,$slashPos);\n        preg_match_all(\u0027\/(?:^|[A-Z])[a-z]+\/\u0027,$classPure,$matches);\n\n        return $base . end($matches[0]) . \u0027s\u0027;\n    }\n\n    public function register()\n    {\n        if ($this-\u0026gt;registered) { return false; }\n        spl_autoload_register(array($this, \u0027autoload\u0027));\n        $this-\u0026gt;registered = true;\n\n        return true;\n    }\n}\u003C\/code\u003E\u003C\/pre\u003E\n\u003Cp\u003EWystarczy zerkn\u0105\u0107 na funkcj\u0119 autoload, \u017ceby wiedzie\u0107, co robi. Ba, nawet nie trzeba zna\u0107 \u017cadnego j\u0119zyka programowania! To\u017c to niemal czysty angielski: \u201cif this is not responsible for class \u2018class name\u2019, return false\u201d. Nie mamy poj\u0119cia \u003Cem\u003Ejak\u003C\/em\u003E funkcja to robi, ale wida\u0107 jak na d\u0142oni \u003Cem\u003Eco\u003C\/em\u003E takiego wykonuje.\u003C\/p\u003E\n\u003Cp\u003E\u003Cem\u003E\u201cJak\u201d\u003C\/em\u003E natomiast znajduje si\u0119 w osobnych, prywatnych metodach. Rozdzielili\u015bmy zatem od siebie r\u00f3\u017cne \u003Cem\u003Ewarstwy abstrakcji\u003C\/em\u003E, kt\u00f3re wcze\u015bniej by\u0142y zlane w jedn\u0105, brzydk\u0105 funkcj\u0119. Bo je\u015bli kto\u015b zajrzy do naszego kodu, to najprawdopodobniej nie b\u0119dzie potrzebowa\u0142 znajomo\u015bci wszystkiego, co tam naskrobali\u015bmy, lecz przyjdzie z jakim\u015b jednym konkretnym zadaniem: \u201cogarn\u0105\u0107 jak mniej wi\u0119cej dzia\u0142a ten autoloader\u201d, \u201czmieni\u0107 format nazwy \u2018zbiorowego pliku\u2019\u201d, \u201czmieni\u0107 kolejno\u015b\u0107 sprawdzania potencjalnych plik\u00f3w\u201d itp. I dlatego rozdzielenie warstw abstrakcji tak bardzo upro\u015bci mu prac\u0119.\u003C\/p\u003E\n\u003Cp\u003ENa pierwszy rzut oka wydawa\u0142oby si\u0119 sensowniejsze stworzenie metody isResponsibleForClass zamiast jej negacji \u2013 isNotResponsibleForClass. Kto normalny pyta\u0142by klas\u0119, czy \u003Cem\u003Enie jest\u003C\/em\u003E za co\u015b odpowiedzialna? Odpowied\u017a \u201ctak, nie jestem\u201d tylko niepotrzebnie by wszystko zagmatwa\u0142a, prawda? No w\u0142a\u015bnie niekoniecznie. U\u017cycie tej metody jest tylko jedno i sprowadza si\u0119 do \u201cje\u015bli nie jeste\u015b za to odpowiedzialna, to daj sobie spok\u00f3j\u201d. Zapisanie tego warunku jako \u003Ccode\u003Eif (!$this-\u0026gt;isResponsibleForClass($className))\u003C\/code\u003E zmniejsza czytelno\u015b\u0107, poniewa\u017c tego wykrzyknika prawie nie wida\u0107. Zawsze zapominamy o wykrzyknikach...\u003C\/p\u003E\n\u003Cp\u003EZanegowanie dopad\u0142o te\u017c zmienn\u0105 \u003Ccode\u003Evalid\u003C\/code\u003E. Zawsze gdy j\u0105 widzia\u0142em, musia\u0142em si\u0119 zastanowi\u0107, po co ona komu. Kiedy niby autoloader staje si\u0119 \u201cinvalid\u201d? A chodzi przecie\u017c o to, \u017ce autoloader mo\u017ce by\u0107 albo \u003Cem\u003Ejeszcze nie zarejestrowany\u003C\/em\u003E, albo \u003Cem\u003Eju\u017c zarejestrowany\u003C\/em\u003E. Czemu wi\u0119c nie zmieni\u0107 nazwy tej w\u0142a\u015bciwo\u015bci na \u003Ccode\u003Eregistered\u003C\/code\u003E? Teraz metoda \u003Ccode\u003Eregister()\u003C\/code\u003E staje si\u0119 bardzo czytelna: \u201cje\u015bli jeste\u015b zarejestrowany, to ju\u017c wi\u0119cej si\u0119 nie rejestruj\u201d.\u003C\/p\u003E\n\u003Cp\u003EBazowy namespace powinien ko\u0144czy\u0107 si\u0119 backslashem, aby unikn\u0105\u0107 takich sytuacji, gdy chcemy na przyk\u0142ad przechwyci\u0107 klasy \u201cJava\/...\u201d, a zgarniemy tak\u017ce i \u201cJavascript\/...\u201d. Dla zachowania jednorodno\u015bci, to samo powinno dotyczy\u0107 drugiego parametru konstruktora (\u015bcie\u017cki do folderu). Ale po co cz\u0142owiekowi, kt\u00f3ry chce u\u017cy\u0107 naszego autoloadera, kaza\u0107 si\u0119 zastanawia\u0107 nad tym, czy wymagamy slasha czy nie? Mo\u017ce lepiej po prostu sprawd\u017amy, czy slash jest na ko\u0144cu parametru, i doklejmy go, je\u015bli nie? Dodanie metody \u003Ccode\u003EaddEndingSlash\u003C\/code\u003E sprawi\u0142o, \u017ce nie tylko sama klasa jest prostsza, ale tak\u017ce i jej u\u017cycie na zewn\u0105trz.\u003C\/p\u003E\n\u003Cp\u003ETym oto sposobem w nowym kodzie jest zdecydowanie mniej zagnie\u017cd\u017ce\u0144, metody s\u0105 kr\u00f3tsze, a ka\u017cda zajmuje si\u0119 dok\u0142adnie jedn\u0105 rzecz\u0105 na jednym poziomie abstrakcji.\u003C\/p\u003E\n\u003Cp\u003EI jak? Kt\u00f3r\u0105 wersj\u0119 wolicie? Kt\u00f3ra nie marnuje czasu programisty pr\u00f3buj\u0105cego j\u0105 zrozumie\u0107?\u003C\/p\u003E\n\u003Cp\u003E\u201cCzysty kod\u201d warto przeczyta\u0107, warto do niego wraca\u0107 i \u2013 przede wszystkim \u2013 warto stosowa\u0107 si\u0119 do zawartych w nim rad.\u003C\/p\u003E","words":1438,"readTime":6,"lang":"pl"}}},"projects\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w":{"key":"projects\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:23+02:00","publishedAt":"2014-11-11T16:56:00+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/stoslowia.avris.it\/","displayUrl":null},{"icon":"brands twitter-square","colour":"secondary","url":"https:\/\/twitter.com\/Stoslowia","displayUrl":null}],"category":"projects","subcategory":null,"slug":"stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w"},"content":{"pl":{"slug":"stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w","title":"Stos\u0142owia - opowiadania na sto s\u0142\u00f3w","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022235.33368365738\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADtElEQVRIibWVSW8cRRSAv6rqfWZsj7xNFAQmwAGEsiCQkIICgRzJlf\/GD+CAQJFyBCkWBxBIHDjhRAqZiSEhkYjjmYlxTy\/1HodZYjuT2CDxpO7qrnrV7+u3lRmNRqqqoKAoAKrjcSrGmNmlqnjveTwYkuc5WZqQJQlFWWEweKmJoojAObz3DIZDvvjyK54Mh4gIQRiSpBlZlpLEMVVVsb19F\/E1H1y8iCmKQg9CHByNMc9ATdemYDu7fR73+6RJwkq7jahiJvrOWYqi4Nsbm2zdukWj0aTZahGFIY93d7n\/xzatVoMrH1\/h3NmzJHH8FGge1FR6vR7Ly8ssLi6yubnJ3t4eIoJzjqqq+PTqVXb7A+49eEgcRXTWVgDIkhRV4f6fD\/ju+x9oNDL6\/QF3bt\/k1Y0NLl36kDdee50gCBARROQw0Dwo7z1bW1usrq7S6XQYjUaICGBmIUYVmeiPioLu3d+xztJZW2Wx1cIA169fo9vt8crGBpcvf0JnfR3nHF4E8R4RQZXjgXZ2dtje3ubMmTPoxLD3nvGyYq19qq9Q+5o4jlEMv3V7VMU+775znl63y3rnNI1GgzAMZ+EfgyhlWVLV9fyQHQXr9XosLCwQxzFVVVNWJWmaYq3FWjvTNRhEPEEQEEURXgRUuPnrFu32Au3lNcIwxHvBGEMQuLGXvEdVqb0\/WQ7Nm5unc1REPM4FVFXF7e4dWs0FWo0GokqaJpPKBmctxkBd15g8zyfen8KMQ\/EM0OEbB\/ccBgVjoBjtU5QV1oU4a3GBoyxL6toThSF5PsI6S6ORoTKuWgyYuq71JH\/7b0V0Amfhb18AkLkIh3nuHmMM5jgaUaWoxvRpaE8Eo6rk1bgG75d9Pr\/3Ewp81nmb95denulMG+2hficiLwRSVYqyxFlLEASHNv8fEhynoKrcffgA7z0vLZ8iSzIwggECFTCGUaAIiq0F9UISWMTAjb2faQYZ59NzGAyJs9iJV4CZhw4+B9OXqfGjbjTWcvrUKTAGLQPySkhlNE7qkHGiuAhjxmVvrJ0cM9B0GZlNccYcyv+DIAftnyiHVHXWmWUy51QmH5hMWHeco58x\/p9zaOQLhsUTkiSZu\/7j\/i\/kWvBR9B5ilCCYnwVfD7\/hQvImF5K3Xgx8HJCI5063R3upzcrK8nP1BoM+u\/0BUnuarRbr62vHNs+joZsBPc+N03D99ejR5IgAVZmdXyJKGAYsLS6R5\/v0B4Nxx7QGayzWjr\/rnCOOYlqt5gtDpqr8A2r8VP6cD2gXAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_small.png\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022235.33368365738\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EIle tre\u015bci mo\u017cna zmie\u015bci\u0107 w stu s\u0142owach? Sam zobacz! Na tej stronie zbieramy stos\u0142owia \u2013 opowiadania na dowolny temat zawarte w jednym akapicie, nie d\u0142u\u017csze ni\u017c sto s\u0142\u00f3w, ale posiadaj\u0105ce fabu\u0142\u0119, bohater\u00f3w i puent\u0119. Sam te\u017c mo\u017cesz je stworzy\u0107 i doda\u0107 do naszego zbioru!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022470.66736731477\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAARCAYAAAC1tw6GAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADtElEQVRIibWVSW8cRRSAv6rqfWZsj7xNFAQmwAGEsiCQkIICgRzJlf\/GD+CAQJFyBCkWBxBIHDjhRAqZiSEhkYjjmYlxTy\/1HodZYjuT2CDxpO7qrnrV7+u3lRmNRqqqoKAoAKrjcSrGmNmlqnjveTwYkuc5WZqQJQlFWWEweKmJoojAObz3DIZDvvjyK54Mh4gIQRiSpBlZlpLEMVVVsb19F\/E1H1y8iCmKQg9CHByNMc9ATdemYDu7fR73+6RJwkq7jahiJvrOWYqi4Nsbm2zdukWj0aTZahGFIY93d7n\/xzatVoMrH1\/h3NmzJHH8FGge1FR6vR7Ly8ssLi6yubnJ3t4eIoJzjqqq+PTqVXb7A+49eEgcRXTWVgDIkhRV4f6fD\/ju+x9oNDL6\/QF3bt\/k1Y0NLl36kDdee50gCBARROQw0Dwo7z1bW1usrq7S6XQYjUaICGBmIUYVmeiPioLu3d+xztJZW2Wx1cIA169fo9vt8crGBpcvf0JnfR3nHF4E8R4RQZXjgXZ2dtje3ubMmTPoxLD3nvGyYq19qq9Q+5o4jlEMv3V7VMU+775znl63y3rnNI1GgzAMZ+EfgyhlWVLV9fyQHQXr9XosLCwQxzFVVVNWJWmaYq3FWjvTNRhEPEEQEEURXgRUuPnrFu32Au3lNcIwxHvBGEMQuLGXvEdVqb0\/WQ7Nm5unc1REPM4FVFXF7e4dWs0FWo0GokqaJpPKBmctxkBd15g8zyfen8KMQ\/EM0OEbB\/ccBgVjoBjtU5QV1oU4a3GBoyxL6toThSF5PsI6S6ORoTKuWgyYuq71JH\/7b0V0Amfhb18AkLkIh3nuHmMM5jgaUaWoxvRpaE8Eo6rk1bgG75d9Pr\/3Ewp81nmb95denulMG+2hficiLwRSVYqyxFlLEASHNv8fEhynoKrcffgA7z0vLZ8iSzIwggECFTCGUaAIiq0F9UISWMTAjb2faQYZ59NzGAyJs9iJV4CZhw4+B9OXqfGjbjTWcvrUKTAGLQPySkhlNE7qkHGiuAhjxmVvrJ0cM9B0GZlNccYcyv+DIAftnyiHVHXWmWUy51QmH5hMWHeco58x\/p9zaOQLhsUTkiSZu\/7j\/i\/kWvBR9B5ilCCYnwVfD7\/hQvImF5K3Xgx8HJCI5063R3upzcrK8nP1BoM+u\/0BUnuarRbr62vHNs+joZsBPc+N03D99ejR5IgAVZmdXyJKGAYsLS6R5\/v0B4Nxx7QGayzWjr\/rnCOOYlqt5gtDpqr8A2r8VP6cD2gXAAAAAElFTkSuQmCC\u0022 data-src=\u0022https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_big.png\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022960\u0022 height=\u0022470.66736731477\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EIle tre\u015bci mo\u017cna zmie\u015bci\u0107 w stu s\u0142owach? Sam zobacz! Na tej stronie zbieramy stos\u0142owia \u2013 opowiadania na dowolny temat zawarte w jednym akapicie, nie d\u0142u\u017csze ni\u017c sto s\u0142\u00f3w, ale posiadaj\u0105ce fabu\u0142\u0119, bohater\u00f3w i puent\u0119. Sam te\u017c mo\u017cesz je stworzy\u0107 i doda\u0107 do naszego zbioru!\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["literatura","opowiadanie","php","poezja","proza","zwi\u0119z\u0142o\u015b\u0107"],"hasMore":false,"image":"https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_small.png","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022117.66684182869\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EIle tre\u015bci mo\u017cna zmie\u015bci\u0107 w stu s\u0142owach? Sam zobacz! Na tej stronie zbieramy stos\u0142owia \u2013 opowiadania na dowolny temat zawarte w jednym akapicie, nie d\u0142u\u017csze ni\u017c sto s\u0142\u00f3w, ale posiadaj\u0105ce fabu\u0142\u0119, bohater\u00f3w i puent\u0119. Sam te\u017c mo\u017cesz je stworzy\u0107 i doda\u0107 do naszego zbioru!\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_big.png\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/stos\u0142owia-opowiadania-na-sto-s\u0142\u00f3w_mini.png\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022117.66684182869\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EIle tre\u015bci mo\u017cna zmie\u015bci\u0107 w stu s\u0142owach? Sam zobacz! Na tej stronie zbieramy stos\u0142owia \u2013 opowiadania na dowolny temat zawarte w jednym akapicie, nie d\u0142u\u017csze ni\u017c sto s\u0142\u00f3w, ale posiadaj\u0105ce fabu\u0142\u0119, bohater\u00f3w i puent\u0119. Sam te\u017c mo\u017cesz je stworzy\u0107 i doda\u0107 do naszego zbioru!\u003C\/p\u003E","words":54,"readTime":null,"lang":"pl"}}},"projects\/cropper":{"key":"projects\/cropper","type":"article","published":true,"meta":{"createdAt":"2017-04-28T19:43:18+02:00","publishedAt":"2014-01-25T23:54:00+01:00","group":null,"links":[{"icon":"globe-europe","colour":"primary","url":"https:\/\/cropper.avris.it\/","displayUrl":null},{"icon":"brands gitlab","colour":"secondary","url":"https:\/\/gitlab.com\/Avris\/Cropper","displayUrl":null}],"category":"projects","subcategory":null,"slug":"cropper"},"content":{"pl":{"slug":"cropper","title":"Cropper","intro":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/cropper_small.jpg\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022274.03636363636\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAF9UlEQVRIiXWWS3BcRxWGv+7b9zUvjUbgINnxI0Gy7IBwqIIAcZIikIeCkuAFj5RhYSpVUN6wMSs2LMiGhWELC6iiypRxJQ4FVUmFvElMEqdiJZZiJDt2ZJctRdLYo2hG87j3djeL0YxmMvLZ3L6P8\/\/n\/Pf0OS2MMZZ1u3r1KrlcFhCABSFIp9JIKQGw1iKEaK87TQjRfial7LpvmTEGay21ao1aFOMFAVhLLh22cVUngTEGrQ1R1EBrjed5YMFYg040QgiklOvfJbie1yYSCEAgJOT78gCsrVVZWf10\/R14nkdfLouQgnToAxbLRtBCCFRn1sVikeXlZTKZNLV6nUJ\/gThJqJTLhKk0caNBJptFa029XsNPNK6rUEqhtcZxFFYboijC930mPzjLuXPnuPDem2wZuZM9oyNMjD\/ERxdm+WRhgXQ6hRekGBv7SlvRtkIARmsGhwYJwxCtNUq51Ot1stkctVoNISWe5xGGIVeuXMb3QSmXfD5PJ07LXCW55XMFskN51hollGx+E1crLMy8z87deyleudQOCOhUyLJz1y4cR5IkCUq5aKNJp9PEcYRSWay1zd+4Lm8UNWg0Gty4fh3XcwmCAD8IMNrgOA5Yw\/TZSca2387vjv6JI9t2MTMzQzo\/wL77xgkCl4Fb6apJtXEjKC4vE6ZTRI0Iz1UYC41GgzAVsra2RhiESCnJ5\/O4yiWdzSCFBCxCSPr6+rqVsvDvF1+muv9eLn48x\/zlyxz8yU9545UXmT59isEdu7DaMDw83HbpKmqlnGYAQUCiNdZoUqmQWq1GoVCgUi5vcFlD6foNlKswxhKGAUIIkiTBcSRKuaxWY+aXV8lkMmijWanWAMjl+\/jCztsZ2raVlXK1rTiAMMbYVkEVl5ex67vG931yudx6+cNG3r21AnbT51PnznPi+N+4+5t3MTU1zZ69e5iYmCCKonYrAVBKdQfUehHHMUII5ubmeOW117HNVrRpwVpryWYyHPj+4yilANBao7XuIruZaa3xfb8dUDu4llydTaxUKvH3p08ipWTi4e8ilUeUGBzPJ45iXnrheRCCnbcO8fhjj7Z9jTG4rgvWYIXTzFg0G6VJYqyQgEDK3qbZDmiz7LEgrEU5kq\/u\/w5vnfgjbz5\/kiS3lZ\/96jc4QoAUWCSthFpWrlaZPDPJO88dp2x8HjhwkNHdt3Hmv2\/w9gvPkhoaYfzADxi9bXsXZQtjc20FKM\/BUw4ZT3Dp3VM8du99bLF1anMfkg58lBC4Tq+7FIKFC1MkVy4xf\/pVomvnUdLh\/Nuvkavc4IPnTpCqr2xKC3R36k5zpUQKyIUBh578Obm4hvJ8RmbP4lTKiCAg0UlT0A75BXDn17\/FHabCR0uLbCst4wi4\/6FHUB9Ps3VwlnDhKuwd6\/4prTn4WcDW2hHNCWStoZEpMHX+Iravn+DQYcj3IQCvoxjbCgFh4DPfiKi6ATOFISSG1MDnuTB\/jXK2wLXCYE8inf5dCrXW9Xodaw1rN4oMFPq548sj6OwWDh\/+JR9OT1GplLE67gGMk5ijv\/8DY18axeS38dc\/\/4XFxSWOHTvGN+65m0tLFf757D\/4LG+b366Haa1tb\/vJyUmeeuq369NXs31LPwcnHuD4y+\/yzDMnKa6WyedzjD\/yPY4ePdoeJ3Ec47pu+1SAbc7y1i7uvNbr9eaOpLsP9Wq+bjqOkY7kpdf\/w\/AXh1kox7x\/5j2WSitooymVVrBRvUf6RhSzWioS42ARSOnQl8uwtnKdhgahXFJBQCb0N+WVN+sHjnIwxtCIYpTrYgGLRBtDojWNKKKxuNTjF8Uxp946za9\/+G2efPR+Zmf\/R7Va419Pn+DIgf0cOfQEC58s9vhZa7HW3ryoa\/WYyloNrXXzCGXB9ZongNbJr+p7PcBCCEJT40cPj\/PgvlHUxWkcR5ITMb\/48RPsyfukFud6lG0r1ALpBAT4tBpTrCTN2WYtxhpyuRzGbDjXGtEmgJaRfV8jlc4xtGMHt\/QP4AjYe9c9eK7P7tFRAi\/o4uq0\/wMzwMw+S82WvgAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/cropper_small.jpg\u0022 alt=\u0022\u0022 class=\u0022border-bottom\u0022 width=\u0022480\u0022 height=\u0022274.03636363636\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EZdj\u0119cia legitymacyjne u fotografa kosztuj\u0105 kilkana\u015bcie z\u0142otych za cztery sztuki. Samo ich wywo\u0142anie jest jednak warte raptem kilkadziesi\u0105t groszy. Je\u015bli sam potrafisz wykona\u0107 i zretuszowa\u0107 zdj\u0119cie, mo\u017cesz potem u\u017cy\u0107 poni\u017cszego narz\u0119dzia, by szybko przyci\u0105\u0107 je i przeskalowa\u0107 do odpowiednich rozmiar\u00f3w (35x45 mm). Nast\u0119pnie wystarczy wywo\u0142a\u0107 je jak zwyk\u0142e zdj\u0119cie 9x13 cm i r\u0119cznie przyci\u0105\u0107 no\u017cyczkami.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","content":"\u003Cfigure\u003E\n                \u003Cnoscript\u003E\n                    \u003Cimg src=\u0022https:\/\/avris.it\/image\/cropper_big.jpg\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022550\u0022 height=\u0022314\u0022\u003E                \n                \u003C\/noscript\u003E\n                \u003Cspan class=\u0022hide-noscript\u0022\u003E\u003Cimg src=\u0022data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAUCAYAAADlep81AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAF9UlEQVRIiXWWS3BcRxWGv+7b9zUvjUbgINnxI0Gy7IBwqIIAcZIikIeCkuAFj5RhYSpVUN6wMSs2LMiGhWELC6iiypRxJQ4FVUmFvElMEqdiJZZiJDt2ZJctRdLYo2hG87j3djeL0YxmMvLZ3L6P8\/\/n\/Pf0OS2MMZZ1u3r1KrlcFhCABSFIp9JIKQGw1iKEaK87TQjRfial7LpvmTEGay21ao1aFOMFAVhLLh22cVUngTEGrQ1R1EBrjed5YMFYg040QgiklOvfJbie1yYSCEAgJOT78gCsrVVZWf10\/R14nkdfLouQgnToAxbLRtBCCFRn1sVikeXlZTKZNLV6nUJ\/gThJqJTLhKk0caNBJptFa029XsNPNK6rUEqhtcZxFFYboijC930mPzjLuXPnuPDem2wZuZM9oyNMjD\/ERxdm+WRhgXQ6hRekGBv7SlvRtkIARmsGhwYJwxCtNUq51Ot1stkctVoNISWe5xGGIVeuXMb3QSmXfD5PJ07LXCW55XMFskN51hollGx+E1crLMy8z87deyleudQOCOhUyLJz1y4cR5IkCUq5aKNJp9PEcYRSWay1zd+4Lm8UNWg0Gty4fh3XcwmCAD8IMNrgOA5Yw\/TZSca2387vjv6JI9t2MTMzQzo\/wL77xgkCl4Fb6apJtXEjKC4vE6ZTRI0Iz1UYC41GgzAVsra2RhiESCnJ5\/O4yiWdzSCFBCxCSPr6+rqVsvDvF1+muv9eLn48x\/zlyxz8yU9545UXmT59isEdu7DaMDw83HbpKmqlnGYAQUCiNdZoUqmQWq1GoVCgUi5vcFlD6foNlKswxhKGAUIIkiTBcSRKuaxWY+aXV8lkMmijWanWAMjl+\/jCztsZ2raVlXK1rTiAMMbYVkEVl5ex67vG931yudx6+cNG3r21AnbT51PnznPi+N+4+5t3MTU1zZ69e5iYmCCKonYrAVBKdQfUehHHMUII5ubmeOW117HNVrRpwVpryWYyHPj+4yilANBao7XuIruZaa3xfb8dUDu4llydTaxUKvH3p08ipWTi4e8ilUeUGBzPJ45iXnrheRCCnbcO8fhjj7Z9jTG4rgvWYIXTzFg0G6VJYqyQgEDK3qbZDmiz7LEgrEU5kq\/u\/w5vnfgjbz5\/kiS3lZ\/96jc4QoAUWCSthFpWrlaZPDPJO88dp2x8HjhwkNHdt3Hmv2\/w9gvPkhoaYfzADxi9bXsXZQtjc20FKM\/BUw4ZT3Dp3VM8du99bLF1anMfkg58lBC4Tq+7FIKFC1MkVy4xf\/pVomvnUdLh\/Nuvkavc4IPnTpCqr2xKC3R36k5zpUQKyIUBh578Obm4hvJ8RmbP4lTKiCAg0UlT0A75BXDn17\/FHabCR0uLbCst4wi4\/6FHUB9Ps3VwlnDhKuwd6\/4prTn4WcDW2hHNCWStoZEpMHX+Iravn+DQYcj3IQCvoxjbCgFh4DPfiKi6ATOFISSG1MDnuTB\/jXK2wLXCYE8inf5dCrXW9Xodaw1rN4oMFPq548sj6OwWDh\/+JR9OT1GplLE67gGMk5ijv\/8DY18axeS38dc\/\/4XFxSWOHTvGN+65m0tLFf757D\/4LG+b366Haa1tb\/vJyUmeeuq369NXs31LPwcnHuD4y+\/yzDMnKa6WyedzjD\/yPY4ePdoeJ3Ec47pu+1SAbc7y1i7uvNbr9eaOpLsP9Wq+bjqOkY7kpdf\/w\/AXh1kox7x\/5j2WSitooymVVrBRvUf6RhSzWioS42ARSOnQl8uwtnKdhgahXFJBQCb0N+WVN+sHjnIwxtCIYpTrYgGLRBtDojWNKKKxuNTjF8Uxp946za9\/+G2efPR+Zmf\/R7Va419Pn+DIgf0cOfQEC58s9vhZa7HW3ryoa\/WYyloNrXXzCGXB9ZongNbJr+p7PcBCCEJT40cPj\/PgvlHUxWkcR5ITMb\/48RPsyfukFud6lG0r1ALpBAT4tBpTrCTN2WYtxhpyuRzGbDjXGtEmgJaRfV8jlc4xtGMHt\/QP4AjYe9c9eK7P7tFRAi\/o4uq0\/wMzwMw+S82WvgAAAABJRU5ErkJggg==\u0022 data-src=\u0022https:\/\/avris.it\/image\/cropper_big.jpg\u0022 alt=\u0022\u0022 class=\u0022border\u0022 width=\u0022550\u0022 height=\u0022314\u0022\u003E\u003C\/span\u003E\n                \n            \u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EZdj\u0119cia legitymacyjne u fotografa kosztuj\u0105 kilkana\u015bcie z\u0142otych za cztery sztuki. Samo ich wywo\u0142anie jest jednak warte raptem kilkadziesi\u0105t groszy. Je\u015bli sam potrafisz wykona\u0107 i zretuszowa\u0107 zdj\u0119cie, mo\u017cesz potem u\u017cy\u0107 poni\u017cszego narz\u0119dzia, by szybko przyci\u0105\u0107 je i przeskalowa\u0107 do odpowiednich rozmiar\u00f3w (35x45 mm). Nast\u0119pnie wystarczy wywo\u0142a\u0107 je jak zwyk\u0142e zdj\u0119cie 9x13 cm i r\u0119cznie przyci\u0105\u0107 no\u017cyczkami.\u003C\/p\u003E\u003Csvg xmlns=\u0022http:\/\/www.w3.org\/2000\/svg\u0022 style=\u0022display: none;\u0022\u003E\u003C\/svg\u003E","tags":["ajax","dokumenty","legitymacyjne","oszcz\u0119dno\u015b\u0107","php","przycinanie","zdj\u0119cia"],"hasMore":false,"image":"https:\/\/avris.it\/image\/cropper_small.jpg","introLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/cropper_big.jpg\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/cropper_mini.jpg\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022137.01818181818\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EZdj\u0119cia legitymacyjne u fotografa kosztuj\u0105 kilkana\u015bcie z\u0142otych za cztery sztuki. Samo ich wywo\u0142anie jest jednak warte raptem kilkadziesi\u0105t groszy. Je\u015bli sam potrafisz wykona\u0107 i zretuszowa\u0107 zdj\u0119cie, mo\u017cesz potem u\u017cy\u0107 poni\u017cszego narz\u0119dzia, by szybko przyci\u0105\u0107 je i przeskalowa\u0107 do odpowiednich rozmiar\u00f3w (35x45 mm). Nast\u0119pnie wystarczy wywo\u0142a\u0107 je jak zwyk\u0142e zdj\u0119cie 9x13 cm i r\u0119cznie przyci\u0105\u0107 no\u017cyczkami.\u003C\/p\u003E","contentLite":"\u003Cfigure\u003E\u003Ca href=\u0022https:\/\/avris.it\/image\/cropper_big.jpg\u0022 target=\u0022_blank\u0022 rel=\u0022noopener\u0022\u003E\u003Cimg src=\u0022https:\/\/avris.it\/image\/cropper_mini.jpg\u0022 alt=\u0022\u0022 width=\u0022240\u0022 height=\u0022137.01818181818\u0022 loading=\u0022lazy\u0022\u003E\u003C\/a\u003E\u003C\/figure\u003E\u003C\/p\u003E\n\u003Cp\u003EZdj\u0119cia legitymacyjne u fotografa kosztuj\u0105 kilkana\u015bcie z\u0142otych za cztery sztuki. Samo ich wywo\u0142anie jest jednak warte raptem kilkadziesi\u0105t groszy. Je\u015bli sam potrafisz wykona\u0107 i zretuszowa\u0107 zdj\u0119cie, mo\u017cesz potem u\u017cy\u0107 poni\u017cszego narz\u0119dzia, by szybko przyci\u0105\u0107 je i przeskalowa\u0107 do odpowiednich rozmiar\u00f3w (35x45 mm). Nast\u0119pnie wystarczy wywo\u0142a\u0107 je jak zwyk\u0142e zdj\u0119cie 9x13 cm i r\u0119cznie przyci\u0105\u0107 no\u017cyczkami.\u003C\/p\u003E","words":73,"readTime":null,"lang":"pl"}}}}}