<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Geeky Nuggets</title>
    <description>Jonathan Foucher&apos;s blog &amp; web development notebook</description>
    <link>https://jfoucher.com/</link>
    <atom:link href="https://jfoucher.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 19 Aug 2022 23:42:56 +0200</pubDate>
    <lastBuildDate>Fri, 19 Aug 2022 23:42:56 +0200</lastBuildDate>
    <generator>Stati (https://stati.jfoucher.com)</generator>
    
      <item>
        <title>Planck 6502, an open hardware extensible retro computer</title>
        <description>&lt;h3 id=&quot;retro-computing&quot;&gt;Retro computing&lt;/h3&gt;

&lt;p&gt;Going back to the roots of your field can provide new insights into your day to day experience. For me, the roots are located in the 70s / 80s a time when personal computers were much simpler and could be fully understood by a regular person. This is a time when the most popular computers were the Vic20, Apple II and later Commodore 64.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2021/02/VIC-20.jpg&quot; alt=&quot;Vic-20&quot; class=&quot;left-image&quot; /&gt;
&lt;img src=&quot;/uploads/2021/02/Apple_II.jpg&quot; alt=&quot;Apple II&quot; class=&quot;right-image&quot; /&gt;&lt;/p&gt;

&lt;div style=&quot;clear:both&quot;&gt;&lt;/div&gt;

&lt;p&gt;These computers booted in less than a second to a raw basic prompt. To be clear: you turn on your computer, and less than one second later you start programming. How wonderful would that be, in our land of a thousand node dependencies, failing composer upgrades and wrong vagrant configurations?&lt;/p&gt;

&lt;h3 id=&quot;what-to-build&quot;&gt;What to build?&lt;/h3&gt;

&lt;p&gt;I was increasingly interested in electronics since doing a project for a friend, so I thought to myself “How hard can it be?” Well, as it turns out, quite, but that’s hardly the point right now. The point is: what should I build? I started by building a simple 6502 based computer on a breadboard. I soon realised that if I was to have anything half useful I was going to need the entire breadboard production of China to be delivered to my house. Also breadboard computers are buggy and you can’t move them around because then a cable will come loose and you will spend five hours troubleshooting your code instead.&lt;/p&gt;

&lt;p&gt;So I decided I would convert it to PCBs. They are pretty cheap to get delivered from China, but since they take about a month to arrive the rate of iteration is not so great. Better to get it right the first time obviously.&lt;/p&gt;

&lt;p&gt;Well I almost did. The first one was very simple, only having a 6502 processor and an 6522 parallel port interface. I interfaced with it from my computer via SPI through an arduino: the 6522 Versatile Interface Adapter can bit bang SPI easily enough. The Arduino Uno/Nano can be an SPI slave easily enough and receive that data. It can then transmit that data to my computer using serial over USB.&lt;/p&gt;

&lt;p&gt;That worked fine (after a couple of bodge wires) but was a bit kludgy, having the Arduino sort of hanging there by a few cables.&lt;/p&gt;

&lt;p&gt;I then tried to design a single board computer with everything I would ever want on it: serial communication, parrallel port, SPI port(s), i2c port(s), PS/2 port for a keyboard, sound chip, VGA output, etc… The board to house all of this was very big and thus very expensive. Combining this with the previous point regarding getting it right the first time, a single board computer suddenly seemed like not that great of an idea.&lt;/p&gt;

&lt;h3 id=&quot;the-plan&quot;&gt;The plan&lt;/h3&gt;

&lt;p&gt;I then decided to build a board with basic circuitry into which other boards that provided actual functionality will be able to plug in. Turns out that already exists and is called a &lt;a href=&quot;https://en.wikipedia.org/wiki/Backplane&quot;&gt;backplane&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So backplane it is then. The size limit for this board and all others will be 10cm x 10cm as that it the cheap price limit for most cheap board houses (yes I’m cheap and I like cheap, preferably cheap that works).&lt;/p&gt;

&lt;p&gt;Here is the backplane design:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2021/02/backplane.png&quot; alt=&quot;backplane 3D view&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it is mostly just slots to plug in the extension boards. The only active components are the clock generation circuit, and some decoding to activate the expansion slots when they are needed.&lt;/p&gt;

&lt;p&gt;The rightmost slot is reserved for the CPU card. The CPU card includes some RAM and ROM, which means the computer is already functional with just this single board plugged in, although admittedly it won’t &lt;em&gt;do&lt;/em&gt; much in that configuration.&lt;/p&gt;

&lt;p&gt;To get the computer to do stuff it’s best to plug in some additional boards.&lt;/p&gt;

&lt;p&gt;The initial design includes an IO board with PS/2 port, SPI ports and a parallel port (and LEDs, of course it has LEDs!) as well as a serial board to allow the user to communicate with the computer.&lt;/p&gt;

&lt;p&gt;After waiting patiently forever for the boards to arrive, I put it all together and gave it a try. It did not work at all. Of course there were mistakes on the boards, which confirmed the soudness of my choice of not doing one huge board at once.
However not all the boards were defective, and the bad ones were quickly fixed with a few wires here and there.&lt;/p&gt;

&lt;p&gt;After which, plugging a usb to serial adapter into the serial board, and into a usb port on my computer, I could see the Planck computer doing something!&lt;/p&gt;

&lt;p&gt;Here it is running &lt;a href=&quot;https://en.wikipedia.org/wiki/Forth_(programming_language)&quot;&gt;Forth&lt;/a&gt; over serial&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2021/02/running-forth.jpg&quot; alt=&quot;Forth over serial on the Planck computer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I am currently working on more expansion boards, such as an LCD board, a sound card, and a VGA card. &lt;a href=&quot;/feed.xml&quot;&gt;Stay tuned&lt;/a&gt; for more.&lt;/p&gt;

&lt;p&gt;For more details about this project, please use the links below:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://planck6502.jfoucher.com&quot;&gt;Project website&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gitlab.com/planck-6502/planck-6502&quot;&gt;Hardware and source code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        
          <description>I have designed a 6502 based computer that can be easily extended</description>
        
        <pubDate>Sun, 28 Feb 2021 11:32:07 +0100</pubDate>
        <link>https://jfoucher.com/2021/02/planck-6502-open-hardware-computer.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2021/02/planck-6502-open-hardware-computer.html</guid>
        
        <category>retro</category>
        
        <category>computer</category>
        
        <category>6502</category>
        
        
      </item>
    
      <item>
        <title>Mailocal, a simple SMTP server and web interface to debug your emails</title>
        <description>&lt;h3 id=&quot;testing-emails&quot;&gt;Testing emails&lt;/h3&gt;

&lt;p&gt;The way I used to do it is to have all emails sent to me via a configuration setting in either Symfony or Laravel. This works fine but there can be a significant delay between the moment when the email is sent and the moment it is received, which is annoying. I have heard about mailtrap, which is a hosted service that does more or less what &lt;a href=&quot;https://mailocal.jfoucher.com&quot;&gt;Mailocal&lt;/a&gt; does. But I’m not a fan of sending my customers’ info to a third party.&lt;/p&gt;

&lt;h3 id=&quot;mailocal&quot;&gt;Mailocal&lt;/h3&gt;

&lt;p&gt;So over a couple of days I developed &lt;a href=&quot;https://mailocal.jfoucher.com&quot;&gt;Mailocal&lt;/a&gt; which is a smtp server you run locally, with a web interface to view the emails is has received.&lt;/p&gt;

&lt;p&gt;It is built almost fully with PHP (Symfony) with only a light smattering of javascript for the front-end.
It works pretty good for the time I spent on it, although I can’t be sure it will parse all emails equally well.&lt;/p&gt;

&lt;p&gt;Obviously this is something that should not be run on a production server because I haven’t looked at the security aspects much, but that’s not the use case anyway.&lt;/p&gt;

&lt;p&gt;So I hope you enjoy it and &lt;a href=&quot;mailto:jfoucher@gmail.com&quot;&gt;let me know&lt;/a&gt; if you find any issues.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mailocal.jfoucher.com&quot;&gt;Mailocal documentation&lt;/a&gt;
&lt;a href=&quot;https://github.com/jfoucher/mailocal&quot;&gt;Source code&lt;/a&gt;&lt;/p&gt;
</description>
        
          <description>Do you know Mailtrap.io? Well this is the same, but without sending (possibly) confidential data to a third party</description>
        
        <pubDate>Thu, 28 Feb 2019 11:32:07 +0100</pubDate>
        <link>https://jfoucher.com/2019/02/mailocal-a-local-smtp-server-to-debug-your-emails.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2019/02/mailocal-a-local-smtp-server-to-debug-your-emails.html</guid>
        
        <category>symfony</category>
        
        <category>php</category>
        
        <category>email</category>
        
        <category>smtp</category>
        
        
      </item>
    
      <item>
        <title>Stati, a PHP static site generator that works on any existing Jekyll site</title>
        <description>&lt;h3 id=&quot;success-&quot;&gt;Success !&lt;/h3&gt;

&lt;p&gt;Well it’s been quite a week. Having had an unexpected lull in my contract work, I decided to work on something fun. I use jekyll for most of my static sites, and I quite like it. It’s still a mistery to me why I decided to build a clone of it. But anyway, one week later, here we are. &lt;a href=&quot;https://github.com/jfoucher/stati&quot;&gt;Stati&lt;/a&gt; can now build all my ex-Jekyll sites properly. Actually this blog is now generated with Stati!&lt;/p&gt;

&lt;h3 id=&quot;architecture&quot;&gt;Architecture&lt;/h3&gt;

&lt;p&gt;I read this &lt;a href=&quot;https://www.bytesandwich.com/jekyll/software/blogging/2016/09/14/how-does-jekyll-work.html&quot;&gt;How does Jekyll work&lt;/a&gt; blog post that gives a nice high level overview of the jekyll architecture, and I decided to mostly use the same kind of process.&lt;/p&gt;

&lt;p&gt;A main Site class holds most of the state while the generation is in progress, with &lt;a href=&quot;http://symfony.com/doc/current/components/event_dispatcher.html&quot;&gt;events&lt;/a&gt; being dispatched at several steps along the process so that plugins can hook into them and modify the input, the output, or simply monitor the progress of the generation process.&lt;/p&gt;

&lt;p&gt;First all files are read, in different manner depending on their type (static files, posts or pages), then they are rendered (again differently depending on their type) and finally they are written to disk at the correct path.&lt;/p&gt;

&lt;h3 id=&quot;plugins&quot;&gt;Plugins&lt;/h3&gt;

&lt;p&gt;At each point in this process, a plugin can come into play to add pages, have them written in different locations, etc…&lt;/p&gt;

&lt;p&gt;The first plugin, and the one that is used by most Jekyll sites I know of, is the paginator. In the case of Stati, it basically takes a list of all posts and generates a new html page for each page of posts. The core Writer then takes care of putting it in it’s proper path.&lt;/p&gt;

&lt;p&gt;The second plugin is a related posts plugin. For each post it calculates the levenshtein distance of it’s title to all the other posts titles and gets the 10 best matches. Lots of room for improvement here.&lt;/p&gt;

&lt;h3 id=&quot;try-it-now&quot;&gt;Try it now!&lt;/h3&gt;

&lt;p&gt;So basically that’s it, a fun project that I will probably continue to use and improve. If you’d like to give it a try, you can &lt;a href=&quot;https://github.com/jfoucher/stati/releases/latest&quot;&gt;get the latest release from github&lt;/a&gt; and, most importantly, &lt;a href=&quot;https://github.com/jfoucher/stati/issues/new&quot;&gt;open an issue&lt;/a&gt; if you run into a snag!&lt;/p&gt;

&lt;p&gt;PS. I just remembered I have a &lt;a href=&quot;/2005/03/changes-coming-soon.html&quot;&gt;history&lt;/a&gt; of &lt;a href=&quot;/2005/03/new-blog.html&quot;&gt;doing this kind of thing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;UPDATE : Just got some &lt;a href=&quot;https://stati.jfoucher.com&quot;&gt;documentation&lt;/a&gt; online&lt;/p&gt;
</description>
        
          <description>Why and how I built a Jekyll clone in PHP</description>
        
        <pubDate>Thu, 28 Sep 2017 00:00:00 +0200</pubDate>
        <link>https://jfoucher.com/2017/09/stati-static-site-generator.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2017/09/stati-static-site-generator.html</guid>
        
        <category>symfony</category>
        
        <category>php</category>
        
        <category>static</category>
        
        <category>jekyll</category>
        
        
      </item>
    
      <item>
        <title>Processing custom events in kernel.terminate</title>
        <description>&lt;h3 id=&quot;what-is-the-kernelterminate-event&quot;&gt;What is the kernel.terminate event?&lt;/h3&gt;

&lt;p&gt;It is the last event that is triggered in Symfony, just after the response has been sent to the client (only on php-fpm). This means that we can do some heavy lifting here because our response time is not affected. The client gets the response quickly and our processing will occur afterwards.&lt;/p&gt;

&lt;p&gt;The Symfony SwiftMailer bundle does this by default, so we should be ok with emails, but my latest project needs to connect to various APIs on some events, and this takes quite some time.&lt;/p&gt;

&lt;p&gt;In this post I will explain the method I use to make them run in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kernel.terminate&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;For a primer about Symfony events, I recommend the &lt;a href=&quot;https://symfony.com/doc/current/reference/events.html&quot;&gt;official Symfony documentation&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;custom-events&quot;&gt;Custom events&lt;/h3&gt;

&lt;p&gt;Custom events are trigerred from our own code, generally in the controller, and it goes something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cd&quot;&gt;/** 
* @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface 
*/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Get the event dispatcher from the container
&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$dispatcher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;event_dispatcher&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Create our custom event
&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NewUserEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Dispatch the event
&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$dispatcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AppEvents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NEW_USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;the-event-subscriber&quot;&gt;The event subscriber&lt;/h3&gt;

&lt;p&gt;Then we have an EventSubscriber that will listen to our custom events, in my case it’s called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApiSubscriber&lt;/code&gt; and is located in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/EventSubscriber&lt;/code&gt;. In this case it listens to two different events: 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppEvents::NEW_USER&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppEvents::DELETE_USER&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiSubscriber&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EventSubscriberInterface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$container&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Symfony\Component\DependencyInjection\ContainerInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$container&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;container&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$container&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getSubscribedEvents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;AppEvents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NEW_USER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;newUser&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;AppEvents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DELETE_USER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;deleteUser&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//...
&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;configure-the-service&quot;&gt;Configure the service&lt;/h3&gt;

&lt;p&gt;You will notice that I inject the container as it makes it easy to get everything we need, such as parameters or other services.&lt;/p&gt;

&lt;p&gt;Don’t forget to configure the event subscriber in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/config/services.yml&lt;/code&gt; like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;api.subscriber&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AppBundle\EventSubscriber\ApiSubscriber&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@service_container&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;kernel.event_subscriber&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now our event subscriber is ready for action whenever we trigger the event from our controllers.&lt;/p&gt;

&lt;h3 id=&quot;handle-your-custom-event&quot;&gt;Handle your custom event&lt;/h3&gt;

&lt;p&gt;The event handling function is where the magic happens. Say when a new user is created, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppEvents::NEW_USER&lt;/code&gt; is triggered and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApiSubscriber::newUser&lt;/code&gt; function is called.&lt;/p&gt;

&lt;p&gt;Here it is, I’ll explain later&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;NewUserEvent&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$newUserEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Get the request from our custom event
&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$newUserEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Add custom attributes to the request, so we can retrieve them later in the kernel.terminate event
&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&apos;user&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$newUserEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&apos;thing_api_key&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;thing_api_key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&apos;thing_api_user&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;thing_api_user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Add our custom listener to the kernel.terminate event
&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We will see later what the createThingOnApi static function looks like
&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;event_dispatcher&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;kernel.terminate&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;createThingOnApi&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// This would also work but is not as clean in my opinion
&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// $this-&amp;gt;container-&amp;gt;get(&apos;event_dispatcher&apos;)-&amp;gt;addListener(&apos;kernel.terminate&apos;, function(PostResponseEvent $event) {
&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Do things on the API
&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// });
&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So, from this code it is clear what happens. We first get the request from our custom event, then we set custom attributes on the request (simply because we need to retrieve them later from our static function) and finally we add a new listener (our static function, could also be an anonymous function) to the kernel.terminate event.&lt;/p&gt;

&lt;h3 id=&quot;do-the-job&quot;&gt;Do the job&lt;/h3&gt;

&lt;p&gt;The final piece of the puzzle is our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createThingOnApi&lt;/code&gt; static function that will do the real work.&lt;/p&gt;

&lt;p&gt;Here it is:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createThingOnApi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;PostResponseEvent&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$attrs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$attrs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;newUser&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//...
&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$ch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;curl_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://api.thing.com/users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;curl_setopt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CURLOPT_CUSTOMREQUEST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;curl_setopt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CURLOPT_POSTFIELDS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;curl_setopt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;curl_setopt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CURLOPT_HTTPHEADER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&apos;Content-Type: application/json&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&apos;Content-Length: &apos;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&apos;X-Auth-Key: &apos;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$attrs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;thing_api_key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&apos;X-Auth-User: &apos;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$attrs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;thing_api_user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;curl_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This basically retrives our data from the request attributes, and then make some sort of curl request to an external API.&lt;/p&gt;

&lt;h3 id=&quot;all-done-&quot;&gt;All done !&lt;/h3&gt;

&lt;p&gt;Do this and watch your response times go way down (&lt;a href=&quot;https://symfony.com/doc/current/components/http_kernel.html#component-http-kernel-kernel-terminate&quot;&gt;only on php-pfm&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I’m not saying the is the best or even the only method to achieve this, just the one I personally use. the alternative would be some sort of mesasge queue, but it still seems like premature optimisation for an app of this size. If you have other options to achieve this, let me know in the comments on HN.&lt;/p&gt;

&lt;h3 id=&quot;profiler-results&quot;&gt;Profiler results&lt;/h3&gt;

&lt;p class=&quot;image&quot;&gt;&lt;img src=&quot;/uploads/2017/08/symfony-kernel.terminate.png&quot; alt=&quot;Profiler results&quot; /&gt;&lt;/p&gt;
</description>
        
          <description>When you have some long running processes to trigger on request, best practice with Symfony is to have them run in the kernel.terminate event.</description>
        
        <pubDate>Fri, 25 Aug 2017 00:00:00 +0200</pubDate>
        <link>https://jfoucher.com/2017/08/symfony-process-custom-events-in-kernel-terminate.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2017/08/symfony-process-custom-events-in-kernel-terminate.html</guid>
        
        <category>symfony</category>
        
        <category>php</category>
        
        <category>events</category>
        
        
      </item>
    
      <item>
        <title>Timeseries charts with Chart.js</title>
        <description>&lt;p&gt;Somehow I always end up having to do timeseries charts. Chart.js is my tool of choice for that task, but I never seem to remember what options to pass and what format the data should have. Yesterday I must have spent 2 hours looking at the docs and trying various things, mostly because it didn’t occur to me to check the last project where I used them, &lt;a href=&quot;https://budgt.eu&quot;&gt;Budgt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So anyway, note to self : this is how you do timeseries graphs in chart.js&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;chartCanvas&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2d&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Labels should be Date objects&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2017&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;08&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2017&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;08&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2017&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;08&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;datasets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Page Views&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;280&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;340&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;borderColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#fe8b36&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;backgroundColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#fe8b36&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;lineTension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;responsive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;scales&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;xAxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;scaleLabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;labelString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;yAxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;ticks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;beginAtZero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;scaleLabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;labelString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Page Views&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Chart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s what it looks like:&lt;/p&gt;

&lt;canvas id=&quot;chartCanvas&quot; width=&quot;400&quot; height=&quot;200&quot;&gt;&lt;/canvas&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;
const ctx = document.getElementById(&apos;chartCanvas&apos;).getContext(&apos;2d&apos;);
const data = {
            labels: [new Date(2017, 08, 16), new Date(2017, 08, 17), new Date(2017, 08, 18)],
            datasets: [{
                fill: false,
                label: &apos;Page Views&apos;,
                data: [280, 250, 340],
                borderColor: &apos;#fe8b36&apos;,
                backgroundColor: &apos;#fe8b36&apos;,
                lineTension: 0,
              }
            ]
          }
const options = {
    type: &apos;line&apos;,
    data: data,
    options: {
        fill: false,
        responsive: true,
        scales: {
            xAxes: [{
                type: &apos;time&apos;,
                display: true,
                scaleLabel: {
                    display: true,
                    labelString: &quot;Date&quot;,
                }
            }],
            yAxes: [{
                ticks: {
                    beginAtZero: true,
                },
                display: true,
                scaleLabel: {
                    display: true,
                    labelString: &quot;Page Views&quot;,
                }
            }]
        }
    }
}
const chart = new Chart(ctx, options);
&lt;/script&gt;

&lt;p&gt;That’s it. Hopefully I’ll remember I posted this here next time I need timeseries charts in chart.js&lt;/p&gt;

</description>
        
          <description>This is more of a note to myself as I seem to always take ages to do time series charts with Chart.js and somehow can&apos;t remember how I did it last time.</description>
        
        <pubDate>Fri, 18 Aug 2017 00:00:00 +0200</pubDate>
        <link>https://jfoucher.com/2017/08/timeseries-charts-with-chart.js.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2017/08/timeseries-charts-with-chart.js.html</guid>
        
        <category>javascript</category>
        
        <category>react</category>
        
        <category>charts</category>
        
        <category>chart.js</category>
        
        
      </item>
    
      <item>
        <title>Communication between iframes with React and postMessage for an html template editor</title>
        <description>&lt;p&gt;I’m working on a project where I need some sort of simple landing page editor, where “blocks” are defined at the template level and they can be rearranged, text changed or added, and not much more. Most of the software I could find was either way too complex for my needs, gave the user the freedom to &lt;em&gt;completely&lt;/em&gt; destroy the template, or were otherwise pretty useless.&lt;/p&gt;

&lt;p class=&quot;image&quot;&gt;&lt;img src=&quot;/uploads/2017/08/leanr.jpg&quot; alt=&quot;Editor in action&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If it was frustrating for me I imagine how it would feel for my users who need to do this quickly and efficiently. What I needed would’t have drag and drop, nor 300 sliders for everything, neither would it give the user the ability to completely modify the template. (Users can provide custom HTML for their pages if they so choose). Anyway, all I &lt;em&gt;need&lt;/em&gt; was the ability to place some template blocks on a page, move them up and down, remove them, and change the text bits.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“How hard can it be ?”&lt;/em&gt; &lt;br /&gt;&lt;small&gt;- me, at 3 AM on thursday night.&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How hard indeed ? Well, as it turns out, &lt;em&gt;quite&lt;/em&gt;. But it’s my fault. Scope creep. I decided it would also be possible to change the images. And also upload them. And maybe add custom buttons. And edit the background images. And the background colors. Anyway, here we are now, and this is how I’ve (almost, anyday now) done it :&lt;/p&gt;

&lt;p&gt;I used an iframe to hold the displayed template, as that allows complete style (and everything else) isolation, and gives a good preview of the final result. &lt;em&gt;However&lt;/em&gt;, javascript between the iframe and the “controlling” page is not that easy. Because of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy&quot;&gt;same origin policy&lt;/a&gt; we can’t just tell the iframe to do this or that, or read it’s elements to see the html that’s in them.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Inter-frame communication is like inter-process communication”&lt;/em&gt; &lt;br /&gt;&lt;small&gt;- My buddy who does C (or C++ maybe?)&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oh I forgot to say, I want to store somewhat structured data about the edits, not just the raw HTML, to enable easier further edits if necessary. &lt;a href=&quot;https://github.com/bustle/mobiledoc-kit&quot;&gt;mobiledoc&lt;/a&gt; seemed promising. I spent about an hour investigating it (my internet was slow that day, I swear!) before I realised it’s basically markdown. I thought adapting it to my use case, but it felt futile (as in: too much work). So what was I on about ? ah yes, iframes ! Mmmm who doesn’t love a  good iframe? You don’t? Good, ‘cause me neither. Anyway, inter-frame communication is like inter-process communication (or so says my buddy that does printer drivers for HP – Yeah, I’ll curse him on your behalf if you have an HP printer): you have to pass messages around, in our case with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.postMessage()&lt;/code&gt;&lt;/a&gt;. It works, it’s great!&lt;/p&gt;

&lt;p&gt;Anyway, here’s how it works: I like to think of it as communication between say a frontend and an API, mostly because of the asynchronous nature of the communication. So basically, the parent page get ready to receive messages from the iframe by listening to the “message” event, like so:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handleFrameTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m using react for the frontend, so this went in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;componentDidMount()&lt;/code&gt; method, but you could place it anywhere, really, as long as it runs when you want to start editing on the iframe side.&lt;/p&gt;

&lt;p&gt;And then on the iframe side I have something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;    &lt;span class=&quot;c1&quot;&gt;// Send message to react
&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;deleteBlock&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;blockId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;blockId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In fact at the start I was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.stringify()&lt;/code&gt;ing the data I sent, as I remember it being necessary a few years ago, but that’s not required anymore.&lt;/p&gt;

&lt;p&gt;Then on the parent React side, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;handleFrameTasks&lt;/code&gt; dispatches the actions required depending on what the iframe requested. For example in the case above it will delete the page block with id &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blockId&lt;/code&gt; from it’s page data structure.&lt;/p&gt;

&lt;p&gt;Did I mention that the iframe content is generated dynamically and injected in the iframe src with a data blob URL? Well then, maybe I’ll post about that the next time.&lt;/p&gt;

&lt;p&gt;Until then, keep coding !&lt;/p&gt;

&lt;p&gt;PS Oh about that &lt;a href=&quot;/2017/03/one-project-per-month-first-budget-tracking.html&quot;&gt;One project per month challenge&lt;/a&gt;? This one is bigger than I expected, so it will (has already been) longer than a month. It will be great, I hope. Everything you need for an early stage startup. &lt;a href=&quot;/contact.html&quot;&gt;Contact me&lt;/a&gt; if you’d like some more info.&lt;/p&gt;

</description>
        
          <description>I decided to roll my own HTML template editor because most opensource ones, while good looking and well designed, are impossible to use.</description>
        
        <pubDate>Thu, 10 Aug 2017 00:00:00 +0200</pubDate>
        <link>https://jfoucher.com/2017/08/interframe-communication-with-postmessage.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2017/08/interframe-communication-with-postmessage.html</guid>
        
        <category>javascript</category>
        
        <category>react</category>
        
        <category>iframe</category>
        
        <category>webdev</category>
        
        
      </item>
    
      <item>
        <title>One project per month during 2017. Here are my first two for January and february</title>
        <description>&lt;p&gt;So at the start of January I discovered the &lt;a href=&quot;https://github.com/1ppm/1ppmLog&quot;&gt;One Project per Month&lt;/a&gt; project, and decided to give it a go. You can follow my progress &lt;a href=&quot;https://github.com/1ppm/1ppmLog/blob/master/HallOfFame.md#jfoucher&quot;&gt;here&lt;/a&gt; and I’ll also try to post here somewhat regularly about my experiences.&lt;/p&gt;

&lt;p&gt;The first project was an app that does &lt;a href=&quot;https://budgt.eu&quot;&gt;budget tracking&lt;/a&gt;. Well, not really budgeting, more like to track what I was spending. I’ve been wondering where all the money went, and this app was made to help me achieve some visibility about that. It’s great, &lt;a href=&quot;https://budgt.eu&quot;&gt;check it out&lt;/a&gt; ! If the queen of england needed a way to track her spending, she told me she’d definitely use it !&lt;/p&gt;

&lt;p class=&quot;image&quot;&gt;&lt;img src=&quot;/assets/img/posts/queen-says-budgt-is-best-tracking-app.jpeg&quot; alt=&quot;Queen of England says budgt is the best app to track your spending&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Not that you care, but if you did you could go to the &lt;a href=&quot;https://github.com/jfoucher/budgetTracker&quot;&gt;github repo&lt;/a&gt; and there you might be able to find out that it was made with React, PouchDB and quite a few more nice things.&lt;/p&gt;

&lt;p&gt;Anyway, the second project was the February one (that’s what one per month means, for all of you following from your nursing home bed). I wanted to try my hand at native iOS apps. I looked at the Swift docs, installed Xcode but then I saw got bored and saw on hackernews that someone had built an app with react native and liked, so I said to myself : “Jonathan : do the right and be lazy. The important thing is to get this ready before the 28th” (Yes February was limited to 28 days by royal decree this year, and well, most years really… Who knows why ?) Anyway, so since &lt;a href=&quot;http://blogoscoped.com/archive/2005-08-24-n14.html&quot;&gt;all good programmers are lazy&lt;/a&gt; I didn’t feel too bad about myself.&lt;/p&gt;

&lt;p class=&quot;image&quot;&gt;&lt;img src=&quot;/assets/img/posts/become-lazy.jpeg&quot; alt=&quot;One does not simply become lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So react-native is was then. Let me tell you, this thing is great ! It works perfectly, you can design stuff with CSS like ease, and all the logic is in javascript. Easy peasy. You can’t get it anywhere though because Apple wants 100€ so that I can upload my free app. But do come by my house and I’ll gladly install it on your phone. If you have an android maybe I’ll just keep for a little while to do a bit of testing. But I’ll give it back. Eventually.&lt;/p&gt;

&lt;p&gt;So yes. So far so good. No idea for March though. Maybe I’ll just do a react-native app for the &lt;a href=&quot;https://budgt.eu&quot;&gt;budget tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any ideas ? &lt;a href=&quot;/contact&quot;&gt;Contact me&lt;/a&gt;.&lt;/p&gt;
</description>
        
          <description>This year I decided to try and develop one project every month. Here&apos;s how January and February went</description>
        
        <pubDate>Wed, 01 Mar 2017 09:17:07 +0100</pubDate>
        <link>https://jfoucher.com/2017/03/one-project-per-month-first-budget-tracking.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2017/03/one-project-per-month-first-budget-tracking.html</guid>
        
        <category>javascript</category>
        
        <category>project</category>
        
        <category>web</category>
        
        
      </item>
    
      <item>
        <title>Building a webGL and webworkers boat hydrostatics app</title>
        <description>&lt;p&gt;I’ve recently felt the urge to start designing boats again, as I have had some ideas I wanted to explore, specifically for boats of the &lt;a href=&quot;http://classemini.org&quot;&gt;Mini 650 class&lt;/a&gt;. The software I use has basic hydrostatics capabilities, but only for an upright boat. It cannot calculate the GZ curve, or get the righting moment at specific heel angles, which is a crucial data for the kind of craft I wanted to design.&lt;/p&gt;

&lt;p&gt;Existing solutions are extremely powerful and extremely expensive, so I decided i’d spend a few days rolling my own, with only the features I needed for the time being. I have all the technical background for this type of calculations, so I figured it shouldn’t be too hard. I really wanted to display a 3D view of the model, and not have it only output data in table format, and since I had never used web GL and was curious about, I figured it was the perfect time to try it out. Pro tip : if you’re not used to openGL or working in 3D programatically, use &lt;a href=&quot;https://threejs.org/&quot;&gt;three.js&lt;/a&gt;. Thank me later.&lt;/p&gt;

&lt;p&gt;So at first I built something very basic that just displayed the model in threejs and did some static calculations, in the UI thread. At first it worked ok, but as soon as I added functionality to solve for trim (to align longitudinal center of gravity with the longitudinal center of buoyancy) it started to get very slow. The 3D view could not even be moved around anymore because the UI thread was doing all the calculations in the foreground. Basically to do this kind of calculations, you have to move the model up and down to adjust for the correct displacement, and then pitch it forwards or aft until the center of buoyancy aligns with the center of gravity. This loop is computationally intensive and made everything grind to a halt.&lt;/p&gt;

&lt;p&gt;So webworkers to the rescue. I had never really used them either, but they promised to make it easy to send most of the work to a background thread that would not slow down my UI. I refactored my code to put all the heavy lifting into separate files that could then be called as a web worker. I also had to add messaging to pass data to and from the UI javascript. That was fairly easy to do, as they are simple calls to postMessage, and a callback function in the UI and the worker to receive sent messages. Very easy and obvious, so great !&lt;/p&gt;

&lt;p&gt;The calculations are accurate, I’ve had them checked in many cases against an old Hydromax version, which is an established solution in the field, and the results match within about a percentage point. The only difficulty I’m still struggling with is to properly draw the underwater stations and the waterline, but this is only a display bug and does not affect the results. What happens is that there are many edge cases that have to be accounted for, depending on the type and quality of the input file. I’m still working on that.&lt;/p&gt;

&lt;p&gt;If you like designing boats and currently do these kinds of calculations by hand or in a spreadsheet, or if your current software does not solve for trim, you will definitely want to &lt;a href=&quot;https://hydro.marinetechs.com/?utm_source=jfoucher&amp;amp;utm_campaign=launch&amp;amp;utm_medium=blog&quot;&gt;give this tool a try&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you do, and you like it, or you hate it, or it doesn’t work, or anything, your feedback would be invaluable. Contact me &lt;a href=&quot;mailto:j_foucher@marinetechs.com&quot;&gt;by email&lt;/a&gt; or through &lt;a href=&quot;/contact&quot;&gt;this blog’s contact form&lt;/a&gt;.&lt;/p&gt;
</description>
        
          <description>I had a need for a simple program to do some calculations for a boat design I&apos;m studying, so I built it from scratch with webGL and webworkers.</description>
        
        <pubDate>Tue, 20 Dec 2016 09:17:07 +0100</pubDate>
        <link>https://jfoucher.com/2016/12/webgl-webworkers-boat-calculations.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2016/12/webgl-webworkers-boat-calculations.html</guid>
        
        <category>javascript</category>
        
        <category>webgl</category>
        
        <category>webworker</category>
        
        <category>yachtdesign</category>
        
        
      </item>
    
      <item>
        <title>How I built running shoes for my whole family in one sunday afternoon (Huarache sandals)</title>
        <description>&lt;p&gt;Some time ago, &lt;a href=&quot;http://diegosantamarta.com&quot;&gt;Diego Santamarta&lt;/a&gt; casually mentioned that the shoes he was wearing were not regular flip flops, but rather some kind of sandal which I had never heard about. “Oh ok” was my less than enthusiastic answer. In the meantime, and even before, I had been reading about barefoot running, even though I had never run more than a few hundred meters without panting uncontrollably. Yes, 15 years smoking does take it’s toll. After two years tobacco free, I feel I’m only now starting to recover.&lt;/p&gt;

&lt;p&gt;Anyway, I had dismissed barefoot running as something that I would love to do — and probably the only way that I would ever run for pleasure — but that seemed very uncomfortable bordering on dangerous. Two days ago, that same Diego Santamarta posts to facebook a link about those sandals as used by barefoot runners to protect their feet on difficult terrain.&lt;/p&gt;

&lt;h3 id=&quot;documentation-and-materials&quot;&gt;Documentation and materials&lt;/h3&gt;

&lt;p&gt;I almost immediately started &lt;a href=&quot;http://bit.ly/10znWIx&quot;&gt;reading about them&lt;/a&gt;, watching &lt;a href=&quot;http://bit.ly/10WcB48&quot;&gt;youtube videos&lt;/a&gt; or &lt;a href=&quot;http://bit.ly/10zo22R&quot;&gt;instructables&lt;/a&gt; explaining how to build them. I had an old tire in the growing “to throw away” pile which I never get around to bringing to the recycling center, so that was the main part of the material sorted. I also have some polypropylene cord (the blue one used to tie straw bales and some black one) which could be used as laces. As will be seen later this is not comfortable nor durable so I used some more elaborate string for the laces.&lt;/p&gt;

&lt;p&gt;Trace your foot on paper and locate the holes as indicated in one of these guides.&lt;/p&gt;

&lt;h3 id=&quot;cutting-the-sole&quot;&gt;Cutting the sole&lt;/h3&gt;

&lt;p&gt;Now the real work begins. We will need to hack at that tire, and it truly is tiresome work. I had some tin snips on hand which I thought would be perfect for  the job. They are in fact totally useless. I did the job with a hacksaw to cut through the metal ring where the tire meets the wheel, and then one of my trusty &lt;a href=&quot;https://en.wikipedia.org/wiki/Opinel&quot;&gt;Opinels&lt;/a&gt; to cut through the reinforced rubber outwall. If the knife is kept sharp, this is actually fairly easy work.&lt;/p&gt;

&lt;p class=&quot;image&quot;&gt;&lt;img src=&quot;http://jfoucher.com/uploads/2013/05/cutting-tire.jpg&quot; alt=&quot;&amp;quot;Cutting the tire&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I cut some rough pieces from the outerwall, one from each side of the tire to match the curvature of the outerwall to the natural curve of each foot.&lt;/p&gt;

&lt;p&gt;You then trace the outline of your paper pattern on the pieces you just cut, enabling you to have perfect tire soles for your huarache sandals.&lt;/p&gt;

&lt;p class=&quot;image&quot;&gt;&lt;img src=&quot;http://jfoucher.com/uploads/2013/05/cutting-soles.jpg&quot; alt=&quot;&amp;quot;Matching sole to template&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I then used a small drill bit, around 5mm to drill the holes.&lt;/p&gt;

&lt;h3 id=&quot;putting-the-laces-in-places&quot;&gt;Putting the laces in places&lt;/h3&gt;

&lt;p&gt;The are many lacing systems that can be used, the one I used seemed simple enough, and can be slipped on and off easily.&lt;/p&gt;

&lt;p&gt;To pull the string through the holes, I used a piece of bent wire as a threader, and pliers to pull on it if need be. A &lt;a href=&quot;http://en.wikipedia.org/wiki/Figure-eight_knot&quot;&gt;figure of 8 knot&lt;/a&gt; locks the string in the toe hole. the string then goes throught the outer hole, around the heel, through the inner whole, and ties around the front part of the lace&lt;/p&gt;

&lt;p class=&quot;image&quot;&gt;&lt;img src=&quot;http://jfoucher.com/uploads/2013/05/threading-cord.jpg&quot; alt=&quot;&amp;quot;Threading the cord&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here is the result: my son’s sandals. He complained that they were not very comfortable though, so I changed the cord (which was too thin) and used a thicker braided one I had around, which is much more comfortable.&lt;/p&gt;

&lt;p class=&quot;image&quot;&gt;&lt;img src=&quot;http://jfoucher.com/uploads/2013/05/huarache-running-sandal-kid.jpg&quot; alt=&quot;&amp;quot;Huarache running sandal complete&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once everyone had their sandals ready, I decided to go for a short run to try them out&lt;/p&gt;

&lt;h3 id=&quot;running&quot;&gt;Running&lt;/h3&gt;

&lt;p&gt;Please keep in mind that I was a heavy smoker for many years, have &lt;em&gt;never&lt;/em&gt; run unless obligated to, and never more than a few hundred meters.&lt;/p&gt;

&lt;p&gt;The first segment was downhill, and I had to readjust the shoes almost immediately, for the only time in the entire run.&lt;/p&gt;

&lt;p&gt;When I arrived at the first uphill segment I got out of breath quickly and decide to alternate running and fast walking for the rest of the outing. That worked out pretty good, and the running actually felt easier and easier. The shoes are superb. They are pretty comfortable, provide just the right amount of protection to avoid having to look out for every little piece of gravel and can be used to cross streams without fear of them becoming slippery.&lt;/p&gt;

&lt;p&gt;My calves started hurting at around the ten minute mark, I think from landing on the front portion of the feet. It only got worse from then on, and now I can barely walk, it feels like I have a continuous cramp in these muscles. The feet are in perfect condition though, I just feel that they have had a good workout.&lt;/p&gt;

&lt;p&gt;Can’t wait to go out tomorrow for more!&lt;/p&gt;
</description>
        
          <description>I never run unless forced to, but today I decided to build modern/traditional running shoes for my whole family and go for a run. Boy am I sore.</description>
        
        <pubDate>Sun, 26 May 2013 21:17:07 +0200</pubDate>
        <link>https://jfoucher.com/2013/05/building-and-using-minimalist-running-shoes.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2013/05/building-and-using-minimalist-running-shoes.html</guid>
        
        <category>sport</category>
        
        <category>running</category>
        
        <category>shoes</category>
        
        
      </item>
    
      <item>
        <title>Installing PHP, Apache2 and nginx for faster websites</title>
        <description>&lt;p&gt;My contract with my previous shared hosting contractor is coming to a close, and as they seem to have all but abandoned their business, I decided it was time to move away. I looked at using Amazon’s EC2, but ultimately decided to go with Bhost, with which I’m fairly happy so far. To get some decent performance out of it, I planned to use Nginx as my web server&lt;/p&gt;

&lt;h3 id=&quot;keep-using-apache-as-usual&quot;&gt;Keep using Apache as usual&lt;/h3&gt;

&lt;p&gt;All my websites are configured to work with apache, sometimes using fairly advanced rewrite rules, and a fair proportion of wordpress sites. So to save myself a ton of work I decided to just use Nginx as a proxy to apache and not reconfigure each site to use Nginx’s rewrite rules.&lt;/p&gt;

&lt;h3 id=&quot;install-required-software&quot;&gt;Install required software&lt;/h3&gt;

&lt;p&gt;I’m using Ubuntu, but the steps below should be fairly easy to adapt to your distribution of choice. To install everything from scratch, just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get install php5 apache2 mysql-server php5-mysql nginx&lt;/code&gt;. This installs mysql, Apache, PHP and Nginx&lt;/p&gt;

&lt;h3 id=&quot;configure-apache-and-nginx&quot;&gt;Configure Apache and Nginx&lt;/h3&gt;

&lt;p&gt;I created one bash script that creates the necessary virtual hosts files for Apache and Nginx, by taking a template a doing a search/replace with sed. Here is the apache virtual host template that goes into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/apache2/sites-available&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;VirtualHost&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; *:8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;nc&quot;&gt;ServerAdmin&lt;/span&gt; webmaster@localhost
        &lt;span class=&quot;nc&quot;&gt;ServerName&lt;/span&gt; template
        &lt;span class=&quot;nc&quot;&gt;ServerAlias&lt;/span&gt; template www.template cdn.template cdn2.template
        &lt;span class=&quot;nc&quot;&gt;DocumentRoot&lt;/span&gt; /var/www/template
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; /&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;                &lt;span class=&quot;nc&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;FollowSymLinks&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;AllowOverride&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;None&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; /var/www/template/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;                &lt;span class=&quot;nc&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;Indexes&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;FollowSymLinks&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;MultiViews&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;AllowOverride&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;All&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;Order&lt;/span&gt; allow,deny
                &lt;span class=&quot;nc&quot;&gt;allow&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;all&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;nc&quot;&gt;ScriptAlias&lt;/span&gt; /cgi-bin/ /usr/lib/cgi-bin/
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; &quot;/usr/lib/cgi-bin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;                &lt;span class=&quot;nc&quot;&gt;AllowOverride&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;None&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;Options&lt;/span&gt; +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                &lt;span class=&quot;nc&quot;&gt;Order&lt;/span&gt; allow,deny
                &lt;span class=&quot;nc&quot;&gt;Allow&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;all&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;nc&quot;&gt;ErrorLog&lt;/span&gt; ${APACHE_LOG_DIR}/error.log
        &lt;span class=&quot;c&quot;&gt;# Possible values include: debug, info, notice, warn, error, crit,
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# alert, emerg.
&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;LogLevel&lt;/span&gt; warn
        &lt;span class=&quot;nc&quot;&gt;CustomLog&lt;/span&gt; ${APACHE_LOG_DIR}/access.log combined
        &lt;span class=&quot;nc&quot;&gt;Alias&lt;/span&gt; /doc/ &quot;/usr/share/doc/&quot;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; &quot;/usr/share/doc/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;            &lt;span class=&quot;nc&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;Indexes&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;MultiViews&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;FollowSymLinks&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;AllowOverride&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;None&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Order&lt;/span&gt; deny,allow
            &lt;span class=&quot;nc&quot;&gt;Deny&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;all&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Allow&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;from&lt;/span&gt; 127.0.0.0/255.0.0.0 ::1/128
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;VirtualHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You’ll notice that Apache listens on port 8080. You need to change the port from 80 to 8080 in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/apache2/ports.conf&lt;/code&gt; as well, this is what mine looks like after the change :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;    &lt;span class=&quot;nc&quot;&gt;NameVirtualHost&lt;/span&gt; *:8080
    &lt;span class=&quot;nc&quot;&gt;Listen&lt;/span&gt; 8080
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; mod_ssl.c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;c&quot;&gt;# If you add NameVirtualHost *:443 here, you will also have to change
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# the VirtualHost statement in /etc/apache2/sites-available/default-ssl
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# to &amp;lt;VirtualHost *:443&amp;gt;
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Server Name Indication for SSL named virtual hosts is currently not
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# supported by MSIE on Windows XP.
&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Listen&lt;/span&gt; 443
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; mod_gnutls.c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;nc&quot;&gt;Listen&lt;/span&gt; 443
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next up is the template for the default nginx site, which is much simpler and goes in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/nginx/sites-available&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot; data-lang=&quot;nginx&quot;&gt;    &lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;access_log&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/var/log/nginx.access.log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;error_log&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/var/log/nginx.error.log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/var/www/template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.php&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;*.template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Real-IP&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-For&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://127.0.0.1:8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/\.(ht|git)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kn&quot;&gt;deny&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;All that does is proxy everything to apache on localhost, port 8080&lt;/p&gt;

&lt;p&gt;So, our virtual host templates are ready, and if you only have one or two sites you could copy the contents to a new file by hand and change the word &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&lt;/code&gt; for your actual domain name / folder, but as I had quite a few sites to convert, I created a small bash script that takes car of it for me, by just calling it with the hostname:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;    &lt;span class=&quot;c&quot;&gt;#!/bin/bash
&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;CMDLN_ARGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Command line arguments for this script
&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;CMDLN_ARGS
    &lt;span class=&quot;c&quot;&gt;# Run this script as root if not already.
&lt;/span&gt;
    chk_root &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-eq&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
       &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Please enter your password.&quot;&lt;/span&gt;
       &lt;span class=&quot;nb&quot;&gt;exec sudo &lt;/span&gt;su &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CMDLN_ARGS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Call this prog as root
&lt;/span&gt;
       &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# since we&apos;re &apos;execing&apos; above, we wont reach this exit
&lt;/span&gt;
                  &lt;span class=&quot;c&quot;&gt;# unless something goes wrong.
&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    chk_root
    &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /etc/apache2/sites-available/template | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;s/template/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/g&quot;&lt;/span&gt; |cat &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /etc/apache2/sites-available/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; /etc/apache2/sites-available/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; /etc/apache2/sites-enabled/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /etc/nginx/sites-available/template | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;s/template/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/g&quot;&lt;/span&gt; |cat &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /etc/nginx/sites-available/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; /etc/nginx/sites-available/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; /etc/nginx/sites-enabled/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; /var/www/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;then
      &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;host directory exists&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else
      &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;creating directory&quot;&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; www-data:www-data /var/www/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    /etc/init.d/apache2 restart
    /etc/init.d/nginx restart&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Put this say in your home folder and give it executable permission with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 755 new_host&lt;/code&gt; or whatever you named the file.&lt;/p&gt;

&lt;p&gt;Then run it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./new_host example.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will create a folder in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/example.com&lt;/code&gt;, copy the updated template virtual host configuration to the required location and restart apache and nginx.&lt;/p&gt;

&lt;p&gt;Now put some content in that folder and configure your mysql connection if neccesary&lt;/p&gt;

&lt;p&gt;You should now be able to browse to example.com and see your brand new site. Some quick benchmarking with ab gives roughly a tenfold increase in requests per second, depending on the configuration of the website itself&lt;/p&gt;

&lt;p&gt;These are some rough numbers for &lt;a href=&quot;http://6px.eu&quot;&gt;Six Pixels&lt;/a&gt;, built with Kohana2 without any caching mechanism&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ab -c20 -n200 http://6px.eu:8080/&lt;/code&gt; is hitting Apache on port 8080&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Server Hostname:        6px.eu
Server Port:            8080

Document Path:          /
Document Length:        12101 bytes

Concurrency Level:      20
Time taken for tests:   282.679 seconds
Complete requests:      200
Failed requests:        0
Write errors:           0
Total transferred:      2486000 bytes
HTML transferred:       2420200 bytes
Requests per second:    0.71 [#/sec] (mean)
Time per request:       28267.940 [ms] (mean)
Time per request:       1413.397 [ms] (mean, across all concurrent requests)
Transfer rate:          8.59 [Kbytes/sec] received
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now with nginx &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ab -c20 -n200 http://6px.eu/&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Server Hostname:        6px.eu
Server Port:            80

Document Path:          /
Document Length:        12081 bytes

Concurrency Level:      20
Time taken for tests:   40.606 seconds
Complete requests:      200
Failed requests:        0
Write errors:           0
Total transferred:      2482000 bytes
HTML transferred:       2416200 bytes
Requests per second:    4.93 [#/sec] (mean)
Time per request:       4060.612 [ms] (mean)
Time per request:       203.031 [ms] (mean, across all concurrent requests)
Transfer rate:          59.69 [Kbytes/sec] received
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;update&quot;&gt;Update:&lt;/h3&gt;

&lt;p&gt;As rightly noted by &lt;a href=&quot;https://twitter.com/#!/arnaud_lb/status/196610128261296129&quot;&gt;@arnaud_lb&lt;/a&gt; the benchmarks should give nearly the same results,
as nginx is waiting for Apache to return it’s data before sending it along.
What happened in those benchmarks is that &lt;a href=&quot;http://cloudflare.com&quot;&gt;cloudflare.com&lt;/a&gt; (highly recommended by the way) was caching some results and not others.&lt;/p&gt;

&lt;p&gt;When I tried on localhost, the results were effectively the same.
So the way to get better performance is to get nginx to cache the results from apache, by adding the following to your nginx virtual host definition :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot; data-lang=&quot;nginx&quot;&gt;    &lt;span class=&quot;k&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;proxy_cache_use_stale&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;invalid_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;updating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;proxy_cache_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$scheme$host$request_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;proxy_cache_valid&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;301&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;302&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;proxy_cache_valid&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;proxy_cache_valid&lt;/span&gt;       &lt;span class=&quot;s&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;where the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy_cache&lt;/code&gt; named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;one&lt;/code&gt; is defined in nginx.conf as such :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot; data-lang=&quot;nginx&quot;&gt;    &lt;span class=&quot;k&quot;&gt;proxy_cache_path&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/usr/local/nginx/proxy&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;levels=1:2&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;keys_zone=one:15m&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;inactive=7d&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;max_size=1000m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy_cache_valid&lt;/code&gt; entries above define different cache times for various response codes&lt;/p&gt;

&lt;p&gt;And here are the real benchmarks :&lt;/p&gt;

&lt;h4 id=&quot;apache&quot;&gt;Apache:&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Server Hostname:        direct.6px.eu
Server Port:            8080

Document Path:          /
Document Length:        11487 bytes

Concurrency Level:      10
Time taken for tests:   15.082 seconds
Complete requests:      200
Failed requests:        0
Write errors:           0
Total transferred:      2338600 bytes
HTML transferred:       2297400 bytes
Requests per second:    13.26 [#/sec] (mean)
Time per request:       754.075 [ms] (mean)
Time per request:       75.408 [ms] (mean, across all concurrent requests)
Transfer rate:          151.43 [Kbytes/sec] received
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;nginx&quot;&gt;Nginx:&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Server Hostname:        direct.6px.eu
Server Port:            80

Document Path:          /
Document Length:        3031 bytes

Concurrency Level:      10
Time taken for tests:   0.027 seconds
Complete requests:      200
Failed requests:        0
Write errors:           0
Total transferred:      654600 bytes
HTML transferred:       606200 bytes
Requests per second:    7305.94 [#/sec] (mean)
Time per request:       1.369 [ms] (mean)
Time per request:       0.137 [ms] (mean, across all concurrent requests)
Transfer rate:          23351.88 [Kbytes/sec] received
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just, wow…&lt;/p&gt;

&lt;p&gt;Careful though as this will wreak havoc on your sessions, at the minimum you can disable caching for logged-in users with&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot; data-lang=&quot;nginx&quot;&gt;    &lt;span class=&quot;k&quot;&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$cookie_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;proxy_no_cache&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$cookie_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session&lt;/code&gt; is the name of your cookie&lt;/p&gt;
</description>
        
          <description>I recently decided to go with a small VPS for some of my sites, and to get decent performance without too much work I used Nginx as a proxy to Apache2.</description>
        
        <pubDate>Mon, 23 Apr 2012 08:17:07 +0200</pubDate>
        <link>https://jfoucher.com/2012/04/installing-apache-php-and-nginx-for-faster-websites.html</link>
        <guid isPermaLink="true">https://jfoucher.com/2012/04/installing-apache-php-and-nginx-for-faster-websites.html</guid>
        
        <category>webdev</category>
        
        <category>php</category>
        
        <category>server admin</category>
        
        
      </item>
    
  </channel>
</rss>
