<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Ivo Petkov</title><link>https://ivopetkov.com/</link><description>Super excited about Dots Mesh. Founder of Bear CMS and Alle.bg. This is my space on the Internet.</description><language>en</language><atom:link href="https://ivopetkov.com/rss.xml" rel="self" type="application/rss+xml"></atom:link><item><title>Responsively Lazy v3 is even better for lazy loading images</title><link>https://ivopetkov.com/b/responsively-lazy-v3-is-even-better-for-lazy-loading-images/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/1c85d936d061-c999999999/58hen90tx91dlha4h0brnkr.jpg" style="max-width:100%;"></div></div><div><div>There is a library called Responsively Lazy I’ve <a href="https://ivopetkov.com/b/lazy-load-responsive-images/" title="">released in 2015</a> that got quite popular.<br><br>Since then a lot has changed. The browsers got <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture" title="">the &lt;picture&gt; tag,</a> <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset" title="">srcset</a> is now widely supported, and the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#loading" title="">loading attribute</a> was introduced. But still, I think Responsively Lazy is the best way to go when you want to lazy load responsive images and other content.<br><br>Version 3.0, <a href="https://github.com/ivopetkov/responsively-lazy/releases" title="">released today</a>, builds on the foundation (the idea from 2015) and provides a lot more possibilities.<br><br>Let’s check it out.<br></div></div><div><h2>What’s not changed</h2></div><div><div>Responsively Lazy still provides the same <b>SEO-friendly way to lazy load images</b>. You can see it in Google Images. All websites at Alle.bg and Bear CMS benefit from this. And still, it’s truly lazy. There are absolutely no unnecessary requests.</div></div><div><h2>What’s new</h2></div><div><div>There is a new API. Now you must use the data-responsively-lazy attributes instead of a class name.<br><br>Here is an example that contains all the new features.<br></div></div><div><code class="bearcms-code-element" id="bce3352810c114bd44082c14d2af9b1b5da">&lt;img
    src=&quot;images/2500.jpg&quot;
    style=&quot;width:100%;aspect-ratio:400/274;&quot;
    srcset=&quot;data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&quot;
    data-responsively-lazy=&quot;images/400.avif 400w avif, images/400.webp 400w webp, images/400.jpg 400w, ...&quot;
    data-responsively-lazy-threshold=&quot;500px&quot; // optional
    data-on-responsively-lazy-load=&quot;...&quot; // optional
/&gt;
&lt;div
    data-responsively-lazy-type=&quot;background&quot;
    data-responsively-lazy=&quot;images/400.avif 400w avif, images/400.webp 400w webp, images/400.jpg 400w, ...&quot;
&gt;&lt;/div&gt;
&lt;div
    data-responsively-lazy-type=&quot;html&quot;
    data-responsively-lazy=&quot;hello world&quot;
&gt;&lt;/div&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce3352810c114bd44082c14d2af9b1b5da");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>As you can see, there is now <b>support for AVIF</b> and you can specify the order the image versions are checked.<br><br>You can also <b>lazy load background images</b>.<br><br>You can <b>customize the load threshold</b>.<br><br><div>All good stuff.</div><div><br></div><div><a href="https://ivopetkov.github.io/responsively-lazy-v3/" title="">Here is a demo</a>.<br></div><br><a href="https://github.com/ivopetkov/responsively-lazy/" title="">Take a look at the code</a> and give it a try.<br></div></div>]]></description><pubDate>Sat, 30 Dec 2023 21:58:07 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/responsively-lazy-v3-is-even-better-for-lazy-loading-images/</guid></item><item><title>Сайт на годината 2020 и моите впечатления като участник и част от журито</title><link>https://ivopetkov.com/b/%D1%81%D0%B0%D0%B9%D1%82-%D0%BD%D0%B0-%D0%B3%D0%BE%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%B0-2020-%D0%B8-%D0%BC%D0%BE%D0%B8%D1%82%D0%B5-%D0%B2%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F-%D0%BA%D0%B0%D1%82%D0%BE-%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA-%D0%B8-%D1%87%D0%B0%D1%81%D1%82-%D0%BE%D1%82-%D0%B6%D1%83%D1%80%D0%B8%D1%82%D0%BE/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/651f60634118-c999999999/xrnemswq236flga141v6leq.jpg" style="max-width:100%;"></div></div><div><div>Тази година имах възможността да се докосна до конкурса <a href="https://bgweb.bg/" title="">Сайт на година</a> и да погледна зад завесите. С тази статия искам да ви споделя моите впечатления и ще се радвам, ако успея да ви мотивирам да участвате през следващите издания, защото ползи има много.</div></div><div><h2>Какво е Сайт на годината?</h2></div><div><div>Това е конкурс, който дава възможност на всеки собственик или създател на уебсайт да покаже това, което е направил през изминалата година и да се състезава за приза “Сайт на година” в една от деветте категории. Жури, коeто включва експерти по програмиране, маркетинг и дизайн, оценява проектите и определя победителите и техните подгласници. Освен това, публиката също има възможност да избере своите победители, чрез онлайн гласуване.</div></div><div><h2>Подготовката</h2></div><div><div>Конкурсът премина през няколко етапа, като естествено през първия етап участниците регистрират своите проекти. През това време <a href="https://bgweb.bg/s/jury/" title="">жури</a>, което се състоя от над 40 професионалиста определи и публикува критериите за оценка. Аз бях част от екипа програмисти. Заедно с колегите, направихме няколко срещи и публикувахме следните <a href="https://bgweb.bg/blog/Kriterii-za-otzenka-Sayt-na-godinata-2020--Programirane/" title="">критерии за оценка на техническата част</a> на уебсайтове. Можете да погледнете и критериите за оценка в сегмента <a href="https://bgweb.bg/blog/Kriterii-za-otzenka-Sayt-na-godinata-2020--Marketing/" title="">Маркетинг</a> и сегмента <a href="https://bgweb.bg/blog/Kriterii-za-otzenka-Sayt-na-godinata-2020--Dizayn/" title="">Дизайн</a>.<br><br>Не мога да пропусна да спомена и организационните срещи с Жюстин Томс, която е ръководителят на екипа и главният виновник това събитие да се случва за трети пореден път. Във всяка среща виждах много ентусиазъм. Всеки искаше това издание да бъде по-добро от предишните и да се даде максимална стойност на участниците. Това успя да ме “зарази” и когато дойде моментът за гласуване, бях готов да бъда обективен и максимално полезен.<br></div></div><div><h2>Гласуването</h2></div><div><div>Гласуването на журито беше разделено на две части. През първия етап, всеки член на журито оценяваше всички сайтове от една или няколко категории (фирмен сайт, личен сайт, сайт за кауза, продуктов сайт и т.н.). Важно условие е да не оценяваш категории, в които има проекти, с който си обвързан. На мен ми се паднаха категориите “Сайт на кауза” и “Фирмен сайт”. За всеки уебсайт трябваше да дам оценка от 1 до 10, и да оставя своя коментар.<br><br>Разполагахме с 14 дни и се радвам, че започнах с преглеждането на проектите сравнително рано. Бързо осъзнах, че техническият анализ и писането на коментари (обратна връзка към автора на сайта) може да отнеме много време. Със сигурност отнема много енергия, особено като се има предвид, че си бях обещал да подходя отговорно и да дам максимална добавена стойност.<br><br>Веднага след изтичането на този етап и сумирането на оценките, стана ясно кои са топ сайтовете във всяка категория и дойде моментът всеки от нас да избере своите топ 3. Тук отново трябваше да отделя доста време за да оценя техническата реализация на най-добрите уебсайтове от другите категории. Естествено подходих професионално и единственото нещо, което остана е да изчакам церемонията по награждаването, на която всички щяхме да научим финалните резултати.<br></div></div><div><h2>Награждаването</h2></div><div><div><img src="https://ivopetkov.com/assets/8053a1f10f2f-c999999999/vqieuj2mk8kflga1b7a7e5x.jpg" style="max-width:100%;"></div></div><div><div>Тази година награждаването беше онлайн. Можете да го видите във <a href="https://www.facebook.com/bgweb.bg/videos/3626813830672385" title="">Facebook страницата на конкурса</a>. Можете да видите и списък със самите победители на <a href="https://bgweb.bg/s/winners/" title="">bgweb.bg</a>. Победителите получиха своите статуетки, но може би по-важното, е че всички получиха обратна връзка и много полезни коментари от едни от най-добрите програмисти, дизайнери и маркетолози в България. Ето това за мен е безценно! Ето това вдига нивото на българския уеб!</div></div><div><h2>Моето впечатление като участник</h2></div><div><div>Конкурсът ме хвана в момент, в който все ще разработвах <a href="https://about.dotsmesh.com" title="">новия си проект</a> in stealth mode. Реших да го регистрирам веднага и се надявах това да ме мотивира да успея да го завърша по-бързо. Успях да го пусна публично преди крайния срок за регистрация, но уви, не беше на нивото, на което исках да бъде. Това ми даде възможност да “предизвикам” колегите от журито и да видя техните коментари и оценки. Очаквано, коментарите бяха много полезни, а оценките бяха напълно заслужени.<br><br><div>Надявам се, че тази статия ви е била полезна и интересна, и ще се радвам, ако съм успял да ви "запаля" да участвате през следващата година. Дотогава имате няколко месеца, през които да дадете най-доброто от себе си и да създадете страхотни уебсайтове и мобилни приложения. Нека “затрудним” журито, като му дадем да избира само между топ проекти!</div><div><br></div><div>И ако има дни, в които не сте мотивирани, не намирате смисъл или ви е трудно, конкурсът “Сайт на година” може да е това, което ще ви издърпа напред, защото можете да разчитате, че вашият труд няма да остане незабелязан.<br></div></div></div>]]></description><pubDate>Mon, 07 Dec 2020 12:25:19 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/%D1%81%D0%B0%D0%B9%D1%82-%D0%BD%D0%B0-%D0%B3%D0%BE%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%B0-2020-%D0%B8-%D0%BC%D0%BE%D0%B8%D1%82%D0%B5-%D0%B2%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F-%D0%BA%D0%B0%D1%82%D0%BE-%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA-%D0%B8-%D1%87%D0%B0%D1%81%D1%82-%D0%BE%D1%82-%D0%B6%D1%83%D1%80%D0%B8%D1%82%D0%BE/</guid></item><item><title>How I improved the performance of my big PHP web project</title><link>https://ivopetkov.com/b/how-i-improved-the-performance-of-my-big-php-web-project/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/86fa0eb0cdc4-c999999999/e67gnnp2h3ga1wi7e3f6.jpg" style="max-width:100%;"></div></div><div><div>In this article, I'll talk about the single architecture approach that improved the most the performance of <a href="https://bearcms.com/">Bear CMS</a> (it's a content management system I work on) and made me confident about its future updates. I have to admit that designing and optimizing systems is more like a passion for me than just a job, but I can also see the benefits this brings when running such systems on a high scale (CPU cycles can be costly and user time is too valuable to waste).</div></div><div><h2>You may have heard about lazy loading</h2></div><div><div>This is a concept that enables resources to be loaded and initialized only when needed. It's recommended when rendering a web page that has a large number of images (an online shop for example) and most of them are under the fold (not visible until the user scrolls). You may have heard of autoloading PHP classes using <a href="https://www.php.net/manual/en/function.spl-autoload-register.php">spl_autoload_register()</a>. <a href="https://getcomposer.org/">Composer</a> already uses this technique, so you can be certain no precious memory and time is wasted when using third-party libraries.</div></div><div><h2>Lazy loading requirements</h2></div><div><div><div>To apply this concept you need the following components:</div><div>1. A way to register a resource for lazy loading.</div><div>2. A mechanism to detect if a resource is needed and load it.</div><div>The spl_autoload_register() method from the example above, with a combination of a function (to include the classes) covers both requirements.</div></div></div><div><h2>How lazy loading helped me big time</h2></div><div><div>I hope you will agree, but a Content Management System is no small project. There is a very diverse set of features that must be supported (themes, editing UI, pages, content elements, etc.) and many different types of requests to handle (HTML pages, images, form submits, background tasks, etc.). To make development easy and enable future improvements without performance implications I decided to enforce lazy loading in the heaviest APIs. Here is an example:</div></div><div><code class="bearcms-code-element" id="bce07750f5551f4621884325fa83a3eaf3c">$cms-&gt;themes-&gt;register(&#039;the-name-of-the-theme&#039;, function() {
    // initialize the theme here.
});</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce07750f5551f4621884325fa83a3eaf3c");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>This enables registering multiple themes and only using the one needed, only when needed.</div><div><br></div><div>As you can see the trick here is using ...</div></div></div><div><h2>Anonymous functions!</h2></div><div><div>Anonymous functions in PHP enable you to postpone or skip the execution of some code. Even defining an array takes memory, and it makes a difference if this array holds lots of data. Let's compare:</div></div><div><code class="bearcms-code-element" id="bcebe86f95363097a1991d093db6d9c9171">$data = array_fill(0, 1000000, &#039;John&#039;); // Fills an array with a million text entries</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bcebe86f95363097a1991d093db6d9c9171");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>The code above takes around 35MB memory and 0.016 seconds on my machine.</div></div><div><code class="bearcms-code-element" id="bce542ade9899a0e56c0ec2b27497b8a6c6">$source = function() {
    return array_fill(0, 1000000, &#039;John&#039;);
};</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce542ade9899a0e56c0ec2b27497b8a6c6");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>This code takes 0 memory and 0 seconds because ... the content of the function is not executed. Smart! :)<div><br></div><div>This type of code (the code that is never run in a request) is the single biggest reason I was able to lower the CPU and memory requirements of my application and still keep it functional.<br><div><br></div><div><div>The real code from the CMS example above looks more like this:</div></div></div></div></div><div><code class="bearcms-code-element" id="bcea426a4c4007815a8d9a6d2e300fc43ac">$cms-&gt;themes-&gt;register(&#039;the-name-of-the-theme&#039;, function($theme) {
    $theme-&gt;options = &#039;data!&#039;; // Defines theme options so that the administrators can customize it
    $theme-&gt;styles = &#039;data!&#039;; // Defines ready-made styles to choose from
    $theme-&gt;manifest = &#039;data!&#039;; // Information about the theme and its author
});</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bcea426a4c4007815a8d9a6d2e300fc43ac");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>As you can see there are a lot of things that need to be done to define a theme. They all take memory and CPU cycles and are not needed on every request. Requests for images, favicons or sitemaps do not need themes.</div></div></div><div><h2>Anonymous functions as properties</h2></div><div><div><div>We can take the approach even further and bring anonymous functions to properties. Here is another update on the themes example above:</div></div></div><div><code class="bearcms-code-element" id="bce3345c247bfb313f871df6e6f6d88d751">$cms-&gt;themes-&gt;register(&#039;the-name-of-the-theme&#039;, function($theme) {
    $theme-&gt;manifest = function() {
        return [
            &#039;name&#039; =&gt; &#039;My awesome theme&#039;,
            &#039;description&#039; =&gt; &#039;This is ...&#039;,
            &#039;author&#039; =&gt; [
                &#039;name&#039; =&gt; &#039;John Smith&#039;,
                &#039;email&#039; =&gt; &#039;example@example.com&#039;
            ]
        ];
    };
});</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce3345c247bfb313f871df6e6f6d88d751");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>As you might have guessed it, the information in the manifest is needed only in very rare cases, so there is no need to spend time and memory on every request.<div><br></div><div>Identifying code that can be skipped can make a big difference.</div></div></div><div><h2>Anonymous functions are beautiful!</h2></div><div><div>If you've written, or seen JavaScript code, you might take them for granted as they are the basic way of defining asynchronous code (in <a href="https://www.w3schools.com/jsref/met_win_settimeout.asp">setTimeout</a> for example). In PHP however, due to lack of async support, they are rarely used. You might have used them to <a href="https://www.php.net/manual/en/function.usort.php">sort</a> and <a href="https://www.php.net/manual/en/function.array-filter.php">filter</a> arrays and I'd like to encourage you to use them more. They keep your code clean (and can even improve it) and can benefit the performance significantly. Even if you are not architecting systems or working on big projects, please, challenge yourself to try them for recursions or other internal optimizations.</div></div><div><h2>Thanks</h2></div><div><div>Thanks for getting this far. I hope this article has inspired you to optimize your projects and improve their performance. I'll be happy to learn about your results and thoughts on this technique.</div></div>]]></description><pubDate>Sun, 02 Jun 2019 13:18:15 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/how-i-improved-the-performance-of-my-big-php-web-project/</guid></item><item><title>Reading locked files in PHP</title><link>https://ivopetkov.com/b/reading-locked-files-in-php/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/8c3b4d0e08cd-c999999999/e1e2y73oomfa1orn68vi.jpg" style="max-width:100%;"></div></div><div><div><div>I hope that you are already familiar with the awesome <a href="http://php.net/manual/en/function.flock.php" title="file locking mechanism">file locking mechanism</a> that PHP and your OS provide. If not, here is a&nbsp; ...</div></div></div><div><h3>Brief introduction to file locking</h3></div><div><div>File locking is a mechanism that allows you to prevent read and write access to a specific file for a specific time. You request a write lock on a file, and if it is provided to you (it can be denied too), you can be sure that no other application/instance/process can write to the same file until you release the lock.</div></div><div><h3>Why use file locking</h3></div><div><div>The main reason is that you do not want invalid/broken data. Imagine you use a file to store some JSON data. In one moment you want to read the data, modify it and then write it back. You will be surprised to find out that at the exact same moment (milliseconds apart of course) some other application is doing the same thing and overwriting your modifications. In such case, you should be using file locking.</div></div><div><h3>File locking in PHP</h3></div><div><div>PHP provides a very simple, yet effective mechanism to <a href="http://php.net/manual/en/function.flock.php" title="require and release locks">acquire and release locks</a>. Here is an example:</div></div><div><code class="bearcms-code-element" id="bce423ba75c93b9e201c19fbc5abd02d7dd">$pointer = fopen(&quot;file.json&quot;, &quot;c+&quot;); // open the file for writing
if (flock($pointer, LOCK_EX)) { // acquire a writer lock
    // do some data manipulation
    flock($pointer, LOCK_UN); // release the lock
} else {
    // the file cannot be locked
}
fclose($pointer);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce423ba75c93b9e201c19fbc5abd02d7dd");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>If a file is locked (for reading or writing) in one application, all other applications that request locks on the same file will be paused until the lock is released in the first application. This is the default blocking mode, and it works great.</div><div><br></div><div>Now we are ready to continue with the main topic of this post ...</div></div></div><div><h2>What happens when you read a locked file</h2></div><div><div>In the following lines, I will show you few ways that you can use to read a file while it is locked for writing (and modified) and the results you will get. Also, for a bonus, I will add the result of the <a href="http://php.net/manual/en/function.filesize.php" title="filesize()">filesize()</a> function.</div></div><div><h3>Reading a locked file with file_get_contents()</h3></div><div><div><div>This is one of the worst way to read a file while it is locked and modified, because:</div><div>- file_get_contents() will return an empty string (like in "")</div><div>- filesize() will return the actual number of bytes written to the file</div></div></div><div><code class="bearcms-code-element" id="bce6702b26725f5831aa7d45543ac9fe1f7">$content = file_get_contents(&#039;file.json&#039;); // will return empty string
clearstatcache(&#039;file.json&#039;); // clear the file cache for the next function
$size = filesize(&#039;file.json&#039;); // will return the actual file size in the moment</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce6702b26725f5831aa7d45543ac9fe1f7");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h3>Reading a locked file with fread() without a reading lock</h3></div><div><div><div>This is the other worst way to read a file while it is locked and modified, because:</div><div>- fread() will return an empty string (like in "")</div><div>- filesize() will return the actual number of bytes written to the file</div></div></div><div><code class="bearcms-code-element" id="bce04ce9237366d0053f55fde0286b43313">$pointer = fopen(&#039;file.json&#039;, &#039;r&#039;);
$content = fread($pointer, 1000); // will return empty string
clearstatcache(&#039;file.json&#039;); // clear the file cache for the next function
$size = filesize(&#039;file.json&#039;); // will return the actual file size in the moment
fclose($pointer);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce04ce9237366d0053f55fde0286b43313");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h3>Reading a locked file with fread() with a reading lock (shared lock)</h3></div><div><div>This is the option to pick if you are OK waiting for the write lock to be released. PHP automatically pauses the execution of the application, so you do not have to do anything special. Just keep in mind the <a href="http://php.net/manual/en/info.configuration.php#ini.max-execution-time" title="max_execution_time">max_execution_time</a> option in your PHP config.</div></div><div><code class="bearcms-code-element" id="bce0114de231a864d0a79a3244a237f56b0">$pointer = fopen(&#039;file.json&#039;, &#039;r&#039;);
if(flock($pointer, LOCK_SH)){ // will block execution until the write lock is released
    $content = fread($pointer, 1000); // will return the correct content
    clearstatcache(&#039;file.json&#039;); // clear the file cache for the next function
    $size = filesize(&#039;file.json&#039;); // will return the correct size
}
fclose($pointer);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce0114de231a864d0a79a3244a237f56b0");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h3>Reading a locked file with fread() with a reading, nonblocking lock</h3></div><div><div>This is the advanced option. You can request a lock while the file is locked and modified in another application, but instead of waiting for file lock release, the execution will continue.</div></div><div><code class="bearcms-code-element" id="bce69fa23b609f3f8aea4b217bf30fd85fc">$pointer = fopen(&#039;file.json&#039;, &#039;r&#039;);
$wouldblock = null; // a variable that will be set to 1 if the file is already locked
if (flock($pointer, LOCK_SH | LOCK_NB, $wouldblock)) { // LOCK_NB tells flock() to continue execution of the application
    $content = fread($pointer, 1000); // will return the correct content
    clearstatcache(&#039;file.json&#039;); // clear the file cache for the next function
    $size = filesize(&#039;file.json&#039;); // will return the correct size
} else {
    if ($wouldblock) {
        // the file is currently locked by another application but we can do something else
    } else {
        // cannot acquire lock for other reason
    }
}
fclose($pointer);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce69fa23b609f3f8aea4b217bf30fd85fc");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h2>Final thoughts</h2></div><div><div><div>As you can see file locking is not a scary thing. It is quite useful if done properly. I will be happy for you to take the following from this post:</div><div><span style="font-style:italic;">Always lock your files (when reading and writing) if multiple applications/instances/processes&nbsp;have&nbsp;write access to those files.</span></div></div></div><div><h2>One more thing ...</h2></div><div><div>I would like to recommend you a library of mine that brings locking benefits to more complex tasks - tasks that you do not want to interfere with one another. The library is simply called <a href="https://github.com/ivopetkov/lock" title="Lock">Lock</a>, and you can find it at GitHub at&nbsp;<a href="https://github.com/ivopetkov/lock" title="https://github.com/ivopetkov/lock">https://github.com/ivopetkov/lock</a>.&nbsp;Its based on file locking and works great in apps that share a common filesystem.</div></div>]]></description><pubDate>Fri, 22 Dec 2017 13:20:47 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/reading-locked-files-in-php/</guid></item><item><title>Advanced PHP: 7 useful techniques that are rarely used</title><link>https://ivopetkov.com/b/advanced-php-7-useful-techniques-that-are-rarely-used/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/c762e0555702-c999999999/ejfnltsqd4fa15rk70cg.jpg" style="max-width:100%;"></div></div><div><div><div>In human languages, not all words are equally used, but they all have a purpose and unique power. The same is true for programming languages. A big part of the code we write is just variables, functions, classes, and loops. They are part of the basics you need to know to get your application done. Today I am not going to talk about them. Instead, I'll show you some of the PHP techniques I rarely use but have great impact.</div><div><br></div><div>Let's get started.</div></div></div><div><h2>Anonymous functions and classes</h2></div><div><div>Sometimes in your code, you must provide a callback function (in <a href="http://php.net/manual/en/function.usort.php" title="usort">usort</a> for example). Instead of defining a global function, you can create one dynamically, use it once or twice and remove it (if you want). It can also save you some time thinking about the right name (because naming things is important).</div></div><div><div>In the following example an anonymous function is used as a callback to help sort people by age.</div></div><div><code class="bearcms-code-element" id="bce0b5bbdab7593561ba75b7683521af687">$list = [
    [&#039;name&#039; =&gt; &#039;John&#039;, &#039;age&#039; =&gt; 31],
    [&#039;name&#039; =&gt; &#039;Mike&#039;, &#039;age&#039; =&gt; 25],
    [&#039;name&#039; =&gt; &#039;Ben&#039;, &#039;age&#039; =&gt; 27]
];
usort($list, function($a, $b) {
    if ($a[&#039;age&#039;] == $b[&#039;age&#039;]) {
        return 0;
    }
    return ($a[&#039;age&#039;] &lt; $b[&#039;age&#039;]) ? -1 : 1;
});</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce0b5bbdab7593561ba75b7683521af687");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Next, a function is dynamically created and saved into a&nbsp;variable, then called and finally destroyed.</div></div><div><code class="bearcms-code-element" id="bce2d1ad2b3e34029fdf9cffb3d8a0e9f8d">$sqrt = function($a) {
    return $a * $a;
};
echo $sqrt(2);
unset($sqrt);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce2d1ad2b3e34029fdf9cffb3d8a0e9f8d");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>PHP 7 adds support for <a href="http://php.net/manual/en/language.oop5.anonymous.php" title="anonymous classes">anonymous classes</a>, so you can get creative with that too.</div></div><div><h2>Autoloading classes</h2></div><div><div>It's a best practice to create one file per class when writing object-oriented applications in PHP. That way the classes are easier to develop and maintain and the application can load only the classes needed to complete the request. This can be achieved by registering a class autoload function that will include the class file when needed. So instead of writing:</div></div><div><code class="bearcms-code-element" id="bce4945a5e3e3deca7f00ed60805518d9ba">include &quot;some/dir/Class1.php&quot;
include &quot;some/dir/Class2.php&quot;
include &quot;some/dir/Class3.php&quot;</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce4945a5e3e3deca7f00ed60805518d9ba");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>you can write</div></div><div><code class="bearcms-code-element" id="bce6941b2c5e4b204df7704f759d066eee1">spl_autoload_register(function ($class) {
    include &quot;some/dir/&quot; . $class . &quot;.php&quot;;
});</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce6941b2c5e4b204df7704f759d066eee1");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>And then the following code will work just fine.</div></div><div><code class="bearcms-code-element" id="bcef131267582b14ca2dd1d58be9bca3abb">$object1 = new Class1();
$object2 = new Class2();
// Class3 will not be loaded because it&#039;s not needed</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bcef131267582b14ca2dd1d58be9bca3abb");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h2>Convert errors to exceptions</h2></div><div><div>Prior PHP 7 error reporting in PHP was a bit messy. In PHP 7 there are <a href="http://php.net/manual/en/language.errors.php7.php" title="some improvements">some improvements</a>. Converting errors to exceptions technique has served me well for years, so would like to show it to you. It's a couple of lines and will improve the way you manage errors.</div></div><div><code class="bearcms-code-element" id="bce07be45be61c0eff5480e2369f0cc6a2f">set_error_handler(function($errorNumber, $errorMessage, $errorFile, $errorLine) {
    throw new \ErrorException($errorMessage, 0, $errorNumber, $errorFile, $errorLine);
});</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce07be45be61c0eff5480e2369f0cc6a2f");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>So now the only thing you have to worry about is catching exceptions. That's a lot easier than suppressing errors, registering custom handlers and taking care of different error handling configurations (in php.ini, runtime, custom handlers and so on). Here is an example:</div></div><div><code class="bearcms-code-element" id="bceebaefc39a532b23502551bc6c8cb8b66">try {
    echo 5 / 0;
} catch (Exception $e) {
    print_r($e);
}</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bceebaefc39a532b23502551bc6c8cb8b66");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h2>Magic methods</h2></div><div><div><div>Object-oriented programming is very popular these days, and PHP provides a great way to bring it to the next level. Magic methods allow you to tweak calls to methods or properties of a class and update the object state when particular operation occur.</div><div><br></div><div>Imagine you have to create the following class:</div></div></div><div><code class="bearcms-code-element" id="bce025a9d58c928e2f7cf9fe1c3e8402260">class Person
{
    public $age = null;
    public $eyesColor = null;
    public $hairColor = null;
}</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce025a9d58c928e2f7cf9fe1c3e8402260");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>You can provide the magic method __construct to set the default values for the object created.</div></div><div><code class="bearcms-code-element" id="bcec3279d34aa50a7e3020dcd87899d86a6">class Person
{
    public $age = null;
    public $eyesColor = null;
    public $hairColor = null;
    function __construct(){
        $this-&gt;age = 20;
        $this-&gt;eyesColor = &#039;blue&#039;;
        $this-&gt;hairColor = &#039;brown&#039;;
    }
}</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bcec3279d34aa50a7e3020dcd87899d86a6");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>You can validate properties using __set and&nbsp;retrieve them using __get.</div></div><div><code class="bearcms-code-element" id="bce16ae9d3db29a10bd27a25c50079b11e8">class Person
{
    private $data = [];
    public $eyesColor = null;
    public $hairColor = null;
    function __set($name, $value)
    {
        if ($name === &#039;age&#039;) {
            if (is_int($value) &amp;&amp; $value &gt;= 18) {
                $this-&gt;data[$name] = $value;
            } else {
                throw new InvalidArgumentException(&#039;Age is invalid. Must be at least 18.&#039;);
            }
        }
    }
    function __get($name)
    {
        return isset($this-&gt;data[$name]) ? $this-&gt;data[$name] : null;
    }
    function __isset($name)
    {
        return isset($this-&gt;data[$name]);
    }
    function __unset($name)
    {
        if (isset($this-&gt;data[$name])) {
            unset($this-&gt;data[$name]);
        }
    }
}</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce16ae9d3db29a10bd27a25c50079b11e8");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>See the full list of magic methods at <a href="http://php.net/manual/en/language.oop5.magic.php" title="php.net">php.net</a>.</div></div><div><h2>Xdebug</h2></div><div><div>Debugging and profiling are a requirement when working on large applications and for years, there has been a tool called Xdebug that does this in a wonderful way. So now you can easily find the slow places in your code and fix them. Head over to <a href="https://xdebug.org/" title="xdebug.org">xdebug.org</a> for more information.</div></div><div><h2>register_shutdown_function()</h2></div><div><div>As the name implies this function registers a function that will be called when the execution of the request is about to finish. I often use it to check for fatal errors and print friendly output.</div></div><div><code class="bearcms-code-element" id="bce3fa0cccb00680da7ffcff4a92459ea50">register_shutdown_function(function() {
    $errorData = error_get_last();
    if (is_array($errorData)) {
        ob_end_clean();
        echo &#039;Error occured! - &#039; . $errorData[&#039;message&#039;];
    }
});</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce3fa0cccb00680da7ffcff4a92459ea50");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Another great use case is to log something.</div></div><div><h2>Command line</h2></div><div><div>PHP is mostly known as one the popular server languages that power our web pages. But it's not just for web pages. You can write useful script and programs that can be called from the command line. Here is an example:</div></div><div><code class="bearcms-code-element" id="bced7add528ce374602f97ca2bb71d2c84d">php hello-world.php -name John</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bced7add528ce374602f97ca2bb71d2c84d");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>And this is the code of the program. The variable&nbsp;$argv contains all the arguments passed. Index 0 is the filename.</div></div><div><code class="bearcms-code-element" id="bce06a54b0ba0ffbf8d65567c77a0ecc6ad">if (isset($argv[1]) &amp;&amp; $argv[1] === &#039;-help&#039;) {
    echo &#039;Enter -name &lt;your-name&gt; so I can greet you properly.&#039;;
    exit();
}
if (isset($argv[1], $argv[2]) &amp;&amp; $argv[1] === &#039;-name&#039;) {
    echo &#039;Hello, &#039; . $argv[2];
    exit();
}
echo &#039;Invalid command. Type -help for help.&#039;;
exit();</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce06a54b0ba0ffbf8d65567c77a0ecc6ad");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h2>Thanks</h2></div><div><div><div>I hope these techniques will be useful for you too.</div>All the code from the examples is available at my <a href="https://github.com/ivopetkov/ivopetkov.github.io/tree/master/7-php-features" title="GitHub repo">GitHub repo</a>.</div></div>]]></description><pubDate>Tue, 31 May 2016 08:08:40 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/advanced-php-7-useful-techniques-that-are-rarely-used/</guid></item><item><title>Install PHP and Apache from source</title><link>https://ivopetkov.com/b/install-php-and-apache-from-source/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/81bf1dda0885-c999999999/e4zg3aumj3fa1c85czyb.jpg" style="max-width:100%;"></div></div><div><div>Installing software in Linux using a package manager is easy as typing "yum install the-name-of-the-thing". But sometimes you want to make deep customizations or make multiple installations of the same software (different PHP or Apache httpd versions for example). Then the only thing you can do is install the software from its source, and this is not that easy. In the following lines you will learn how to install PHP (with PHP-FPM and OPcache) and Apache from source code.</div></div><div><h3>Installation steps</h3></div><div><div><div>The steps are the same for both PHP and Apache httpd.</div><div><ol><li>Download the archive containing the source and extract it to some directory</li><li>Install dependencies (if any)</li><li>Configure the installer (enable or disable features)</li><li>Run the installer</li><li>Configure the software</li></ol></div></div></div><div><h3>What to expect at the end</h3></div><div><div>After executing the following commands, you will have a fresh PHP installation, a running PHP-FPM daemon and an Apache HTTP server that will be executing PHP files with the help from the PHP-FPM daemon (Apache will proxy PHP files to PHP-FPM).</div></div><div><h2>Prerequisites</h2></div><div><div>Compiling PHP and Apache httpd requires a compiler. Let's install it.</div></div><div><code class="bearcms-code-element" id="bce6335c7a33c6122f367b1ee8991947f97">yum install gcc</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce6335c7a33c6122f367b1ee8991947f97");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Let's create a user that will be used by Apache and PHP and the dir where our files will be located</div></div><div><code class="bearcms-code-element" id="bce089e5b11b5b41455770540edc8c3aa3c">mkdir /var/www
groupadd www-group
useradd -d /var/www -g www-group -s /bin/false www-user
chown www-user:www-group /var/www</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce089e5b11b5b41455770540edc8c3aa3c");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Create the dir where the source will be downloaded and extracted (it may already exists).</div></div><div><code class="bearcms-code-element" id="bce42e1cf0ab55160f6e19652eb2661cfd1">mkdir /usr/local/src/</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce42e1cf0ab55160f6e19652eb2661cfd1");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h2>Installing PHP from source</h2></div><div><div>Create the dir where PHP will be installed</div></div><div><code class="bearcms-code-element" id="bce67aa6f38ee7c41330e6a1d35248f03c9">mkdir /opt/php-7.0.4</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce67aa6f38ee7c41330e6a1d35248f03c9");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Download the PHP source and extract it to the target directory</div></div><div><code class="bearcms-code-element" id="bce545b57d4183eb990a4eb9dc232289654">wget http://us.php.net/get/php-7.0.4.tar.bz2/from/this/mirror -O /usr/local/src/php-7.0.4.tar.bz2
tar jxf /usr/local/src/php-7.0.4.tar.bz2  -C /usr/local/src</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce545b57d4183eb990a4eb9dc232289654");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Install some dependencies</div></div><div><code class="bearcms-code-element" id="bce63a76967c858870e83fa1635d8e7b615">yum install libxml2 libxml2-devel</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce63a76967c858870e83fa1635d8e7b615");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Change the current directory</div></div><div><code class="bearcms-code-element" id="bce91e6271151942163f722f6304ff464be">cd /usr/local/src/php-7.0.4</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce91e6271151942163f722f6304ff464be");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Configure the installer</div></div><div><code class="bearcms-code-element" id="bce20f7fce065296d71151ea7df5aaabe41">./configure --prefix=/opt/php-7.0.4 --enable-fpm --enable-opcache</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce20f7fce065296d71151ea7df5aaabe41");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Build and install the software</div></div><div><code class="bearcms-code-element" id="bce2cf97e3cbb88f4cfa463c95a0ed56362">make &amp;&amp; make install</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce2cf97e3cbb88f4cfa463c95a0ed56362");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Create the configuration files (or copy the recommended ones) to their proper places</div></div><div><code class="bearcms-code-element" id="bceaea76e4318ff0a1131e59e231e115549">cp /usr/local/src/php-7.0.4/php.ini-production /opt/php-7.0.4/lib/php.ini
cp /opt/php-7.0.4/etc/php-fpm.conf.default /opt/php-7.0.4/etc/php-fpm.conf</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bceaea76e4318ff0a1131e59e231e115549");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>In the php.ini file (located at /opt/php-7.0.4/lib/php.ini) add the following code</div></div><div><code class="bearcms-code-element" id="bce65ee9245c746215d03d5aa5849294a3a">zend_extension=/opt/php-7.0.4/lib/php/extensions/no-debug-non-zts-20151012/opcache.so
opcache.enable = 1
opcache.enable_cli = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 10000
opcache.use_cwd = 0
opcache.validate_timestamps = 0
opcache.save_comments = 0
opcache.load_comments = 0
opcache.enable_file_override = 1</code><script>clientPackages.get("-bearcms-code-element-highlight-plaintext").then(function(o){var b=document.getElementById("bce65ee9245c746215d03d5aa5849294a3a");var r=o.library.highlight("plaintext",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>In the php-fpm.conf file (located at /opt/php-7.0.4/etc/php-fpm.conf) make some changes.</div><div><ul><li>Remove the following line (it should be last in the file)</li></ul></div></div></div><div><code class="bearcms-code-element" id="bce915fff9fbea73f8e007d34d84f7251fa">include=/opt/php-7.0.4/etc/php-fpm.d/*.conf</code><script>clientPackages.get("-bearcms-code-element-highlight-plaintext").then(function(o){var b=document.getElementById("bce915fff9fbea73f8e007d34d84f7251fa");var r=o.library.highlight("plaintext",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><ul><li>Add the following lines</li></ul></div></div><div><code class="bearcms-code-element" id="bcefb5a5eb25b39c231ca87b0d9d1428558">pid = run/php-fpm.pid
[www]
user = www-user
group = www-group
listen = 127.0.0.1:8999
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4</code><script>clientPackages.get("-bearcms-code-element-highlight-plaintext").then(function(o){var b=document.getElementById("bcefb5a5eb25b39c231ca87b0d9d1428558");var r=o.library.highlight("plaintext",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Start PHP-FPM</div></div><div><code class="bearcms-code-element" id="bce40bcc39e4d777a4534380ddef2d68a59">/opt/php-7.0.4/sbin/php-fpm --fpm-config /opt/php-7.0.4/etc/php-fpm.conf</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce40bcc39e4d777a4534380ddef2d68a59");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>You can stop it later with the following command</div></div><div><code class="bearcms-code-element" id="bce1098c9283dc6e2d29258bc3961978114">pkill php-fpm</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce1098c9283dc6e2d29258bc3961978114");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>It's done! Now we have a PHP installation and a running PHP-FPM daemon. Let's install Apache httpd and point it towards PHP-FPM.</div></div><div><h2>Installing Apache httpd from source</h2></div><div><div>Create the dir where Apache will be installed</div></div><div><code class="bearcms-code-element" id="bce7c186f89e1edc6f28d51a59afc975573">mkdir /opt/httpd-2.4.20</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce7c186f89e1edc6f28d51a59afc975573");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Download the Apache source and extract it to the target directory</div></div><div><code class="bearcms-code-element" id="bcef074645c7670471ac67ab777257c1012">wget http://apache.cbox.biz//httpd/httpd-2.4.20.tar.bz2 -O /usr/local/src/httpd-2.4.20.tar.bz2
tar jxf /usr/local/src/httpd-2.4.20.tar.bz2 -C /usr/local/src</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bcef074645c7670471ac67ab777257c1012");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Download some dependencies</div></div><div><code class="bearcms-code-element" id="bce599b45bcdd0562c195bacca6641f47b7">wget http://apache.cbox.biz//apr/apr-1.5.2.tar.bz2 -O /usr/local/src/httpd-2.4.20/srclib/apr-1.5.2.tar.bz2
tar jxf /usr/local/src/httpd-2.4.20/srclib/apr-1.5.2.tar.bz2 -C /usr/local/src/httpd-2.4.20/srclib
mv /usr/local/src/httpd-2.4.20/srclib/apr-1.5.2 /usr/local/src/httpd-2.4.20/srclib/apr
wget http://apache.cbox.biz//apr/apr-util-1.5.4.tar.bz2 -O /usr/local/src/httpd-2.4.20/srclib/apr-util-1.5.4.tar.bz2
tar jxf /usr/local/src/httpd-2.4.20/srclib/apr-util-1.5.4.tar.bz2 -C /usr/local/src/httpd-2.4.20/srclib
mv /usr/local/src/httpd-2.4.20/srclib/apr-util-1.5.4 /usr/local/src/httpd-2.4.20/srclib/apr-util
yum install pcre-devel</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce599b45bcdd0562c195bacca6641f47b7");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Change the current directory</div></div><div><code class="bearcms-code-element" id="bce769529b5eca911177515df57be00c18f">cd /usr/local/src/httpd-2.4.20/</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce769529b5eca911177515df57be00c18f");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Configure the installer</div></div><div><code class="bearcms-code-element" id="bce309c15e58b74a897864c25a70693a12f">./configure --prefix=/opt/httpd-2.4.20 --enable-so</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce309c15e58b74a897864c25a70693a12f");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Build and install the software</div></div><div><code class="bearcms-code-element" id="bceb0a50c7f3fe92c4bcc5b1d23ce46132e">make &amp;&amp; make install</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bceb0a50c7f3fe92c4bcc5b1d23ce46132e");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>In the httpd.conf file (located at /opt/httpd-2.4.20/conf/httpd.conf) make the following changes</div><div><ul><li>Enable (uncomment) these modules</li></ul></div></div></div><div><code class="bearcms-code-element" id="bcee9bb63c3eb208f15a2edb89b1f00bde2">LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so</code><script>clientPackages.get("-bearcms-code-element-highlight-apache").then(function(o){var b=document.getElementById("bcee9bb63c3eb208f15a2edb89b1f00bde2");var r=o.library.highlight("apache",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><ul><li>Change the user and the group</li></ul></div></div><div><code class="bearcms-code-element" id="bcea895c3821b763745693320523c186f3c">User www-user
Group www-group</code><script>clientPackages.get("-bearcms-code-element-highlight-apache").then(function(o){var b=document.getElementById("bcea895c3821b763745693320523c186f3c");var r=o.library.highlight("apache",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><ul><li>Add the following line at the end</li></ul></div></div><div><code class="bearcms-code-element" id="bceffc5d77c0807da3f5fb6803bcb6e8e82">ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:8999/var/www/$1</code><script>clientPackages.get("-bearcms-code-element-highlight-apache").then(function(o){var b=document.getElementById("bceffc5d77c0807da3f5fb6803bcb6e8e82");var r=o.library.highlight("apache",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Start Apache</div></div><div><code class="bearcms-code-element" id="bceeeef0f773b04c58de343f1170946598a">/opt/httpd-2.4.20/bin/apachectl start</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bceeeef0f773b04c58de343f1170946598a");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>You can stop it later by running</div></div><div><code class="bearcms-code-element" id="bced4d6a11da344c3fe574ab2e9d496e2bb">/opt/httpd-2.4.20/bin/apachectl stop</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bced4d6a11da344c3fe574ab2e9d496e2bb");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Create a test file to test the installations</div></div><div><code class="bearcms-code-element" id="bceaffe54646392835414854cb73b11f862">echo &quot;&lt;?php phpinfo();&quot; &gt;&gt; /var/www/info.php</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bceaffe54646392835414854cb73b11f862");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Done! We now have running Apache and PHP.</div></div>]]></description><pubDate>Wed, 04 May 2016 12:57:58 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/install-php-and-apache-from-source/</guid></item><item><title>A better HTML5 parser for PHP</title><link>https://ivopetkov.com/b/a-better-html5-parser-for-php/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/76766a8ee43d-c999999999/ep6dbnf3g2fa1cceona8.jpg" style="max-width:100%;"></div></div><div><div>HTML and PHP have existed for a long time, and one of the main use cases for PHP is to render HTML. Unfortunately, it is not that easy to parse and modify HTML in PHP, especially HTML5. That's why I created a simple library that will help you do that, and more, easily. It extends PHP's native <a href="http://php.net/manual/en/class.domdocument.php" title="DOMDocument">DOMDocument</a> library, so it is very familiar to use. DOMDocument is a very powerful library, but it doesn't work quite well with HTML5. So, meet <a href="https://github.com/ivopetkov/html5-dom-document-php" title="HTML5DOMDocument">HTML5DOMDocument</a> - an open source library that extends DOMDocument, fixes some issues and adds some functionality.</div></div><div><h2>The fixes</h2></div><div><div><div><span style="font-weight:bold;">Preserves white spaces</span> - the DOMDocument library removes some white spaces between text and HTML tags, and sometimes they are important.</div><div><br></div><div><span style="font-weight:bold;">Preserves &amp;nbsp;</span> - the DOMDocument library convertes &amp;nbsp; to space character (" ").</div><div><br></div><div><span style="font-weight:bold;">Preserves void tags</span> - the DOMDocument library converts &lt;source&gt; tag to &lt;source&gt;&lt;/source&gt;, which is an invalid HTML tag.</div></div></div><div><h2>The new</h2></div><div><div><span style="font-weight:bold;">Inserting HTML</span> - Sometimes you need to insert dynamically HTML snippet in an HTML code. The most common case is to append it to the bottom.&nbsp;You may also want&nbsp;to insert a whole HTML document. Then the head content will be added in the proper place.&nbsp;You can also specify an insert&nbsp;target for the body content. Here is an example:</div></div><div><code class="bearcms-code-element" id="bce8b98e8c43624da36ca40f3b8f2737436">$dom = new IvoPetkov\HTML5DOMDocument();
$dom-&gt;loadHTML(&#039;&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;body&gt;&lt;div&gt;Hello &lt;/div&gt;&lt;/body&gt;&lt;/html&gt;&#039;);
// Find the div element and appends insert target
$dom-&gt;querySelector(&#039;div&#039;)-&gt;appendChild($dom-&gt;createInsertTarget(&#039;target1&#039;));
// Inserts the HTML snipped into the insert target
$dom-&gt;insertHTML(&#039;&lt;html&gt;&lt;body&gt;world&lt;/body&gt;&lt;/html&gt;&#039;, &#039;target1&#039;);
echo $dom-&gt;saveHTML();
// Output will be: &lt;!DOCTYPE html&gt;&lt;html&gt;&lt;body&gt;&lt;div&gt;Hello world&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce8b98e8c43624da36ca40f3b8f2737436");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><span style="font-weight:bold;">Querying the DOM</span> - The method querySelectorAll is very popular in the JavaScript world only because it's very helpful, and now it's available for DOMDocuments in PHP. Here is an example:</div></div><div><code class="bearcms-code-element" id="bceb4098882fc4d5ac26d0fdfdba8327be0">$dom = new IvoPetkov\HTML5DOMDocument();
$dom-&gt;loadHTML(&#039;&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;body&gt;&lt;div&gt;Div 1&lt;/div&gt;&lt;div&gt;Div 2&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;&#039;);
$divElements = $dom-&gt;querySelectorAll(&#039;div&#039;);
// $divElements will be a DOMNodeList</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bceb4098882fc4d5ac26d0fdfdba8327be0");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>I hope you'll find this library helpful. Download and contribute at <a href="https://github.com/ivopetkov/html5-dom-document-php" title="GitHub">GitHub</a>.</div></div>]]></description><pubDate>Thu, 31 Mar 2016 15:22:47 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/a-better-html5-parser-for-php/</guid></item><item><title>Let&#039;s Encrypt on EC2</title><link>https://ivopetkov.com/b/let-s-encrypt-on-ec2/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/b0e1568b0fa9-c999999999/e7r2t2inf1fa1rz61a7t.jpg" style="max-width:100%;"></div></div><div><div><div>The year 2016 started with a couple of free SSL certificate solutions. <a href="https://www.cloudflare.com/" title="CloudFlare">CloudFlare</a> offers a free certificate for all their customers; Amazon joined with <a href="https://aws.amazon.com/certificate-manager/" title="AWS Certificate Manager">AWS Certificate Manager</a>, and <a href="https://letsencrypt.org/" title="Let's Encrypt">Let's Encrypt</a> is in public beta. Today I want to show you how easy it is to get a free certificate from Let's Encrypt and automatically renew it in the future. I'll provide the commands for an <a href="https://aws.amazon.com/amazon-linux-ami/" title="Amazon Linux AMI">Amazon Linux AMI</a>, but they are similar for other Linux distributions.</div><div><br></div><div>There are some things you should know about Let's Encrypt:</div><div><ol><li>Certificates last only 90 days.</li><li>You are "forced" to automate the process. Actually, this is great, and I'll show you how to do it.</li><li>They do not offer wildcard certificates.</li></ol></div></div></div><div><h2>Requirements</h2></div><div><div><div><ol><li>An email address. You may get useful information about your certificates.</li><li>The domain pointing to a directory on the server, that's accessible on the Internet. Let's Encrypt servers will access a file on http://example.com/some_secret_file_name to validate that you own the domain.</li><li>Clearly understand that Let's Encrypt is currently in beta, and you will be running a beta version software on your server.</li></ol></div></div></div><div><h2>The commands</h2></div><div><div>Install some requirements for the following steps.</div></div><div><code class="bearcms-code-element" id="bcea2b038feee7393a7706927163bcdf498">yum install python27-devel git</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bcea2b038feee7393a7706927163bcdf498");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Clone the letsencrypt repository and run the installer.</div></div><div><code class="bearcms-code-element" id="bce9de1147d3fa982c5ab5737584297e486">git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
/opt/letsencrypt/letsencrypt-auto --debug</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce9de1147d3fa982c5ab5737584297e486");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Create a config file that will be used for new certificates and renewals. It contains the private key size and your email address.</div></div><div><code class="bearcms-code-element" id="bced6d711dbb6356bb9ff7774199f74bf28">echo &quot;rsa-key-size = 4096&quot; &gt;&gt; /etc/letsencrypt/config.ini
echo &quot;email = email@example.com&quot; &gt;&gt; /etc/letsencrypt/config.ini</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bced6d711dbb6356bb9ff7774199f74bf28");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Request a certificate for your domain and it's www subdomain. You must also specify the root directory of the domain.</div></div><div><code class="bearcms-code-element" id="bce9797b8d70c6d4e8dbabb530376ddff8f">/opt/letsencrypt/letsencrypt-auto certonly --webroot -w /var/www/yourdomainroot -d yourdomain.com -d www.yourdomain.com --config /etc/letsencrypt/config.ini --agree-tos</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce9797b8d70c6d4e8dbabb530376ddff8f");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Remove the directory that was used for validation. This step is optional.</div></div><div><code class="bearcms-code-element" id="bce14a8557810bbd392d28763d5e9f241cb">rmdir /var/www/yourdomainroot/.well-known</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce14a8557810bbd392d28763d5e9f241cb");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>The certificates are located at /etc/letsencrypt/live/ and the last thing is to update your webserver's configuration. For apache it will look like this:</div></div><div><code class="bearcms-code-element" id="bcee0d24a98e6f36966d65d7b2922f74473">Listen 443
&lt;VirtualHost *:443&gt;
    ServerName yourdomain.com
    DocumentRoot &quot;/var/www/yourdomainroot&quot;
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/yourdomain.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/yourdomain.com/chain.pem
    SSLProtocol All -SSLv2 -SSLv3
    SSLHonorCipherOrder on
    SSLCipherSuite &quot;EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS&quot;
&lt;/VirtualHost&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-apache").then(function(o){var b=document.getElementById("bcee0d24a98e6f36966d65d7b2922f74473");var r=o.library.highlight("apache",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Be sure to add the renew command in a crontab. Refresing your webserver command should also be here.</div></div><div><code class="bearcms-code-element" id="bcefa8b682924d128e427e81376595f2a64">/opt/letsencrypt/letsencrypt-auto renew --config /etc/letsencrypt/config.ini --agree-tos &amp;&amp; apachectl graceful</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bcefa8b682924d128e427e81376595f2a64");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>That's it. I hope it was helpful.</div></div>]]></description><pubDate>Mon, 29 Feb 2016 10:04:08 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/let-s-encrypt-on-ec2/</guid></item><item><title>Object Storage</title><link>https://ivopetkov.com/b/object-storage/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/b3b1df4dfbea-c999999999/ezfdkvxti0fa1v19rmv1.jpg" style="max-width:100%;"></div></div><div><div><div>I've always been a fan of simplicity. And the simplest method to <span style="font-weight:bold;">store data</span>, and the first one that comes into mind when you are a newbie in programming, is the <span style="font-weight:bold;">file system</span>. The file API (read, write, delete, etc.) is simple, and the access is pretty fast (especially on SSDs). Also, you can easily list files, sync them and archive them. It's just beautiful and works for huge files too.</div><div><br></div><div>A couple of years ago, cloud services like <a href="https://aws.amazon.com/s3/" title="AWS S3">AWS S3</a> and <a href="https://cloud.google.com/storage/" title="Google Cloud Storage">Google Cloud Storage</a> emerged. They offered the same simple API. The files are called "objects" and are identified by keys, rather than directory names and file names. The object can have metadata, can be versioned and automatically deleted, and can be stored on multiple machines without changing the API. The only missing thing is searching for objects (the only way to search is by prefix).</div><div><br></div><div>I wanted all these features in my PHP apps locally and in production. And I wanted them available through a simple API. Simple API allows improvements in the backend to be made in the future. One of them could be using Memcached for speeding up some things.</div><div><br></div><div>So I've made <span style="font-weight:bold;">Object Storage</span> - a simple to use <span style="font-weight:bold;">PHP library</span> for storing objects as files.</div><div>Here are some of the ...</div></div></div><div><h3>Highlights</h3></div><div><div><div><ul><li><span style="font-weight:bold;">Basic operations</span>: set, get, delete, append, duplicate, rename and search.</li><li><span style="font-weight:bold;">Objects can have metadata</span>. You can add multiple name-value pairs.</li><li><span style="font-weight:bold;">Atomic operations</span>. A write operation ("set" for example) locks the object until it's done and other write operations cannot interfere with the one currently running.</li><li><span style="font-weight:bold;">Transactions</span>. You can execute multiple actions on multiple objects that will be applied atomically. This is achieved by locking all files that need modification, prior execution.&nbsp;</li></ul></div></div></div><div><h3>Examples</h3></div><div><code class="bearcms-code-element" id="bce35622854cab75736891b78032759f0e2">$storage = new ObjectStorage(&#039;data/&#039;);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce35622854cab75736891b78032759f0e2");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>When you construct the Object Storage client, you provide the directory where the object will be saved. In that directory will be created three more directories:</div><div><ul><li><span style="font-weight:bold;">objects/</span> - stores the objects as files</li><li><span style="font-weight:bold;">metadata/</span> - storage the objects metadata</li><li><span style="font-weight:bold;">temp/</span> - stores the library internal data</li></ul></div></div></div><div><div><span>Setting data</span></div></div><div><code class="bearcms-code-element" id="bced298ee5d7d009c3930bd4c5dd3cc3907">$storage-&gt;set([
    &#039;key&#039; =&gt; &#039;users/1&#039;,
    &#039;body&#039; =&gt; &#039;{&quot;name&quot;:&quot;John Smith&quot;,&quot;email&quot;:&quot;john@example.com&quot;}&#039;,
    &#039;metadata.lastAccessTime&#039; =&gt; (string) time()
]);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bced298ee5d7d009c3930bd4c5dd3cc3907");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Getting data</div></div><div><code class="bearcms-code-element" id="bce4775c9d8e7e18b590100762641659197">$result = $storage-&gt;get([
    &#039;key&#039; =&gt; &#039;users/1&#039;,
    &#039;result&#039; =&gt; [&#039;body&#039;, &#039;metadata&#039;]
]);
/*
If the object exists $result will be:
Array
(
    [body] =&gt; {&quot;name&quot;:&quot;John Smith&quot;,&quot;email&quot;:&quot;john@example.com&quot;}
    [metadata.lastAccessTime] =&gt; 1234567890
)
If the object does not exists $result will empty array.
*/</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce4775c9d8e7e18b590100762641659197");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Appending data</div></div><div><code class="bearcms-code-element" id="bced8f5063c5755672987b4ce73b99fd766">$storage-&gt;append([
    &#039;key&#039; =&gt; &#039;visits/ip.log&#039;,
    &#039;body&#039; =&gt; &quot;123.123.123.123\n&quot;
]);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bced8f5063c5755672987b4ce73b99fd766");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Duplicating data</div></div><div><code class="bearcms-code-element" id="bcea71d5c6dfc262c5972500ce7eb26dbe3">$storage-&gt;duplicate([
    &#039;sourceKey&#039; =&gt; &#039;users/1&#039;,
    &#039;targetKey&#039; =&gt; &#039;users/2&#039;
]);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bcea71d5c6dfc262c5972500ce7eb26dbe3");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Renaming data</div></div><div><code class="bearcms-code-element" id="bce6cd27f25b5c99dd8b41cc5d77cbe806f">$storage-&gt;rename([
    &#039;sourceKey&#039; =&gt; &#039;users/2&#039;,
    &#039;targetKey&#039; =&gt; &#039;users/3&#039;
]);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce6cd27f25b5c99dd8b41cc5d77cbe806f");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Deleting data</div></div><div><code class="bearcms-code-element" id="bcecb70b7c2b3a2405c7e10e88ba91365ca">$storage-&gt;delete([
    &#039;key&#039; =&gt; &#039;users/3&#039;
]);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bcecb70b7c2b3a2405c7e10e88ba91365ca");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Searching for data</div></div><div><code class="bearcms-code-element" id="bcea4345a66fa68c52676d81201b6538cbb">$result = $storage-&gt;search([
    &#039;where&#039; =&gt; [
        [&#039;key&#039;, [&#039;users/1&#039;]]
    ],
    &#039;result&#039; =&gt; [&#039;key&#039;, &#039;body&#039;]
]);
/*
The result is array of arrays containing the matching objects
Array
(
    [0] =&gt; Array
        (
            [key] =&gt; users/1
            [body] =&gt; {&quot;name&quot;:&quot;John Smith&quot;,&quot;email&quot;:&quot;john@example.com&quot;}
        )
)
*/

// You can also use regular expresions
$result = $storage-&gt;search([
    &#039;where&#039; =&gt; [
        [&#039;key&#039;, &#039;^users\/&#039;, &#039;regexp&#039;]
    ],
    &#039;result&#039; =&gt; [&#039;key&#039;, &#039;body&#039;]
]);</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bcea4345a66fa68c52676d81201b6538cbb");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Multiple operations</div></div><div><code class="bearcms-code-element" id="bce7f4496b7b1f11100c1651faa7d86c3b2">$result = $storage-&gt;execute(
    [
        [
            &#039;command&#039; =&gt; &#039;set&#039;,
            &#039;key&#039; =&gt; &#039;users/9&#039;,
            &#039;body&#039; =&gt; &#039;{&quot;name&quot;:&quot;John Smith&quot;,&quot;email&quot;:&quot;john@example.com&quot;}&#039;
        ],
        [
            &#039;command&#039; =&gt; &#039;append&#039;,
            &#039;key&#039; =&gt; &#039;emails.log&#039;,
            &#039;body&#039; =&gt; &#039;john@example.com&#039;
        ],
        [
            &#039;command&#039; =&gt; &#039;set&#039;,
            &#039;key&#039; =&gt; &#039;emails/&#039; . md5(&#039;john@example.com&#039;),
            &#039;body&#039; =&gt; &#039;9&#039;
        ],
        [
            &#039;command&#039; =&gt; &#039;get&#039;,
            &#039;key&#039; =&gt; &#039;users/9&#039;,
            &#039;result&#039; =&gt; [&#039;body&#039;]
        ],
    ]
);
/*
The $result variable will be:
Array
(
    [0] =&gt; true // the result of the first operation
    [1] =&gt; true  // the result of the second operation
    [2] =&gt; true  // the result of the third operation
    [3] =&gt; Array  // the result of the fourth operation
        (
            [body] =&gt; {&quot;name&quot;:&quot;John Smith&quot;,&quot;email&quot;:&quot;john@example.com&quot;}
        )
)
*/</code><script>clientPackages.get("-bearcms-code-element-highlight-php").then(function(o){var b=document.getElementById("bce7f4496b7b1f11100c1651faa7d86c3b2");var r=o.library.highlight("php",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>The source and the latest release are available at <a href="https://github.com/ivopetkov/object-storage" title="GitHub">GitHub</a>.</div></div>]]></description><pubDate>Sun, 31 Jan 2016 15:58:05 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/object-storage/</guid></item><item><title>Speed up your website with CloudFront</title><link>https://ivopetkov.com/b/speed-up-your-website-with-cloudfront/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/194057daf80f-c999999999/ezvybedayxea1lknty1g.jpg" style="max-width:100%;"></div></div><div><div><div>We all love fast websites, and there are many techniques to make a website fast. In this post, you'll learn how to make your website faster by utilizing <a href="https://aws.amazon.com/cloudfront/" title="CloudFront">CloudFront</a>. This website does. CloudFront is a CDN (<a href="https://en.wikipedia.org/wiki/Content_delivery_network" title="Content Delivery Network">Content Delivery Network</a>) provided by <a href="https://aws.amazon.com/" title="Amazon Web Services">Amazon Web Services</a>.</div><div><br></div><div>You may have heard that CDNs are great for static content. They store it closer to the visitors (on edge servers), deliver it very fast and offload our server. It's true. And what about dynamic content? The dynamic content is actually a static content with TTL of 0. In this case, CloudFront will proxy the content and will not cache it.</div><div><br></div><div>What we are going to do now is position CloudFront in front of our website and make it cache content (or just proxy it) based on the headers (Cache-Control) sent from our server. It sounds easy and really is.</div><div><br></div><div>In the following example, we will speed up ivopetkov.com. The first thing is to make our website accessible via another domain. I'm using the subdomain origin.ivopetkov.com, which points to the exact same place as ivopetkov.com. Later we'll point ivopetkov.com to CloudFront, and point CloudFront to origin.ivopetkov.com.</div></div></div><div><div>Now it the best time to <a href="https://aws.amazon.com/" title="create AWS account">create AWS account</a> if you don't have one.</div></div><div><h2>Create a CloudFront distribution</h2></div><div><div><div>Using the <a href="https://aws.amazon.com/console/" title="AWS Management Console">AWS Management Console</a> find the CloudFront service and click Create Distribution.</div></div></div><div><div><img src="https://ivopetkov.com/assets/7560ce750d71-c999999999/eksh2op9yxea18n9wfn8.png" style="max-width:100%;"></div></div><div><div>In the first screen select "Web distribution".</div></div><div><div><img src="https://ivopetkov.com/assets/22eb88090a83-c999999999/eu6h4ogdxxea1hq5trfo.png" style="max-width:100%;"></div></div><div><div>The following settings makes the distribution proxy or cache everything based on the response headers from the server. It's a very basic configuration, but it gets the job done.</div></div><div><div><img src="https://ivopetkov.com/assets/f2028845e9bf-c999999999/e0hboqbdxxea1lky5iix.png" style="max-width:100%;"></div></div><div><div>It may take up to 15 minutes for your distribution to be ready for use.</div></div><div><h2>Using a CloudFront distribution</h2></div><div><div>After your distribution is ready, you'll get a unique domain name (looks like abcdefghijk.cloudfront.net). You can open it, and you should see your website.</div></div><div><div><img src="https://ivopetkov.com/assets/84bd692b1416-c999999999/e0m93jmnxxea11lxtntu.png" style="max-width:100%;"></div></div><div><div>The next step is to point your visitors this way. <a href="https://aws.amazon.com/route53/" title="Route 53">Route 53</a> (the AWS DNS Service) makes this very easy. You just need to create an A type DNS record and make it an alias.</div></div><div><div><img src="https://ivopetkov.com/assets/e170b3c6a51a-c999999999/ey1ky8qexxea17intq9s.png" style="max-width:100%;"></div></div><div><h2>Cache headers</h2></div><div><div>All you must do now is make sure your server returns correct Cache-Control header for every request. It usually looks like this:</div></div><div><code>Cache-Control: max-age=60, public</code></div><div><h2>The results and the cost</h2></div><div><div><div>The results depend on many things. Some of them are:</div><div>- how many requests are made on each pageview</div><div>- what amount of data is transferred on request</div><div>- where you visitors are located (how close to <a href="http://aws.amazon.com/cloudfront/details/" title="CloudFront edge location">CloudFront edge location</a>)</div><div>- how popular is your content (does it hit the CacheFront cache)</div><div><br></div><div>Some other performance benefits are:</div><div>- Persistent connections are used between your visitors and the edge servers and between the edge servers and your server. This makes loading more than one resource faster.</div><div>- You server take it easy when traffic spikes occur and you can actually lower your server bill.</div><div>- The cache-control headers tell CloudFront to cache the content, but users' browsers also cache it.</div><div><br></div><div>AWS offers&nbsp;<a href="https://aws.amazon.com/free/" title="a free tier">a free tier</a> so you can test this technique for free very easily. Take a look at the <a href="https://aws.amazon.com/cloudfront/pricing/" title="pricing">pricing</a> too. This <a href="http://calculator.s3.amazonaws.com/index.html" title="calculator">calculator</a> will make estimating costs easier.</div></div></div>]]></description><pubDate>Thu, 12 Nov 2015 11:19:55 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/speed-up-your-website-with-cloudfront/</guid></item><item><title>HTML Server Components</title><link>https://ivopetkov.com/b/html-server-components/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/5f0958383444-c999999999/ekqr9f42yvea41fegnna5.jpg" style="max-width:100%;"></div></div><div><div><div>HTML was created a long time ago. We started building documents that contained only text, images and links. We enhanced them with CSS and JavaScript. We built image galleries with fancy animations, drop downs and many other UI elements. We started using Ajax. Now we use tools like <a href="https://jquery.com/" title="jQuery">jQuery</a>, <a href="http://facebook.github.io/react/" title="React">React</a>, <a href="https://angularjs.org/" title="AngularJS">AngularJS</a> in the browser. On the server, we've got frameworks, compilers, preprocessors. All these tools help us prepare the HTML, CSS and JavaScript code that browsers render to our visitors.</div><div><br></div><div>When we learn a new language, we start with a simple "Hello world". This is how it looks in HTML.</div></div></div><div><code class="bearcms-code-element" id="bcecbc3fd6c706ad9156f7a78f537fc8ce3">&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Hi&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        Hello world
    &lt;/body&gt;
&lt;/html&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bcecbc3fd6c706ad9156f7a78f537fc8ce3");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>But the real websites look like this</div></div><div><code class="bearcms-code-element" id="bcef116e92d4ea1791e4670d9121baef485">&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Hi&lt;/title&gt;
        &lt;script src=&quot;/assets/jquery.js&quot; /&gt;
        &lt;script src=&quot;/assets/lightbox.js&quot; /&gt;
        &lt;link href=&quot;/assets/lightbox.css&quot; /&gt;
        &lt;style&gt; ... &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;Breaking news!&lt;/h1&gt;
        &lt;!-- Image gallery --&gt;
        &lt;div&gt;
            &lt;img ... &gt;
            &lt;img ... &gt;
        &lt;/div&gt;
        &lt;!-- Social buttons --&gt;
        &lt;script&gt; ... &lt;/script&gt;
        &lt;!-- Comment box --&gt;
        &lt;script&gt; ... &lt;/script&gt;
        &lt;!-- Analytics --&gt;
        &lt;script&gt; ... &lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bcef116e92d4ea1791e4670d9121baef485");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>As you can see:</div><div><ol><li>there are a lot of JS and CSS files included</li><li>there is a lot of inlined JS and CSS code</li><li>there is a lot of HTML code for different UI elements</li></ol><div><br></div></div><div>The final code is messy, so we need tools to help us do our job.</div></div></div><div><h2>Something new</h2></div><div><div><div>Today I would like to present you a new concept for building HTML documents that are full of different components (fancy galleries, social share buttons, 3rd party comment boxes and so on). It looks a lot like <a href="http://facebook.github.io/react/" title="React">React</a>, <a href="http://webcomponents.org/" title="WebComponents">WebComponents</a> and <a href="https://mustache.github.io/" title="Template engines">Template engines</a>, but it's also a little different.</div><div><br></div><div>Let's start with the following HTML code:</div></div></div><div><code class="bearcms-code-element" id="bce6402aeb6c52eecf9ce5fc496d5aae8ae">&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Hi&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        Hello world
    &lt;/body&gt;
&lt;/html&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce6402aeb6c52eecf9ce5fc496d5aae8ae");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>When we add an image gallery with carousel effect we got the following code:</div></div><div><code class="bearcms-code-element" id="bce3a48e2ceb828cd4a2725935da25e517c">&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Hi&lt;/title&gt;
        &lt;script src=&quot;/assets/carousel.js&quot; /&gt;
        &lt;link href=&quot;/assets/carousel.css&quot; /&gt;
    &lt;/head&gt;
    &lt;body&gt;
        Hello world
        &lt;div&gt;
            &lt;img ... &gt;
            &lt;img ... &gt;
        &lt;/div&gt;
        &lt;script&gt; initializeCarousel(); &lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce3a48e2ceb828cd4a2725935da25e517c");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>As you can see we as developers must do the following three things:</div><div>1. Include the JavaScript and CSS files of the library we've selected in the document head tag.</div><div>2. Write the HTML code in the body tag.</div><div>3. Call a JavaScript function to make the carousel work.</div><div><br></div><div>Often we write the different parts in different files, especially when we use frameworks and template engines. And this is where the things get really messy:</div><div><ol><li>we include libraries more than once</li><li>we include things we don't use on every page</li><li>CSS and JavaScript conflicts occur</li></ol></div></div></div><div><h2>A simple concept</h2></div><div><div><div>The concept is ... components, just like <a href="http://facebook.github.io/react/" title="React">React</a> and <a href="http://webcomponents.org/" title="WebComponents">WebComponents</a>, but on the server.</div><div><br></div><div>A component is just an HTML document (with &lt;html&gt; and &lt;body&gt; tags). We create one for each part of the HTML page (header, footer, carousel, product list, social plugin). Then we merge them into the final HTML result.</div><div><br></div><div>In this example, we've created two component files:</div></div></div><div><code class="bearcms-code-element" id="bceb03396fe54e03149441cac0533e8aa7a">&lt;!-- main.html --&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Hi&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;component src=&quot;file:body.html&quot; /&gt;
    &lt;/body&gt;
&lt;/html&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bceb03396fe54e03149441cac0533e8aa7a");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><code class="bearcms-code-element" id="bce1c91a807f74f0c787e71fc478a75acb9">&lt;!-- body.html --&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;style&gt;
            h1{ font-size: 50px; }
        &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;Hello world&lt;/h1&gt;
    &lt;/body&gt;
&lt;/html&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce1c91a807f74f0c787e71fc478a75acb9");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>When we process them, we get</div></div><div><code class="bearcms-code-element" id="bcef4989eb5354ce51e170ccf58acf91c5e">&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Hi&lt;/title&gt;
        &lt;style&gt;
            h1{ font-size: 50px; }
        &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;Hello world&lt;/h1&gt;
    &lt;/body&gt;
&lt;/html&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bcef4989eb5354ce51e170ccf58acf91c5e");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>Here is what happened</div><div>1. The &lt;component src="file:body.html" /&gt; tag is replaced with the content of the body tag from body.html</div><div>2. The style tag from body.html is moved to the head of the result document.</div><div><br></div><div>Simple, yet very powerful for real-world websites.</div><div><br></div><div>And of course, components can communicate using attributes.</div></div></div><div><code class="bearcms-code-element" id="bce610a731cb8ebbf2680447a84690fffc8">&lt;component src=&quot;file:facebook-share-button.php&quot; url=&quot;http://...&quot;&gt;
&lt;component src=&quot;file:navigation.php&quot; selected=&quot;contact-us&quot;&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce610a731cb8ebbf2680447a84690fffc8");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>And they can be nested too.</div></div><div><h2>The benefits</h2></div><div><div><div><ol><li>All the code needed for one component to work is in the component's file. It's easy to develop, test and debug.</li><li>If the component is not used on the current page it's dependencies are not loaded (jQuery for example)</li><li>Components can be easily shared</li><li>Components can be easily reused and extended</li></ol></div><div><br></div><div>It's easier, faster and simpler to develop websites using HTML Server Components. Need proof? Here is a <a href="http://ivopetkov.github.io/demos/html-server-components/" title="sample web page build in PHP">sample web page build in PHP</a>. View the source at <a href="https://github.com/ivopetkov/ivopetkov.github.io/tree/master/demos/html-server-components/" title="GitHub">GitHub</a>. A very lightweight compiler (written in PHP) is used. It's available at <a href="https://github.com/ivopetkov/html-server-components-compiler" title="GitHub">GitHub</a> too. It's only job is to merge the components by putting the different elements in the right places.</div></div></div><div><h2>What&#039;s next</h2></div><div><div><div>In this article, I presented you a concept that I call HTML Server Components, an example web page and a PHP compiler.</div><div><br></div><div>The things I've planned for future updates are:</div><div>1. CSS preprocessors (<a href="http://lesscss.org/" title="Less">Less</a>, <a href="http://sass-lang.com/" title="Sass">Sass</a>) integration</div><div><div>2. External JS and CSS files merging to minimize requests</div><div>3. Format options. You may want your output HTML pretty or minified</div><div>4. CSS and JavaScript minification</div><div>5. Inlining CSS and JavaScript files so there are fewer requests</div><div>6. Moving inlined CSS and JavaScript code to files</div><div>7. Template engines integration</div></div></div></div><div><h2>Contribute</h2></div><div><div>I'd love to hear your comments and ideas.&nbsp;The PHP compiler is really small and it's free on <a href="https://github.com/ivopetkov/html-server-components-compiler" title="GitHub">GitHub</a>. If you want you can fork it or port it to other languages.</div></div>]]></description><pubDate>Thu, 10 Sep 2015 21:22:24 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/html-server-components/</guid></item><item><title>Lazy load responsive images</title><link>https://ivopetkov.com/b/lazy-load-responsive-images/</link><description><![CDATA[<div><div><i>Note: This article is still useful but there is a <a href="https://ivopetkov.com/b/responsively-lazy-v3-is-even-better-for-lazy-loading-images/" title="">new version</a> with an updated API.<br></i></div></div><div><div><img src="https://ivopetkov.com/assets/ffffe1455200-c999999999/ejwk7o8louea41p1ekftl.jpg" style="max-width:100%;"></div></div><div><div>Let's start with a brief intro if you've never heard of <span style="font-weight:bold;">lazy loading</span> HTML images.</div></div><div><div><span style="font-style:italic;">Lazy loading is a technique that defers image loading until it becomes visible on the screen. This saves bandwidth and can drastically improve page loading time and user experience.</span></div></div><div><div>There are few ways to implement lazy image loading. The most popular of them looks like this:</div></div><div><code class="bearcms-code-element" id="bced73c44f6c1fac844928cab7a3acb9f73">&lt;img src=&quot;blank.gif&quot; data-src=&quot;image.jpg&quot; /&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bced73c44f6c1fac844928cab7a3acb9f73");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>The blank.gif is a 1x1 transparent image file that loads very quickly because of it's tiny size. Then JavaScript code checks if the image is visible on the screen (after page load or page scroll) and replaces the value of the src attribute with the value of the data-src attribute. This triggers the loading of the original image. It's a very simple and powerful implementation, but it has some drawbacks.</div><div><br></div><div>One of the them is the lack of responsive images support. Hopefully, this can be easily fixed. The updated version looks like this:</div></div></div><div><code class="bearcms-code-element" id="bce0cbe3d27b1d02c975fb3ded70df9ce3c">&lt;img src=&quot;blank.gif&quot; data-srcset=&quot;image1.jpg 100w, image2.jpg 200w, image3.jpg 300w&quot; /&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce0cbe3d27b1d02c975fb3ded70df9ce3c");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>The available image versions with their corresponding width are listed in the data-srcset attribute, and the appropriate image is selected.</div><div><br></div><div>Another big drawback is the blank.gif file itself. This image is bad for SEO, social networks sharing and read-it-later tools. You'll miss valuable traffic from Google Images, your posts in Facebook will not contain images (unless og:image meta tag is specified), they will not be shareable on Pinterest and the important graphics in your article will not be visible in Pocket. And again there is a fix:</div></div></div><div><code class="bearcms-code-element" id="bce0390ed7826b771a812871abdbc2c9a84">&lt;img src=&quot;small-image.jpg&quot; data-src=&quot;image.jpg&quot; /&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce0390ed7826b771a812871abdbc2c9a84");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>The small-image.jpg file is a scaled down version of the original image. Let's say 200px in width and 10kb in size.</div><div>Loading this image is faster than loading the original image but still requires bandwidth and another HTTP request, so it's not the perfect solution.</div><div><br></div><div>Also, it will be great to set the image size to avoid reflowing when the original image url is set. Here is an updated version for fixed-sized images:</div></div></div><div><code class="bearcms-code-element" id="bcef8437d3b2b126833180043655aaa53f4">&lt;img src=&quot;blank.gif&quot; data-src=&quot;image.jpg&quot; style=&quot;width:800px;height:600px;&quot; /&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bcef8437d3b2b126833180043655aaa53f4");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>and a version for fluid (responsive) ones:</div></div><div><code class="bearcms-code-element" id="bce563348386b9a8fa2ef5841e2f6a9df73">&lt;div style=&quot;position:relative;height:0;padding-bottom:75%;&quot;&gt;
    &lt;img 
        src=&quot;blank.gif&quot;
        data-srcset=&quot;image1.jpg 100w, image2.jpg 200w, image1.jpg 300w&quot;
        style=&quot;position:absolute;top:0;left:0;width:100%;height:100%;&quot;
    /&gt;
&lt;/div&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce563348386b9a8fa2ef5841e2f6a9df73");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Read about this technique <a href="http://alistapart.com/article/creating-intrinsic-ratios-for-video" title="here">here</a>.<div><br></div><div>And now it's time for ...</div></div></div><div><h3>The best implementation</h3></div><div><div><div>The following implementation:</div><div>- is called&nbsp;Responsively Lazy (<a href="https://github.com/ivopetkov/responsively-lazy" title="available on GitHub">available on GitHub</a>)</div><div>- handles responsive images</div><div>- does not make ANY unnecessary requests</div><div>- works on modern browsers that support srcset. As of December 2017 that's&nbsp;<span><a href="https://caniuse.com/#feat=srcset" title="86.78%"><span>86.78</span>%</a></span>.</div><div>- handles loaded image resizes (when the phone is rotated from portrait to landscape). It's a nice bonus.</div><div>- does not break the page when an old browser is used</div><div>- works great in read-it-later tools like Pocket</div><div>- works great in content sharing on social networks like Facebook and Pinterest</div><div>- supports <a href="https://en.wikipedia.org/wiki/WebP" title="WebP">WebP</a></div><div><br></div><div>Here is an example:</div></div></div><div><code class="bearcms-code-element" id="bcec082e5b7f92be29282f0c3db34270891">&lt;div class=&quot;responsively-lazy&quot; style=&quot;padding-bottom:68.44%;&quot;&gt;
    &lt;img
        alt=&quot;&quot;
        src=&quot;images/2500.jpg&quot;
        srcset=&quot;data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&quot;
        data-srcset=&quot;images/400.jpg 400w, images/600.jpg 600w, images/800.jpg 800w, images/1000.jpg 1000w, images/1500.jpg 1500w, images/2000.jpg 2000w&quot;
    /&gt;
&lt;/div&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bcec082e5b7f92be29282f0c3db34270891");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>And this is how the magic happens:</div><div><br></div><div>- The responsively-lazy class and the padding-bottom style save space for the image and eliminate reflowing. The padding value is calculated using the following formula: imageHeight/imageWidth*100. Learn more&nbsp;<a href="http://alistapart.com/article/creating-intrinsic-ratios-for-video" title="here">here</a>. A different HTML code is available at the end of the article in case you don't know the image size.</div><div><br></div><div>- The data:image in the srcset attribute shows a transparent image and prevents loading the image in the src attribute in modern browsers. That's <span style="font-weight:bold;">the most important part</span>.</div><div><br></div><div>- The image in the src is attribute is used if the browser does not support the srcset attribute or when the page is scanned by social networks and read-it-later tools.</div><div><br></div><div>- The data-srcset attribute contains the available image versions with their corresponding width. The best version is selected based on the space available and the device pixel ratio.</div></div></div><div><h3>A brand new concept</h3></div><div><div>Other lazy loading libraries make you break your HTML by removing the src attribute, or make you put tiny version there or make you use &lt;noscript&gt; to make your images appear in search engines. The following code has worked for ages:</div></div><div><code class="bearcms-code-element" id="bce4f1f62baa037000043d750fea4773344">&lt;img src=&quot;image.jpg&quot; /&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce4f1f62baa037000043d750fea4773344");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Responsively Lazy enhances it without breaking it.</div></div><div><code class="bearcms-code-element" id="bce16b535e8c39bfdeb3b611f1b2d7094d7">&lt;img src=&quot;image.jpg&quot; data-src=&quot;image-200.jpg 200w, image-400.jpg 400w&quot; srcset=&quot;...&quot; /&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce16b535e8c39bfdeb3b611f1b2d7094d7");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h3>Customize</h3></div><div><div>You can drop the div tag and move the&nbsp;responsively-lazy class name to the img tag if you don't know the image aspect ratio.</div></div><div><code class="bearcms-code-element" id="bce6fdd2fa51c52d67ef5ff14a95de70dd7">&lt;img
    alt=&quot;&quot;
    class=&quot;responsively-lazy&quot;
    src=&quot;images/2500.jpg&quot;
    srcset=&quot;data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&quot;
    data-srcset=&quot;images/400.jpg 400w, images/600.jpg 600w, images/800.jpg 800w, images/1000.jpg 1000w, images/1500.jpg 1500w, images/2000.jpg 2000w&quot;
/&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-html").then(function(o){var b=document.getElementById("bce6fdd2fa51c52d67ef5ff14a95de70dd7");var r=o.library.highlight("html",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><a title="" href="http://ivopetkov.github.io/responsivelyLazy/" tabindex="0">VIEW DEMO</a></div><div><div>The source is available at <a href="https://github.com/ivopetkov/responsively-lazy/" title="https://github.com/ivopetkov/responsively-lazy/">https://github.com/ivopetkov/responsively-lazy/</a></div></div><div><div>So, are you ready to use lazy loading for your website's images?</div></div>]]></description><pubDate>Wed, 19 Aug 2015 19:27:51 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/lazy-load-responsive-images/</guid></item><item><title>Installing SSL certificate on Apache</title><link>https://ivopetkov.com/b/installing-ssl-certificate-on-apache/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/65bbcc7d5473-c999999999/er6kx94bfuea41im6tllq.jpg" style="max-width:100%;"></div></div><div><div><div>Now you know <a href="http://ivopetkov.com/b/how-ssl-works/" title="how SSL works">how SSL works</a> and how <a href="http://ivopetkov.com/b/getting-ssl-certificate/" title="you can get one">you can get one</a>. It's time to secure our website connection.</div><div><br></div><div>The first thing we need to do is ensure the private key, the certificate, and the CA's intermediate certificates are located on the machine where the website is hosted but not in the website root directory. We must keep the private key secure.</div><div><br></div><div>Here is how the private key (located at /var/certificates/example.com.key) file should look like:</div></div></div><div><code class="bearcms-code-element" id="bce9fc0386c698e187dd6b79a8a4da6d85b">-----BEGIN PRIVATE KEY-----
MIIEvgIBADANx715AmXSr8RTIQfIgAAECggEANYzLwNPLOlRfwE81BEoeBgkqklW
...
UshV/35RY677Xe739BYnl+hZO5DBGNPeU/grdKdTqyZD/b2DtS5bK
-----END PRIVATE KEY-----</code><script>clientPackages.get("-bearcms-code-element-highlight-plaintext").then(function(o){var b=document.getElementById("bce9fc0386c698e187dd6b79a8a4da6d85b");var r=o.library.highlight("plaintext",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>This is how the certificate file (located at /var/certificates/example.com.crt) should look like:</div></div><div><code class="bearcms-code-element" id="bceedc982cc4781983e87a8025fff0248a5">-----BEGIN CERTIFICATE-----
MIIFQjCCBCa9qjIx2VwiQeoLBzE0pahyM4Z9MJLNE5tOuQOIFZhbGlkYXRlZg7EB
...
HLmJnggdhbqMjQjZWipQ
-----END CERTIFICATE-----</code><script>clientPackages.get("-bearcms-code-element-highlight-plaintext").then(function(o){var b=document.getElementById("bceedc982cc4781983e87a8025fff0248a5");var r=o.library.highlight("plaintext",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>And finally this is how the intermediates file (located at /var/certificates/example.com.chain) should look like:</div></div><div><code class="bearcms-code-element" id="bce2eff20290a6317fb4c2b4dcd656a6bcb">-----BEGIN CERTIFICATE-----
MIIFdDCCBFygAwIB01PRE8gUlNBIENlcngKCAgEAkehUktIKVrGsDKCAgEAkehUk
...
pQ8pTIqXOi6YEbvFScL
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGCDCCA/CgAw1jSz9AdDTScBkxwtiBhcNMjkwMjExMjM1OTU5WjASIwDQYJKoZ
...
hZ7Q7drNJ3gjIVq4rXmsX
-----END CERTIFICATE-----</code><script>clientPackages.get("-bearcms-code-element-highlight-plaintext").then(function(o){var b=document.getElementById("bce2eff20290a6317fb4c2b4dcd656a6bcb");var r=o.library.highlight("plaintext",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>As you can see, we've concatenated the intermediate certificates provided by our Certificate Authority.</div><div><br></div><div>The next step is to edit the Apache configuration file (httpd.conf). Find or create &lt;VirtualHost&gt; that is configured to accept secure connections on port 443 and add the highlighted lines:</div></div></div><div><code class="bearcms-code-element" id="bceffa2a54b5be658af5d17ee90cc08f829">&lt;VirtualHost *:443&gt;
   ServerName example.com
   DocumentRoot &quot;/var/www&quot;
   SSLEngine on
   SSLCertificateFile /var/certificates/example.com.crt
   SSLCertificateKeyFile /var/certificates/example.com.key
   SSLCertificateChainFile /var/certificates/example.com.chain
   SSLProtocol All -SSLv2 -SSLv3
   SSLHonorCipherOrder on
   SSLCipherSuite &quot;EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS&quot;
&lt;/VirtualHost&gt;</code><script>clientPackages.get("-bearcms-code-element-highlight-apache").then(function(o){var b=document.getElementById("bceffa2a54b5be658af5d17ee90cc08f829");var r=o.library.highlight("apache",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>Then test the configuration file using the following command</div></div><div><code class="bearcms-code-element" id="bcebaa2b3be95e5a02be5bef0f3919508b1">apachectl configtest</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bcebaa2b3be95e5a02be5bef0f3919508b1");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>and if the file is valid run the following command to apply the new configuration</div></div><div><code class="bearcms-code-element" id="bcebc29e1529cbd331834d376eab23ebd35">apachectl graceful</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bcebc29e1529cbd331834d376eab23ebd35");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>That's it. Now visitors can access your website securely.</div></div>]]></description><pubDate>Thu, 30 Jul 2015 19:56:18 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/installing-ssl-certificate-on-apache/</guid></item><item><title>Getting SSL certificate</title><link>https://ivopetkov.com/b/getting-ssl-certificate/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/471468bd3f4e-c999999999/ervfbka0fuea416dzkikh.jpg" style="max-width:100%;"></div></div><div><div><div>In the previous post <a href="http://ivopetkov.com/b/how-ssl-works/" title="How SSL works">How SSL works</a>, we learned what the private key and the certificate are and how they can secure our website connection. Now it's time to get a pair. These are the steps needed:</div><div>1. Generate a private key</div><div>2. Generate a Certificate Signing Request (CSR)</div><div>3. Get a certificate</div><div><br></div><div>Currently, the easiest and most secure method to generate a private key and CSR is on a Linux machine. All you need is the openssl library. Run this command to ensure it's installed.</div></div></div><div><code class="bearcms-code-element" id="bce4e745a4c13ce95c7e360e71d95aac8bd">yum install openssl</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce4e745a4c13ce95c7e360e71d95aac8bd");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h3>Generate private key</h3></div><div><div>The following command will generate a 2048 bit long private key and write it to a file named example.com.key.</div></div><div><code class="bearcms-code-element" id="bce16149fa12f7ef1535c1eb343e88aab5d">openssl genrsa -out example.com.key 2048</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce16149fa12f7ef1535c1eb343e88aab5d");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><h3>Generate Certificate Signing Request (CSR)</h3></div><div><div>The following command will create a file named example.com.csr containing your Certificate Signing Request using the key created in the previous step.</div></div><div><code class="bearcms-code-element" id="bce4e3e10c1d65031f0f7a8b5368ea733cf">openssl req -new -sha256 -key example.com.key -out example.com.csr</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce4e3e10c1d65031f0f7a8b5368ea733cf");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div>During the file creation, you will be requested to enter some information. Here is an example:</div></div><div><code class="bearcms-code-element" id="bced84200d553465ce53fc44b67427f60b4">You are about to be asked to enter information that will be incorporated into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter &#039;.&#039;, the field will be left blank.
-----
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:New York
Locality Name (eg, city) [Default City]:New York City
Organization Name (eg, company) [Default Company Ltd]:My Company Ltd
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server&#039;s hostname) []:www.example.com
Email Address []:support@example.com

Please enter the following &#039;extra&#039; attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bced84200d553465ce53fc44b67427f60b4");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>The Common Name field is the place where you must enter the domain name you want to secure. The typical values are www.example.com and *.example.com.</div><div><br></div><div>When requested for a password just press enter to leave the field blank.</div><div><br></div><div>When created, this file will contain the public key and the information you've entered.</div></div></div><div><h3>Get the certificate</h3></div><div><div>If you plan to use the certificate only for developer purposes you can sign it yourself for free. This types of certificates trigger warnings in the browsers, so they are not a valid solution for public websites. The following command will generate a self-signed certificate&nbsp;in a file named example.com.crt&nbsp;that will be valid for 365 days.</div></div><div><code class="bearcms-code-element" id="bce5389973d28627584f10fc0897d51bdd6">openssl req -x509 -days 365 -in example.com.csr -key example.com.key -out example.com.crt</code><script>clientPackages.get("-bearcms-code-element-highlight-shell").then(function(o){var b=document.getElementById("bce5389973d28627584f10fc0897d51bdd6");var r=o.library.highlight("shell",b.innerText);try{b.innerHTML=o.update(r.value);}catch(e){};});</script></div><div><div><div>If you need to secure a public website, you must find a Certificate Authority to issue (sign with their private key) a certificate that can be validated by the browsers. This is a paid service, and here are some popular authorities: <a href="http://ivopetkov.com/visit/namecheap.com" title="NameCheap">NameCheap</a>, <a href="http://ivopetkov.com/visit/comodo.com" title="Comodo">Comodo</a>, <a href="http://ivopetkov.com/visit/digicert.com" title="DigiCert">DigiCert</a>. They all offer different validation options. The most common and cheap one is the domain validation option. With this type of validation, you usually get a certificate within minutes, and all you must do is verify domain ownership.</div><div><br></div><div>Usually, these are the steps for domain validation only certificates:</div><div>1. You choose the certificate type (single domain, unlimited subdomains, etc.) and make a payment.</div><div>2. You fill a small form with the domain name and the content of your CSR file.</div><div>3. They send you a confirmation email at one of the following email addresses: admin@example.com, webmaster@example.com or other listed in the domain's WHOIS data.</div><div>4. You open the email and click a confirmation link.</div><div>5. Soon you receive another email containing your certificate and some other CA certificates called intermediate.</div><div><br></div><div>In the next post, we'll learn how to use the private key, the certificate, and the intermediate certificates to secure a website running on Apache.</div></div></div>]]></description><pubDate>Sun, 26 Jul 2015 07:46:07 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/getting-ssl-certificate/</guid></item><item><title>How SSL works</title><link>https://ivopetkov.com/b/how-ssl-works/</link><description><![CDATA[<div><div><img src="https://ivopetkov.com/assets/0153c50e8256-c999999999/edo0jm726uea41r7mhvn0.jpg" style="max-width:100%;"></div></div><div><div>SSL stands for Secure Sockets Layer. It's also known as Transport Layer Security (TLS). This is the technology used to provide security on the web. Today we will learn how SSL makes the communication between a&nbsp;web browser&nbsp;and a&nbsp;website&nbsp;secure so no one can see our passwords or other personal data while they are&nbsp;traveling&nbsp;through the Internet.</div></div><div><h3>Terms we need to know before we start</h3></div><div><div><div><span style="font-weight:bold;">Key</span></div><div>A combination of characters (like a password) but preferably long in size, so it is very very hard to guess.</div><div><br></div><div><span style="font-weight:bold;">Encryption</span></div><div>The process of encoding information so that it can only be decoded by using a&nbsp;special&nbsp;key.</div><div><br></div><div><span style="font-weight:bold;">Decryption</span></div><div>The process of decoding information by using a special key.</div><div><br></div><div><span style="font-weight:bold;">Private key</span></div><div>A key that can encrypt or decrypt information. As the name implies, it must be kept in a secret place.</div><div><br></div><div><span style="font-weight:bold;">Public key</span></div><div>A key that can encrypt or decrypt information but can be shared.</div><div><br></div><div><div><span style="font-weight:bold;">Certificate</span></div><div>File that contains a public key and other information.</div></div><div><br></div><div><span style="font-weight:bold;">Certificate authority</span></div><div>Organization that issues certificates and can help your browser verify them.</div><div><br></div><div><span style="font-weight:bold;">Certificate authority root certificates</span></div><div>Certificates that are securely stored on your device and help you verify the identity of authority issued certificates. Your OS vendor takes care of updating them.</div></div></div><div><div>The secure connection requires two keys to be created, one public, and one private. Through the magic of mathematics, they can be used to encrypt or decrypt information. Everything encrypted with the private key can only be decrypted with its corresponding public key and vice versa.</div></div><div><h3>How secure communication is established</h3></div><div><div><div style="text-align:left;">These are our main players:</div><div style="text-align:center;"><div style="text-align:left;">- A browser that has access to the root certificates</div><div style="text-align:left;">- A web server that hosts the website. It stores a private key securely and has a valid certificate issued by a Certificate Authority.</div></div></div></div><div><div><div style="text-align:center;">The browser requests secure connection from the web server</div></div></div><div><div class="ivo-background"><div style="position:relative;width:100%;padding-top:22%;background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODAuODEyIiBoZWlnaHQ9IjE0OC41NyI+PGRlZnM+PG1hcmtlciBpZD0iYSIgb3JpZW50PSJhdXRvIiBvdmVyZmxvdz0idmlzaWJsZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iLjJwdCIgZD0iTS0xLjIgMGwtMSAxIDMuNS0xLTMuNS0xIDEgMXoiLz48L21hcmtlcj48bWFya2VyIG9yaWVudD0iYXV0byIgb3ZlcmZsb3c9InZpc2libGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9Ii44cHQiIGQ9Ik00LjYyIDBMLTIuMyA0di04bDYuOTIgNHoiLz48L21hcmtlcj48bWFya2VyIG9yaWVudD0iYXV0byIgb3ZlcmZsb3c9InZpc2libGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTS0xMC43LTQuNDRMMS4zNC0uMDItMTAuNyA0LjRjMS45My0yLjYgMS45Mi02LjE4IDAtOC44NHoiLz48L21hcmtlcj48bWFya2VyIG9yaWVudD0iYXV0byIgb3ZlcmZsb3c9InZpc2libGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9Ii44cHQiIGQ9Ik0tMTAgMGwtNCA0TDAgMGwtMTQtNCA0IDR6Ii8+PC9tYXJrZXI+PC9kZWZzPjxnPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zMS40MjkgLTM1MC4yMDkpIHRyYW5zbGF0ZSgtNjI4LjU3MSAtMzU2LjQ0KSI+PHJlY3Qgd2lkdGg9IjIxNy4xNDMiIGhlaWdodD0iMTQ4LjU3MSIgeD0iNjYwIiB5PSI3MDYuNjQ4IiByeT0iMi41ODYiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNjYyLjM1IDcyNC41SDg3NC44djEyOC40N0g2NjIuMzR6Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTY3MS4yIDcxNS41NWMwIDEuNzMtMS40IDMuMTMtMy4xMiAzLjEzLTEuNzMgMC0zLjEzLTEuNC0zLjEzLTMuMTMgMC0xLjcyIDEuNC0zLjEyIDMuMTMtMy4xMiAxLjcyIDAgMy4xMiAxLjQgMy4xMiAzLjEyIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTY4MC4xOCA3MTUuNTVjMCAxLjczLTEuNCAzLjEzLTMuMTMgMy4xMy0xLjcyIDAtMy4xMi0xLjQtMy4xMi0zLjEzIDAtMS43MiAxLjQtMy4xMiAzLjEyLTMuMTIgMS43MyAwIDMuMTMgMS40IDMuMTMgMy4xMiIvPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik02ODkuMTUgNzE1LjU1YzAgMS43My0xLjQgMy4xMy0zLjEzIDMuMTMtMS43MiAwLTMuMTItMS40LTMuMTItMy4xMyAwLTEuNzIgMS40LTMuMTIgMy4xMi0zLjEyIDEuNzMgMCAzLjEzIDEuNCAzLjEzIDMuMTIiLz48L2c+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMxLjQyOSAtMzUwLjIwOSkgdHJhbnNsYXRlKDIyOC45MDMgLTIyNi44MzgpIj48cmVjdCB3aWR0aD0iMjE3LjE0MyIgaGVpZ2h0PSIxNDguNTcxIiB4PSIyNjYuMTk1IiB5PSI1NzcuMDQ2IiByeT0iMi41ODYiLz48cmVjdCB3aWR0aD0iMzAuODkzIiBoZWlnaHQ9IjYuMjUiIHg9IjQ0NS4xODMiIHk9IjcxMS4yMDMiIGZpbGw9IiNmZmYiIHJ5PSIzLjEyNSIvPjxyZWN0IHdpZHRoPSIzMC44OTMiIGhlaWdodD0iNi4yNSIgeD0iNDQ1LjE4MyIgeT0iNzAwLjcwMSIgZmlsbD0iI2ZmZiIgcnk9IjMuMTI1Ii8+PHJlY3Qgd2lkdGg9IjMwLjg5MyIgaGVpZ2h0PSI2LjI1IiB4PSI0NDUuMTgzIiB5PSI2OTAuMiIgZmlsbD0iI2ZmZiIgcnk9IjMuMTI1Ii8+PC9nPjxnPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSI0LjgiIGQ9Ik0yODEuODUgMzk2LjEzSDQ0My4xIiBtYXJrZXItZW5kPSJ1cmwoI2EpIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzEuNDI5IC0zNTAuMjA5KSB0cmFuc2xhdGUoNy4xNDMpIi8+PHRleHQgeD0iMzY0LjM3OSIgeT0iNDM3LjA1IiBzdHlsZT0ibGluZS1oZWlnaHQ6MTI1JSIgZm9udC1zaXplPSIyNi4xIiBsZXR0ZXItc3BhY2luZz0iMCIgd29yZC1zcGFjaW5nPSIwIiBmb250LWZhbWlseT0iU2FucyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMxLjQyOSAtMzUwLjIwOSkgdHJhbnNsYXRlKDcuMTQzKSI+PHRzcGFuIHg9IjM2NC4zNzkiIHk9IjQzNy4wNSIgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246QXJpYWwiIGZvbnQtc2l6ZT0iMjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJBcmlhbCI+TGV0J3MgY29tbXVuaWNhdGU8L3RzcGFuPjx0c3BhbiB4PSIzNjQuMzc5IiB5PSI0NjIuMDUiIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkFyaWFsIiBmb250LXNpemU9IjIwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iQXJpYWwiPnNlY3VyZWx5PC90c3Bhbj48L3RleHQ+PC9nPjwvZz48L3N2Zz4=');background-repeat:no-repeat;background-size:contain;background-position:center center;"></div></div></div><div><div><div style="text-align:center;">The web server responds with its certificate</div></div></div><div><div class="ivo-background"><div style="position:relative;width:100%;padding-top:22%;background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODAuODEyIiBoZWlnaHQ9IjE0OC41NyI+PGRlZnM+PG1hcmtlciBpZD0iYSIgb3JpZW50PSJhdXRvIiBvdmVyZmxvdz0idmlzaWJsZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iLjJwdCIgZD0iTTEuMiAwbDEtMS0zLjUgMSAzLjUgMS0xLTF6Ii8+PC9tYXJrZXI+PG1hcmtlciBvcmllbnQ9ImF1dG8iIG92ZXJmbG93PSJ2aXNpYmxlIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIuMnB0IiBkPSJNLTEuMiAwbC0xIDEgMy41LTEtMy41LTEgMSAxeiIvPjwvbWFya2VyPjxtYXJrZXIgb3JpZW50PSJhdXRvIiBvdmVyZmxvdz0idmlzaWJsZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iLjhwdCIgZD0iTTQuNjIgMEwtMi4zIDR2LThsNi45MiA0eiIvPjwvbWFya2VyPjxtYXJrZXIgb3JpZW50PSJhdXRvIiBvdmVyZmxvdz0idmlzaWJsZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNLTEwLjctNC40NEwxLjM0LS4wMi0xMC43IDQuNGMxLjkzLTIuNiAxLjkyLTYuMTggMC04Ljg0eiIvPjwvbWFya2VyPjxtYXJrZXIgb3JpZW50PSJhdXRvIiBvdmVyZmxvdz0idmlzaWJsZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iLjhwdCIgZD0iTS0xMCAwbC00IDRMMCAwbC0xNC00IDQgNHoiLz48L21hcmtlcj48L2RlZnM+PGc+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMxLjQyOSAtMzUwLjIwOSkgdHJhbnNsYXRlKC02MjguNTcxIC0zNTYuNDQpIj48cmVjdCB3aWR0aD0iMjE3LjE0MyIgaGVpZ2h0PSIxNDguNTcxIiB4PSI2NjAiIHk9IjcwNi42NDgiIHJ5PSIyLjU4NiIvPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik02NjIuMzUgNzI0LjVIODc0Ljh2MTI4LjQ3SDY2Mi4zNHoiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNjcxLjIgNzE1LjU1YzAgMS43My0xLjQgMy4xMy0zLjEyIDMuMTMtMS43MyAwLTMuMTMtMS40LTMuMTMtMy4xMyAwLTEuNzIgMS40LTMuMTIgMy4xMy0zLjEyIDEuNzIgMCAzLjEyIDEuNCAzLjEyIDMuMTIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNjgwLjE4IDcxNS41NWMwIDEuNzMtMS40IDMuMTMtMy4xMyAzLjEzLTEuNzIgMC0zLjEyLTEuNC0zLjEyLTMuMTMgMC0xLjcyIDEuNC0zLjEyIDMuMTItMy4xMiAxLjczIDAgMy4xMyAxLjQgMy4xMyAzLjEyIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTY4OS4xNSA3MTUuNTVjMCAxLjczLTEuNCAzLjEzLTMuMTMgMy4xMy0xLjcyIDAtMy4xMi0xLjQtMy4xMi0zLjEzIDAtMS43MiAxLjQtMy4xMiAzLjEyLTMuMTIgMS43MyAwIDMuMTMgMS40IDMuMTMgMy4xMiIvPjwvZz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzEuNDI5IC0zNTAuMjA5KSB0cmFuc2xhdGUoMjI4LjkwMyAtMjI2LjgzOCkiPjxyZWN0IHdpZHRoPSIyMTcuMTQzIiBoZWlnaHQ9IjE0OC41NzEiIHg9IjI2Ni4xOTUiIHk9IjU3Ny4wNDYiIHJ5PSIyLjU4NiIvPjxyZWN0IHdpZHRoPSIzMC44OTMiIGhlaWdodD0iNi4yNSIgeD0iNDQ1LjE4MyIgeT0iNzExLjIwMyIgZmlsbD0iI2ZmZiIgcnk9IjMuMTI1Ii8+PHJlY3Qgd2lkdGg9IjMwLjg5MyIgaGVpZ2h0PSI2LjI1IiB4PSI0NDUuMTgzIiB5PSI3MDAuNzAxIiBmaWxsPSIjZmZmIiByeT0iMy4xMjUiLz48cmVjdCB3aWR0aD0iMzAuODkzIiBoZWlnaHQ9IjYuMjUiIHg9IjQ0NS4xODMiIHk9IjY5MC4yIiBmaWxsPSIjZmZmIiByeT0iMy4xMjUiLz48L2c+PGc+PGc+PHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjQuOCIgZD0iTTI4MS44NSAzOTYuMTNINDQzLjEiIG1hcmtlci1zdGFydD0idXJsKCNhKSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIxLjEzNCAtMzUwLjIwOSkiLz48dGV4dCB4PSIzNTkuOTM5IiB5PSI0MzcuMDUiIHN0eWxlPSJsaW5lLWhlaWdodDoxMjUlIiBmb250LXNpemU9IjI2LjEiIGxldHRlci1zcGFjaW5nPSIwIiB3b3JkLXNwYWNpbmc9IjAiIGZvbnQtZmFtaWx5PSJTYW5zIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjEuMTM0IC0zNTAuMjA5KSI+PHRzcGFuIHg9IjM1OS45MzkiIHk9IjQzNy4wNSIgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246QXJpYWwiIGZvbnQtc2l6ZT0iMjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJBcmlhbCI+T2ssIHRoaXMgaXMgbXk8L3RzcGFuPjx0c3BhbiB4PSIzNTkuOTM5IiB5PSI0NjIuMDUiIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkFyaWFsIiBmb250LXNpemU9IjIwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iQXJpYWwiPmNlcnRpZmljYXRlPC90c3Bhbj48L3RleHQ+PC9nPjwvZz48L2c+PC9zdmc+');background-repeat:no-repeat;background-size:contain;background-position:center center;"></div></div></div><div><div><div style="text-align:center;">The browser verifies the certificate using the root certificates, so it is sure it's communicating with the right web server</div></div></div><div><div><div style="text-align:center;">Now both parties have valid keys they can use for secure communication. The browser is using the public key for decrypting server responses and sending encrypted requests. The server is using its private key for decrypting requests and encrypting responses.</div></div></div><div><div class="ivo-background"><div style="position:relative;width:100%;padding-top:22%;background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODAuODEyIiBoZWlnaHQ9IjE0OC41NyI+PGRlZnM+PG1hcmtlciBpZD0iYSIgb3JpZW50PSJhdXRvIiBvdmVyZmxvdz0idmlzaWJsZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iLjJwdCIgZD0iTTEuMiAwbDEtMS0zLjUgMSAzLjUgMS0xLTF6Ii8+PC9tYXJrZXI+PG1hcmtlciBpZD0iYiIgb3JpZW50PSJhdXRvIiBvdmVyZmxvdz0idmlzaWJsZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iLjJwdCIgZD0iTS0xLjIgMGwtMSAxIDMuNS0xLTMuNS0xIDEgMXoiLz48L21hcmtlcj48bWFya2VyIG9yaWVudD0iYXV0byIgb3ZlcmZsb3c9InZpc2libGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9Ii44cHQiIGQ9Ik00LjYyIDBMLTIuMyA0di04bDYuOTIgNHoiLz48L21hcmtlcj48bWFya2VyIG9yaWVudD0iYXV0byIgb3ZlcmZsb3c9InZpc2libGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTS0xMC43LTQuNDRMMS4zNC0uMDItMTAuNyA0LjRjMS45My0yLjYgMS45Mi02LjE4IDAtOC44NHoiLz48L21hcmtlcj48bWFya2VyIG9yaWVudD0iYXV0byIgb3ZlcmZsb3c9InZpc2libGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9Ii44cHQiIGQ9Ik0tMTAgMGwtNCA0TDAgMGwtMTQtNCA0IDR6Ii8+PC9tYXJrZXI+PC9kZWZzPjxnPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zMS40MjkgLTM1MC4yMDkpIHRyYW5zbGF0ZSgtNjI4LjU3MSAtMzU2LjQ0KSI+PHJlY3Qgd2lkdGg9IjIxNy4xNDMiIGhlaWdodD0iMTQ4LjU3MSIgeD0iNjYwIiB5PSI3MDYuNjQ4IiByeT0iMi41ODYiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNjYyLjM1IDcyNC41SDg3NC44djEyOC40N0g2NjIuMzR6Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTY3MS4yIDcxNS41NWMwIDEuNzMtMS40IDMuMTMtMy4xMiAzLjEzLTEuNzMgMC0zLjEzLTEuNC0zLjEzLTMuMTMgMC0xLjcyIDEuNC0zLjEyIDMuMTMtMy4xMiAxLjcyIDAgMy4xMiAxLjQgMy4xMiAzLjEyIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTY4MC4xOCA3MTUuNTVjMCAxLjczLTEuNCAzLjEzLTMuMTMgMy4xMy0xLjcyIDAtMy4xMi0xLjQtMy4xMi0zLjEzIDAtMS43MiAxLjQtMy4xMiAzLjEyLTMuMTIgMS43MyAwIDMuMTMgMS40IDMuMTMgMy4xMiIvPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik02ODkuMTUgNzE1LjU1YzAgMS43My0xLjQgMy4xMy0zLjEzIDMuMTMtMS43MiAwLTMuMTItMS40LTMuMTItMy4xMyAwLTEuNzIgMS40LTMuMTIgMy4xMi0zLjEyIDEuNzMgMCAzLjEzIDEuNCAzLjEzIDMuMTIiLz48L2c+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMxLjQyOSAtMzUwLjIwOSkgdHJhbnNsYXRlKDIyOC45MDMgLTIyNi44MzgpIj48cmVjdCB3aWR0aD0iMjE3LjE0MyIgaGVpZ2h0PSIxNDguNTcxIiB4PSIyNjYuMTk1IiB5PSI1NzcuMDQ2IiByeT0iMi41ODYiLz48cmVjdCB3aWR0aD0iMzAuODkzIiBoZWlnaHQ9IjYuMjUiIHg9IjQ0NS4xODMiIHk9IjcxMS4yMDMiIGZpbGw9IiNmZmYiIHJ5PSIzLjEyNSIvPjxyZWN0IHdpZHRoPSIzMC44OTMiIGhlaWdodD0iNi4yNSIgeD0iNDQ1LjE4MyIgeT0iNzAwLjcwMSIgZmlsbD0iI2ZmZiIgcnk9IjMuMTI1Ii8+PHJlY3Qgd2lkdGg9IjMwLjg5MyIgaGVpZ2h0PSI2LjI1IiB4PSI0NDUuMTgzIiB5PSI2OTAuMiIgZmlsbD0iI2ZmZiIgcnk9IjMuMTI1Ii8+PC9nPjxnPjxnPjxnPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSI0LjgiIGQ9Ik0yODEuODUgMzk2LjEzSDQ0My4xIiBtYXJrZXItc3RhcnQ9InVybCgjYSkiIG1hcmtlci1lbmQ9InVybCgjYikiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMS4xMzQgLTM1MC4yMDkpIi8+PHRleHQgeD0iMzYyLjA3NiIgeT0iNDM3LjA1IiBzdHlsZT0ibGluZS1oZWlnaHQ6MTI1JSIgZm9udC1zaXplPSIyNi4xIiBsZXR0ZXItc3BhY2luZz0iMCIgd29yZC1zcGFjaW5nPSIwIiBmb250LWZhbWlseT0iU2FucyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIxLjEzNCAtMzUwLjIwOSkiPjx0c3BhbiB4PSIzNjIuMDc2IiB5PSI0MzcuMDUiIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkFyaWFsIiBmb250LXNpemU9IjIwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iQXJpYWwiPlllYWghIE5vdyB3ZTwvdHNwYW4+PHRzcGFuIHg9IjM2Mi4wNzYiIHk9IjQ2Mi4wNSIgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246QXJpYWwiIGZvbnQtc2l6ZT0iMjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJBcmlhbCI+Y2FuIHRhbGsgaW4gcHJpdmF0ZTwvdHNwYW4+PC90ZXh0PjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=');background-repeat:no-repeat;background-size:contain;background-position:center center;"></div></div></div><div><div><div>That's it.</div><div><br></div><div>Please use the comment box bellow if you like this post or need more information.</div></div></div>]]></description><pubDate>Tue, 14 Jul 2015 09:05:29 +0000</pubDate><guid isPermaLink="false">https://ivopetkov.com/b/how-ssl-works/</guid></item></channel></rss>