<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Paul (hxii) Glushak</title>
    <description>The latest articles on Forem by Paul (hxii) Glushak (@hxii).</description>
    <link>https://forem.com/hxii</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F318131%2F810f84bd-7f81-4faf-9567-085476ad0db2.png</url>
      <title>Forem: Paul (hxii) Glushak</title>
      <link>https://forem.com/hxii</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hxii"/>
    <language>en</language>
    <item>
      <title>Automating the Boring Stuff</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Thu, 16 May 2024 15:25:01 +0000</pubDate>
      <link>https://forem.com/hxii/automating-the-boring-stuff-38dj</link>
      <guid>https://forem.com/hxii/automating-the-boring-stuff-38dj</guid>
      <description>&lt;p&gt;This is copy pasted from my &lt;a href="https://0xff.nu"&gt;blog&lt;/a&gt; with added commentary below.&lt;/p&gt;




&lt;p&gt;The idea for &lt;em&gt;boku&lt;/em&gt; came from my dayjob, where we developed a robust onboarding script that takes care of setting up development environments for new onboardees.&lt;/p&gt;

&lt;p&gt;While the onboarding script we use is robust and comfortable, it requires knowledge in Python to be able to add even the simplest of tasks to it. Moreover, each addition requires the binary to be recompiled and redistributed, so the portability and versatility of this tool suffers as a result.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Boku&lt;/em&gt; then comes to bridge this gap, yet is not intended to replace much more robust tools in order to keep it's simplicity.&lt;/p&gt;

&lt;p&gt;The TL;DR is -&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Was bored&lt;/li&gt;
&lt;li&gt;Thought it might be useful because I personally need it&lt;/li&gt;
&lt;li&gt;Learning experience \o/&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;The idea to automate things is doubtfully foreign to any of us, as we constantly seek better, more creative, more efficient ways of achieving certain results and completing certain tasks, while, if we're being honest, investing as little resources as possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/d/onboarding master• ❱ pygount . --suffix py -f summary
┏━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━━━━┳━━━━━━┓
┃ Language      ┃ Files ┃     % ┃ Code ┃    % ┃ Comment ┃    % ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━╇━━━━━━━━━╇━━━━━━┩
│ Python        │    52 │  94.5 │ 3571 │ 54.7 │    1090 │ 16.7 │
│ __empty__     │     1 │   1.8 │    0 │  0.0 │       0 │  0.0 │
│ __duplicate__ │     2 │   3.6 │    0 │  0.0 │       0 │  0.0 │
├───────────────┼───────┼───────┼──────┼──────┼─────────┼──────┤
│ Sum           │    55 │ 100.0 │ 3571 │ 54.7 │    1090 │ 16.7 │
└───────────────┴───────┴───────┴──────┴──────┴─────────┴──────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's easy to throw in overkill solutions (or money) at problems, and while the outcome will most likely be the same, the resources required or wasted (let alone the lack of any newly gained knowledge) will most likely offset the benefits.&lt;/p&gt;

&lt;p&gt;These are the thoughts I had in mind while thinking about ways of improving the aforementioned onboarding script.&lt;br&gt;
However, the ultimate decision for the onboarding script was to leave it as is complexity-wise, and improve it in other areas, but take the basic premise of developing a code-less solution for small tasks into a separate tool.&lt;/p&gt;

&lt;p&gt;Just as an example, here's how a simple task could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.4&lt;/span&gt;
&lt;span class="na"&gt;information&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;Create a Mise config in the folder.&lt;/span&gt;
    &lt;span class="s"&gt;Author: Paul Glushak (paul@glushak.net)&lt;/span&gt;
&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;[tools]&lt;/span&gt;
        &lt;span class="s"&gt;python = "latest"&lt;/span&gt;

        &lt;span class="s"&gt;[env]&lt;/span&gt;
        &lt;span class="s"&gt;_.python.venv = { path = ".venv", create = true }&lt;/span&gt;
&lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;create_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;cat &amp;gt; .mise.toml &amp;lt;&amp;lt; EOL&lt;/span&gt;
            &lt;span class="s"&gt;variables.config&lt;/span&gt;
            &lt;span class="s"&gt;EOL&lt;/span&gt;
    &lt;span class="na"&gt;trust&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mise trust&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is how TaskRunner™ was born! Yea, very original, I know. Worry not, now it's called &lt;em&gt;boku&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The intention was, and is to keep the feature set simple, and make the tasks portable, hence the YAML files. This also allows &lt;em&gt;boku&lt;/em&gt; to tie in to an existing workflow, or be run as a cronjob.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/d/boku main• ❱ pygount . --suffix py -f summary
┏━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━━━━┳━━━━━┓
┃ Language  ┃ Files ┃     % ┃ Code ┃    % ┃ Comment ┃   % ┃
┡━━━━━━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━╇━━━━━━━━━╇━━━━━┩
│ Python    │     4 │  80.0 │  473 │ 75.4 │      47 │ 7.5 │
│ __empty__ │     1 │  20.0 │    0 │  0.0 │       0 │ 0.0 │
├───────────┼───────┼───────┼──────┼──────┼─────────┼─────┤
│ Sum       │     5 │ 100.0 │  473 │ 75.4 │      47 │ 7.5 │
└───────────┴───────┴───────┴──────┴──────┴─────────┴─────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To conclude, as with other tools and things that I developed for personal use, I often find that a tailor-made brick can be 100% more effective than a generic, mass-produced spaceship (Wonderful analogy, I know!), depending on the situation of course.&lt;/p&gt;

&lt;p&gt;Seek to improve, strive for efficiency and don't be afraid to make stuff for yourself and by yourself. Worst case - you're gonna learn something new.&lt;/p&gt;




&lt;p&gt;Check out boku on &lt;a href="https://git.sr.ht/~hxii/boku"&gt;SourceHut&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>automation</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Notes.cx</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Thu, 29 Jul 2021 09:53:17 +0000</pubDate>
      <link>https://forem.com/hxii/notes-cx-1l4e</link>
      <guid>https://forem.com/hxii/notes-cx-1l4e</guid>
      <description>&lt;p&gt;Notes.cx is a simple public note taking app where every note you create (anonymously) lives for 24 hours.&lt;/p&gt;

&lt;p&gt;Every time you open the main page, a random ID is being generated for an empty note.&lt;/p&gt;

&lt;p&gt;You can compose your note using Markdown syntax, or just paste a bunch of text if you don't want it to look pretty ¯\_(ツ)_/¯.&lt;/p&gt;

&lt;p&gt;After you're done writing, just hit Enter or click Create and share your note with the world.&lt;/p&gt;

&lt;p&gt;Any note you create (which lives for 24 hours) can only be edited by you.&lt;/p&gt;

&lt;p&gt;Check it out at &lt;a href="https://notes.cx/"&gt;https://notes.cx/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
    </item>
    <item>
      <title>diary.by</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Thu, 22 Jul 2021 12:40:14 +0000</pubDate>
      <link>https://forem.com/hxii/diary-by-3j2f</link>
      <guid>https://forem.com/hxii/diary-by-3j2f</guid>
      <description>&lt;p&gt;Very excited and happy to introduce my new project - diary.by!&lt;/p&gt;

&lt;p&gt;diary.by is a project I've been carving away at for some time now. It's a super simple blog/diary/notes/journal platform that will let you write in peace, without tracking, ads, likes and other digital noise. The focus here is to give you a simple, easy to use and clean slate on the internet to share your thoughts with the world.&lt;/p&gt;

&lt;p&gt;You can check it out here: &lt;a href="https://diary.by/"&gt;https://diary.by/&lt;/a&gt;&lt;br&gt;
Example post: &lt;a href="https://diary.by/%7Epaul/minimalism-again"&gt;https://diary.by/~paul/minimalism-again&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: diary.by is currently invite-only. If you'd like to try it out, please send me a DM and I'll be happy to provide you with an invite code.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>php</category>
    </item>
    <item>
      <title>Text Summarizer Experiment</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Tue, 19 Jan 2021 10:41:04 +0000</pubDate>
      <link>https://forem.com/hxii/text-summarizer-experiment-1c74</link>
      <guid>https://forem.com/hxii/text-summarizer-experiment-1c74</guid>
      <description>&lt;p&gt;Another weekend, another experiment. This time I decided to dawdle around a bit with text processing.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;actual&lt;/strong&gt; goal was to come up with an automatic tagging system for my personal KMS/KB project (you can read about it &lt;a href="https://0xff.nu/making-my-own-kms"&gt;here&lt;/a&gt;) which led me down a rabbit hole of mostly fooling around with text and code as well as a little bit of research.&lt;/p&gt;

&lt;p&gt;The automatic tagging was put on hold, but the concept and base code behind it paved the way for this little project and while this is far from being finished and/or actually usable, I managed to learn quite a bit about the process.&lt;/p&gt;

&lt;p&gt;There exist libraries and projects that do a much better job, but using something readymade is not as fun now, is it?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://0xff.nu/text-summarization"&gt;The Process&lt;/a&gt;&lt;br&gt;
&lt;a href="https://0xff.nu/p/ts"&gt;Demo&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/hxii/text_summarizer"&gt;Code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How-to: Creating a super simple URL shortening service</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Sat, 10 Oct 2020 13:29:20 +0000</pubDate>
      <link>https://forem.com/hxii/how-to-creating-a-super-simple-url-shortening-service-2nhm</link>
      <guid>https://forem.com/hxii/how-to-creating-a-super-simple-url-shortening-service-2nhm</guid>
      <description>&lt;p&gt;You know how they say the best way to learn something is to teach it?&lt;br&gt;
I personally don't have time to participate in something like &lt;a href="https://www.100daysofcode.com/"&gt;100 days of code&lt;/a&gt; or anything similar, but that doesn't mean that I shouldn't do &lt;em&gt;something&lt;/em&gt;, you know?&lt;/p&gt;

&lt;p&gt;So today I will explain how I made a super simple URL shortening service for myself and hopefully it will help some of you.&lt;/p&gt;

&lt;p&gt;The code is available &lt;a href="https://0xff.nu/url-shortener"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please note:&lt;/strong&gt; This is &lt;em&gt;my&lt;/em&gt; take on a URL shortener and this whole thing is written as an exercise based on my current level of PHP knowledge, so I may revisit this in the future. This is most likely not the best or most efficient way of doing this. Please keep this in mind while reading this and, of course, I'd love to hear from you if you've got any ideas or suggestions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why do I/you need this?
&lt;/h2&gt;

&lt;p&gt;Traditionally, I guess, URL shorteners were and are used to make long URLs short. These are often used in places where there exists a limit on the character count that can be used such as Twitter, SMS, Mastodon, various descriptions et cetera.&lt;br&gt;
They can also be used to create "nice" links (i.e. &lt;code&gt;service.tld/mylink&lt;/code&gt; is "nicer" than &lt;code&gt;somewebsiteonline.tld/service/longlinkgalore?query=yesplease&lt;/code&gt;, right?) and as a way to make sure certain links don't change (e.g. &lt;code&gt;service.tld/blog&lt;/code&gt; will always point to your blog's address even if you move to a different domain).&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I am personally using nginx with a directive that forwards all requests to the index file in case the requested path is not found.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;location&lt;/span&gt; / {
&lt;span class="n"&gt;try_files&lt;/span&gt; $&lt;span class="n"&gt;uri&lt;/span&gt; $&lt;span class="n"&gt;uri&lt;/span&gt;/ /&lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;$&lt;span class="n"&gt;is_args&lt;/span&gt;$&lt;span class="n"&gt;args&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;There is no need for a database as all entries as stored in a file.&lt;/li&gt;
&lt;li&gt;Currently, I am using &lt;a href="https://insomnia.rest/"&gt;Insomnia&lt;/a&gt; to add, remove and list entries. There is no GUI.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The "database"
&lt;/h2&gt;

&lt;p&gt;Instead of using an SQL database that would've indubitably made this more complex than it should be (not by much, but still) we are going to store all entries in a simple file.&lt;/p&gt;

&lt;p&gt;To introduce some security, I went with a PHP file that has the following header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"HTTP/1.0 404 Not Found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This means, that if the database file is accessed directly it will just return a 404 error.&lt;br&gt;
The entries themselves are stored on an entry per line basis with a single space separator between the key and URL, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@home https://0xff.nu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;

&lt;p&gt;The script needs to have a couple of parts in order to work as we want it to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Variables and Init - Where we store and create the things.&lt;/li&gt;
&lt;li&gt;Database - Reading from and storing to the database file.&lt;/li&gt;
&lt;li&gt;Router - Routing the request through the correct method.&lt;/li&gt;
&lt;li&gt;Redirection - The main purpose of this thing - redirecting the request.&lt;/li&gt;
&lt;li&gt;Management - Creating and deleting entries.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Variables and Init
&lt;/h3&gt;

&lt;p&gt;We are using a handful of variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$dbHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;?php header("HTTP/1.0 404 Not Found");die(); ?&amp;gt;'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$authkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'79A69C0D4B9DFCD94B1BF72799E334D0CC4D1972'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;readDatabase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$filename&lt;/code&gt; - The database filename we initialize the class with.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$database&lt;/code&gt; - This is where the database will be stored after it's parsed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$prefix&lt;/code&gt; - Entry prefix, if specified. I am using &lt;code&gt;@&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$dbHeader&lt;/code&gt; - The header that should be prepended to the database file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$authkey&lt;/code&gt; - Hashed key that should be used to authenticate management commands.&lt;/li&gt;
&lt;li&gt;The contructor method sets the variables with the values given once the class is instantiated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;initializeDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;file_put_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dbHeader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;readDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;file_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;initializeDatabase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;$rawDatabase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rawDatabase&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$fh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dbHeader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initializeDatabase()&lt;/code&gt; creates a new file and appends the header to it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;readDatabase()&lt;/code&gt; reads the database (or calls to initialize it), parses the entries and stores them into the &lt;code&gt;$database&lt;/code&gt; variable. The first "entry" is sliced off as it's actually the header (&lt;code&gt;$dbHeader&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;updateDatabase()&lt;/code&gt; updates the database &lt;code&gt;$filename&lt;/code&gt; with the values from &lt;code&gt;$database&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Router
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;parseRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'request'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;matchRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;parseRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'request'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'Entry'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;is_callable&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;call_user_func_array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;doRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'request'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;parseRequest()&lt;/code&gt; parses the request that was made from both the URI and POST data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;matchRequest()&lt;/code&gt; routes the request through the appropriate method, else we're calling the &lt;code&gt;doRedirect()&lt;/code&gt; method to try and perform the redirect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note the &lt;code&gt;validate()&lt;/code&gt; method. We use this to make sure the request is authorized:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PHP_AUTH_PW'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;authkey&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PHP_AUTH_PW'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;http_response_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If the auth key does not match, halt execution and return &lt;code&gt;401 Unauthorized&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I am using a &lt;a href="https://en.wikipedia.org/wiki/Basic_access_authentication"&gt;basic auth&lt;/a&gt; so it's important to use this together with HTTPS to actually be somewhat secure.&lt;br&gt;
You can, of course, implement better security if you wish.&lt;/p&gt;
&lt;h3&gt;
  
  
  Redirection
&lt;/h3&gt;

&lt;p&gt;Redirection is very, very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;doRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;doesEntryExist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Location: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;http_response_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If the requested key exists in the database, we simply append a &lt;code&gt;Location&lt;/code&gt; header&lt;sup id="fnref1"&gt;1&lt;/sup&gt; which redirects to the location we want.&lt;br&gt;
If it doesn't, we return a &lt;code&gt;404 Not Found&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Management
&lt;/h3&gt;

&lt;p&gt;For management, we have four methods: add, update, remove and list entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;generateID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'loc'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;doesEntryExist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'loc'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;removeEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;doesEntryExist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;unset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;listEntry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;generateID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;base64_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;microtime&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;These are fairly self-explanatory. Each method does the neccessary change and updates the database.&lt;br&gt;
You can also notice that we can create new entries without specifying a key.&lt;/p&gt;

&lt;p&gt;If a key is not specified, it will be created by &lt;code&gt;generateID()&lt;/code&gt; which, in essence, creates an ID of a specified &lt;code&gt;$length&lt;/code&gt; from a randomized, base64 encoded unix timestamp.&lt;br&gt;
You may notice there's no verification if the generated ID already exists, but this should be easy to add using &lt;code&gt;doesEntryExist()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was fairly easy but still a fun to write excercise. I am certain some things can be further simplified or written better to begin with, so I will revisit this little project from time to time to make some adjustments.&lt;/p&gt;

&lt;p&gt;This functionality will probably be added to &lt;a href="https://0xff.nu/saisho"&gt;Saisho&lt;/a&gt; or may be on a separate domain, we'll see.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Location header: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location"&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>php</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>A case for microblogging</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Sun, 27 Sep 2020 14:55:27 +0000</pubDate>
      <link>https://forem.com/hxii/a-case-for-microblogging-132d</link>
      <guid>https://forem.com/hxii/a-case-for-microblogging-132d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This entry is just me thinking out loud and trying to make a case for microblogging.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recently I &lt;a href="https://merveilles.town/@hxii/104875101804537409"&gt;tooted&lt;/a&gt; about potentially adding support for microblogging to, at least, my website if not Saisho in general. But this made me think and consider the options, requirements, and ways of implementing this (potentially) minor functionality.&lt;/p&gt;

&lt;p&gt;The basic need and idea are simple - a way to distinguish between long-form articles and posts to short updates, shares, and misc posts, but it turns out that implementing this correctly could prove a little bit more challenging than I initially thought.&lt;/p&gt;

&lt;p&gt;There are several problems that must be addressed and questions that must be answered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Necessity - Do I really need to add this functionality? Why not just mix all the different kinds of posts together? Is this going to make my life easier?&lt;/li&gt;
&lt;li&gt;File Structure - If I do implement this, would the microblog entries be in the same &lt;code&gt;/data/&lt;/code&gt; folder? A separate folder? How would the files look like? Metadata?&lt;/li&gt;
&lt;li&gt;Visuals - How would I display the microblog entries? Should they be in a separate list? Should I emphasize the distinction?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Necessity
&lt;/h2&gt;

&lt;p&gt;To be frank here, I'm not &lt;em&gt;entirely&lt;/em&gt; sure I &lt;em&gt;need&lt;/em&gt; microblogging, but I do know that I will sometimes refrain from posting something unless I have enough "material", so microblogging would certainly address this.&lt;/p&gt;

&lt;p&gt;I guess it is also important to understand what microblogging is in its essence and to the best of my understanding it exists to express thoughts out loudly, draft things and quickly share resources with the people that are following your blog. A replacement for Twitter, if you will.&lt;/p&gt;

&lt;p&gt;Maybe for this reason the micro-entries should be separated from, you know, the "actual content".&lt;/p&gt;

&lt;h2&gt;
  
  
  File Structure
&lt;/h2&gt;

&lt;p&gt;There are two issues here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entries require a front matter header. The whole point of microblogging is for it to be a quick way to share information, so writing metadata doesn't make sense.&lt;/li&gt;
&lt;li&gt;All entries are stored in the &lt;code&gt;/data&lt;/code&gt; folder. It would make sense to keep all entries in the same folder, but it would also make sense to separate the micro-entries from the rest of the content, in the interest of keeping some sort of organization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these would most likely necessitate a code change in Saisho, at the very least the separate data folder part.&lt;br&gt;
The header conundrum can be resolved by using an API that will create the files with the appropriate metadata - which will make creating entries even simpler.&lt;/p&gt;

&lt;p&gt;There are a few ways to go about this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Separate data folder but metadata stays - core code change which means everyone gets microblogging. Will probably require API to create micro-entries.&lt;/li&gt;
&lt;li&gt;Separate data folder and no metadata - core code changes with an extra side of code changes, as metadata will be taken from filename and fileinfo.&lt;/li&gt;
&lt;li&gt;Same data folder and metadata stays - no core changes. Doable by using a "magic" tag and the implementation relies on the template.&lt;/li&gt;
&lt;li&gt;Same data folder and no metadata - requires core changes (same as #2), but implementation is the same as above.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Visuals
&lt;/h2&gt;

&lt;p&gt;I do believe it's obvious that micro-entries have to be separate from the rest of the content in one way or another, as having them mixed with the other content will either&lt;br&gt;
A. Create visual clutter.&lt;br&gt;
B. Make articles hard to find with the number of micro-entries or&lt;br&gt;
C. All of the above.&lt;/p&gt;

&lt;p&gt;Think of this as a "blog" vs. "professional content" or similar terms.&lt;/p&gt;

&lt;p&gt;Since we know a distinction HAS to be made, the question then is what is the best way to do it. Is it a separate page? A list similar to the article list? If so, should it be on the same page? If not, should I only display the latest entry? All entries? N entries?&lt;/p&gt;

&lt;p&gt;==This is the part I'm struggling with.== I will be exploring different methods and update this article accordingly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's Discuss
&lt;/h2&gt;

&lt;p&gt;What do you think about microblogging? Do you or would you utilize something like microblogging?&lt;/p&gt;

&lt;p&gt;If so, what do you think is the best way to do it correctly?&lt;/p&gt;
&lt;h1&gt;
  
  
  The Chosen Path
&lt;/h1&gt;

&lt;p&gt;After writing the article above, I continued thinking, testing different things, and working on implementing the microblogging functionality given the constraints and requirements that I have from the microblogging functionality into Saisho.&lt;/p&gt;
&lt;h2&gt;
  
  
  Source File
&lt;/h2&gt;

&lt;p&gt;I wanted to make sure the source file is humanly readable even when looking at it directly, so I've decided on using a plain TXT file in reversed order (latest entry is always at the top) that follows a very simple format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{RFC3339 Date}  {Unique ID} {Flags} {Entry text with MarkDown}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A sample entry would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2020-09-26T21:17:34Z    ed58b9 P Aside from the ID and the flags (still need to figure out if I *really* need those), the format is TWTXT compatible.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see, the format is &lt;a href="https://twtxt.readthedocs.io/en/latest/user/twtxtfile.html#format-specification"&gt;twtxt&lt;/a&gt; compatible (aside from the ID and flags).&lt;/p&gt;

&lt;p&gt;Flags are TBD as I've added them thinking I'll find use (for example P for Published, S for Secret, etc.) for them, but I'm not entirely sure yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Presentation
&lt;/h2&gt;

&lt;p&gt;I've decided to have an entirely separate page for the microblog to make sure people can actually get to it (the other option is having it below the main article list).&lt;/p&gt;

&lt;p&gt;A new 'special' page was made in Saisho to make this happen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'microblog'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;include_once&lt;/span&gt; &lt;span class="no"&gt;INC_DIR&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DS&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'microblog.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$mb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;microblog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'blog.txt'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$requestedPage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$mb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$requestedPage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$mb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'all'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;
        &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Microblog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'body'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$mb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;renderEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entries&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'microblog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This made me realize that creating 'special' pages in Saisho is less than comfortable, but this will be addressed at a later time.&lt;/p&gt;

&lt;p&gt;As you can see, I've made it so that you can link to specific entries in the microblog by using &lt;code&gt;/microblog?id={id-of-entry}&lt;/code&gt; or if you navigate to &lt;a href="https://dev.to/microblog"&gt;/microblog&lt;/a&gt; without an ID, you'll get all the entries.&lt;/p&gt;

&lt;p&gt;The microblog is using &lt;a href="https://gist.github.com/jbroadway/2836900"&gt;Slimdown&lt;/a&gt; as opposed to Parsedown which is a very simple and quick Markdown parser with a very limited subset of tags.&lt;/p&gt;

&lt;p&gt;The output on the microblog page is a simple ID (which gives you the date when you hover above it) and the parsed entry text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Posting
&lt;/h2&gt;

&lt;p&gt;Currently, adding new entries to the microblog can be done either by adding them directly to the source file (less than ideal since you need both the timestamp and ID) or by using a simple API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T9TUsDM---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/77f5l2sr8ac90rn234kx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T9TUsDM---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/77f5l2sr8ac90rn234kx.png" alt="O5AitXEKej-fs8"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The API currently only supports creating new entries, but I might modify it to support changing and removing entries as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Words
&lt;/h2&gt;

&lt;p&gt;This is still a learning experience for me and things may change depending on my requirements from this functionality. That being said, I am trying to keep it (and everything else in Saisho) simple and functional in a way that I will use it.&lt;/p&gt;

&lt;p&gt;The format might still change depending on if I can find a use for flags and I still need to revise and refine the API.&lt;/p&gt;

&lt;p&gt;The code will be pushed to both Sourcehut and GitHub once I verify that everything is working as it should.&lt;/p&gt;

&lt;p&gt;Got any ideas? Want to discuss this? I'd love to hear what you think.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>php</category>
      <category>microblog</category>
    </item>
    <item>
      <title>Image Optimization for The Web</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Wed, 09 Sep 2020 08:37:15 +0000</pubDate>
      <link>https://forem.com/hxii/image-optimization-for-the-web-3iai</link>
      <guid>https://forem.com/hxii/image-optimization-for-the-web-3iai</guid>
      <description>&lt;p&gt;In my &lt;a href="https://0xff.nu/speed-pt3#images"&gt;previous rant about web bloat&lt;/a&gt; I briefly spoke about image sizes and incorrect image usage and what you can do to either reduce it or make your visitors' life easier.&lt;br&gt;
A &lt;a href="https://merveilles.town/@FredBednarski/104821640863683259"&gt;specific toot&lt;/a&gt; prompted me to continue and expand about this with the hopes that this information will help some of you.&lt;/p&gt;
&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Images, like anything else, are to be use to answer a need or solve a problem&lt;sup id="fnref1"&gt;1&lt;/sup&gt; (like demonstrating something in a visual manner).&lt;/p&gt;

&lt;p&gt;If you don't absolutely need images - don't use them. If you &lt;em&gt;have&lt;/em&gt; to use images, make sure they are optimized.&lt;/p&gt;
&lt;h2&gt;
  
  
  Image Formats
&lt;/h2&gt;

&lt;p&gt;Many things have been said about image formats and I will most likely have nothing new to contribute, so instead I'll direct you to some useful reading material:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/using-webp-images/"&gt;Using WebP Images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://abraia.me/docs/image-optimization/"&gt;How to optimize images for web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.freecodecamp.org/news/best-image-format-for-web-in-2019-jpeg-webp-heic-avif-41ba0c1b2789/"&gt;Picture this: the best image format for the web in 2019&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Image Size
&lt;/h2&gt;

&lt;p&gt;Before I delve into fancy (and helpful) tricks that you can do to make sure your page load speeds are not inhibited by images, it is important to deal with the root of the problem - image size.&lt;/p&gt;

&lt;p&gt;As I mentioned in my previous rant (linked above), I often see websites usings images of disproportional sizes (both image dimension and filesize) to their intended purpose where people would, for example, use a large (1728x1152) profile photo and resize it via CSS (to 30%, effectively 496x331 on a 1080P display):&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gPa_pLn0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://0xff.nu/i/Quarrelsome-e8b8d9b085ba00e3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gPa_pLn0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://0xff.nu/i/Quarrelsome-e8b8d9b085ba00e3.jpg" alt="Very large profile photo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm not going to rant (again) why you really (really) shouldn't do this, so we'll get straight to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimize.&lt;/strong&gt; Optimize your images. Deal with the dimensions first - if you know you're only going to use 30% of your image, resize it beforehand!&lt;br&gt;
Using smaller dimensions will also result in smaller filesizes.&lt;/p&gt;

&lt;p&gt;You think larger screens deserve larger images (not of your fucking face, please!)? Save multiple sizes and use &lt;a href="https://html.com/attributes/img-srcset/"&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You made your images smaller? Thank you! You want to go the extra mile? Use optimization tools, either online or offile. For example:&lt;/p&gt;
&lt;h3&gt;
  
  
  Online Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://tinypng.com/"&gt;tinypng&lt;/a&gt; and &lt;a href="https://tinyjpg.com/"&gt;tinyjpg&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://compressor.io/"&gt;compressor.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ditherit.com/"&gt;Dither it!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jpeg.io/"&gt;jpeg.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Offline Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://x128.ho.ua/color-quantizer.html"&gt;Color Quantizer (WIN)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://imageoptim.com/mac"&gt;ImageOptim (MAC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pngquant.org/#download"&gt;pngquant (Multi)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;small&gt;Know any other good tools? LMK.&lt;/small&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Lazy Loading
&lt;/h2&gt;

&lt;p&gt;Unless images are part of the layout, you should always be using lazy loading.&lt;/p&gt;

&lt;p&gt;Lazy loading will make the page load faster, as images that are offscreen will not be loaded until they are scrolled into view.&lt;/p&gt;

&lt;p&gt;All you have to do is add &lt;code&gt;loading="lazy"&lt;/code&gt; as an attribute to your &lt;code&gt;img&lt;/code&gt; tags, e.g.:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;img src="path-to-img.jpg" alt="Image description" loading="lazy"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can read more on CSS-Tricks about &lt;a href="https://css-tricks.com/native-lazy-loading/"&gt;Native Lazy Loading&lt;/a&gt; and &lt;a href="https://css-tricks.com/the-complete-guide-to-lazy-loading-images/"&gt;The Complete Guide to Lazy Loading Images&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Going the extra step
&lt;/h2&gt;

&lt;p&gt;Your images have been smallified and are lazy, but you want to do a bit more, huh?&lt;br&gt;
Another thing you can do is make sure images are only loading once a user triggers them.&lt;/p&gt;
&lt;h3&gt;
  
  
  The JS route
&lt;/h3&gt;

&lt;p&gt;This is a technique I employ here, on &lt;a href="https://0xff.nu"&gt;my website&lt;/a&gt;, and it consists of setting a placeholder as the image source and the actual image under a &lt;code&gt;data-src&lt;/code&gt; attribute, creating a listener and switching the sources once the user clicks on the image.&lt;/p&gt;

&lt;p&gt;Image example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;img src="/load.png" data-src="https://0xff.nu/i/Tubby-ae0ddc24569cabab.png" alt="Holy fucking unreadable font, Batman!" loading="lazy"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The JS code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/javascript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img[data-src]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  The CSS route
&lt;/h3&gt;

&lt;p&gt;If you don't want to use JavaScript (I wouldn't blame you), you can achieve a similar effect using HTML and CSS. You can see a little demo I wrote on &lt;a href="https://codepen.io/nocte/pen/wvGpOyE"&gt;Codepen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The gist is to place the image offscreen or hide it using a CSS rule and use a checkbox element to switch the styling once it's checked.&lt;/p&gt;

&lt;p&gt;For example, the HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fig"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"loadimage"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"loadimage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"loadimage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Load Image&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://images.unsplash.com/photo-1568431477192-52bb13a55088"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A cute hedgehog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.fig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inline&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.fig&lt;/span&gt; &lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;underline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.fig&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.fig&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-9999px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.fig&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This might be a bit less straightforward and more cumbersome solution, but it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  In conclusion
&lt;/h3&gt;

&lt;p&gt;You don't have to do this, and you might argue how this is kind of fucking annoying in terms of UX, especially if you are using a lot of images, and you'd be right and this is exactly where you need to excercise common sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;Images are only part of the web bloat, but they could be one of the easier things to fix. Everything depends on you and how much effort and thought you're willing to put into this.&lt;/p&gt;

&lt;p&gt;The important thing is that we acknowledge the problem and take steps towards fixing it, rather than contributing to it.&lt;/p&gt;

&lt;p&gt;Let's make the fucking &lt;br&gt;
web nice again, shall we?&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Part of my, I guess, way of minimalism, is that when writing code, if something doesn't solve a problem, answer a need or makes someone's life easier it's most likely not required. This is true for many things, images included. I only reserve images for things I cannot show using text. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>images</category>
    </item>
    <item>
      <title>Pursuing Speed, Pt. 3</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Sun, 16 Aug 2020 12:42:08 +0000</pubDate>
      <link>https://forem.com/hxii/pursuing-speed-pt-3-2o94</link>
      <guid>https://forem.com/hxii/pursuing-speed-pt-3-2o94</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This entry (and collection of links) is a continuation of &lt;a href="https://0xff.nu/speed-pt2"&gt;Pursuing Speed, Pt. 2&lt;/a&gt; which you can read for context.&lt;/p&gt;

&lt;p&gt;You are more than welcome to share the link to this with your friends and colleagues.&lt;br&gt;
If you'd like to contribute relevant points of interesting links or resources, &lt;del&gt;please contact me using the form at the bottom of the page.&lt;/del&gt; Since this is dev.to, please leave a comment and I'll update the post with the links.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point, you might be going "This dude can only rant! Provide some alternatives, then!" and, in a sense, you'd have a point, since all I did so far is rant and only briefly talk (or rather mention) the "correct" way to go about things.&lt;/p&gt;

&lt;p&gt;So it's time to remedy this. In this post, I will try to provide tips and rough guidelines as well as links to further reading which will help you in making your website faster and more accessible.&lt;/p&gt;

&lt;p&gt;As I've said before, speed, lightness, mindfullness and minimalism do not neccessarily mean that your website must or will look as minimal (and some would say barren) as mine, not at all! You can have colors, images and whatever fucking flashy, hip things you want, but you MUST be mindful with everything that you add. Dont use jQuery just to do a fucking POST call and don't use Bootstrap just because you're fucking lazy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The least you can do
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Base
&lt;/h3&gt;

&lt;p&gt;In order for your website to be quick, it needs to have a solid, lightweight base. To ensure this, &lt;em&gt;my&lt;/em&gt; solution was to create &lt;a href="https://0xff.nu/why-saisho"&gt;my own website engine&lt;/a&gt; that will fit &lt;em&gt;my needs&lt;/em&gt;. You, of course, don't have to do this, but you do have to take your base into account as it's probably the most important aspect.&lt;/p&gt;

&lt;p&gt;WordPress is bloated&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. You need a plugin for even the most basic things which make the platform even heavier. This is not to say that WordPress is bad per se, but it is more often than not an overkill solution just to handle your blog or journal.&lt;/p&gt;

&lt;p&gt;If you can afford it, use a static site builder like &lt;a href="https://www.11ty.dev/"&gt;11ty&lt;/a&gt; or literally anything from &lt;a href="https://www.staticgen.com/"&gt;StaticGen&lt;/a&gt;. If you need a CMS, use lighter solutions like &lt;a href="https://www.bludit.com/"&gt;Bludit&lt;/a&gt;, &lt;a href="https://getgrav.org/"&gt;Grav&lt;/a&gt; or &lt;a href="https://automad.org/"&gt;Automad&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;p&gt;Making your website more accessible to different kinds of people probably was not in your list of considerations, but you can now help change this. There are tons of small things you can do which will ensure a lot more people can read your website comfortably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Animations&lt;/strong&gt; - Want to use animations? Great! But please note not everybody likes (or can stomach) this fucking trash:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Af0n6CcE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qtnuu6nf0axu3j7iffql.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Af0n6CcE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qtnuu6nf0axu3j7iffql.gif" alt="Pointless animation you can't turn off"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Animations, at worst, need to compliment your content and &lt;strong&gt;enhance&lt;/strong&gt; the user experience, and at best not to be used at all.&lt;br&gt;
Still want to use them (in moderation, of course)? Sure, BUT also use the &lt;code&gt;prefers-reduced-motion&lt;/code&gt; &lt;sup id="fnref2"&gt;2&lt;/sup&gt; media query. This way, those who do not wan't to see animations will not be forced to do so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Colors&lt;/strong&gt; - Use tools like &lt;a href="https://color.a11y.com/"&gt;this&lt;/a&gt; to make sure the colors you are using are contrasty enough.&lt;br&gt;
While some say dark mode (or night mode) is not important, it is becoming way more requested and needed. Besides, it is now easier than ever to implement using the &lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;sup id="fnref3"&gt;3&lt;/sup&gt; media query, so why not add it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fonts&lt;/strong&gt; - Avoid webfonts if you can. The system stack is usable enough for most people. If you are using webfonts, don't use unreadable shit like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4K1c-7AR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://0xff.nu/i/Tubby-ae0ddc24569cabab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4K1c-7AR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://0xff.nu/i/Tubby-ae0ddc24569cabab.png" alt="Unreadable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Relevant reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://alistapart.com/article/designing-safer-web-animation-for-motion-sensitivity/"&gt;Designing Safer Web Animation For Motion Sensitivity&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://css-tricks.com/introduction-reduced-motion-media-query/"&gt;An Introduction to the Reduced Motion Media Query&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/hackernoon/web-fonts-when-you-need-them-when-you-dont-a3b4b39fe0ae"&gt;Web fonts: when you need them, when you dont&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Frameworks
&lt;/h3&gt;

&lt;p&gt;Want to use a framework? Don't. &lt;strong&gt;Absolutely have&lt;/strong&gt; to use CSS &amp;amp; JS frameworks? Use them wisely.&lt;br&gt;
How does, for example, loading 500-fucking-kilobytes for 5 social icons makes any sense?&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F3W7NwxP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://0xff.nu/i/Secondary-d154d301072c74d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F3W7NwxP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://0xff.nu/i/Secondary-d154d301072c74d3.png" alt="this is 500kb"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Write your own CSS according to your/your website's needs. Only add classes you're going to use. Optimize.&lt;/p&gt;

&lt;p&gt;If you're only using jQuery (31kb) to do ONE thing, I'm sure as hell you can manage to do it using vanilla JS. Need a small framework to make things easier? Check out &lt;a href="https://github.com/fabiospampinato/cash"&gt;Cash&lt;/a&gt; or &lt;a href="https://umbrellajs.com/"&gt;UmbrellaJS&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Images
&lt;/h3&gt;

&lt;p&gt;Images are there to compliment the rest of your content. Unless I'm looking at your portfolio of visual arts, don't make me download a megabyte photo of your mug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimize&lt;/strong&gt; - there's no fucking reason for you to force me to load unnecessary data, for example the illustration &lt;a href="https://merichard123.github.io/"&gt;here&lt;/a&gt; provides no additional value yet weighs 26% of the total page size.&lt;br&gt;
FFS the same image in WEBP is 132kB, which is a 31.6% reduction in size and it only took me 5 fucking seconds to do this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resize&lt;/strong&gt; - The profile image &lt;a href="http://www.pierrealexandreholliger.fr/"&gt;here&lt;/a&gt; for example is 1728x1152 pixels yet you're limiting it to 30% in CSS, thus making your visitors download more data than is actually required. WHY? 5 fucking seconds to resize it to 600x400 and optimize and it's a miniscule 17.9 kB instead of 95.1kB, and all it took is some caring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lazy loading&lt;/strong&gt; - Utilize &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading"&gt;lazy-loading&lt;/a&gt; on all the images that you use. It costs you nothing, yet helps a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else can I do?
&lt;/h2&gt;

&lt;p&gt;This all depends on how much you care. If you care about your website, and you actually put some time in crafting it rather than throwing a bunch of crap together - it will look and perform great.&lt;br&gt;
Make sure to check out &lt;a href="https://www.nginx.com/blog/nginx-caching-guide/"&gt;client-side caching&lt;/a&gt; (if your HTML won't change, you could try caching it as well), preload/prefetch&lt;sup id="fnref4"&gt;4&lt;/sup&gt; resources, minification, pre-compressing your resources, read what &lt;a href="https://daverupert.com/2020/03/maintaining-performance/"&gt;others&lt;/a&gt; &lt;a href="https://www.kizu.ru/a-bit-of-performance/"&gt;have done&lt;/a&gt;, and try to stay on top of whats new in web technologies.&lt;/p&gt;

&lt;p&gt;The main point I'm trying to get across is this - Experiment to your heart's content, but please give a shit when it comes to a publicly available website, your visitors are not your test subjects. They would rather read what you have to say than bask at the unholy abomination that is your fancy fucking React APP. &lt;/p&gt;

&lt;h3&gt;
  
  
  Further Reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://kokorobot.ca/site/leanerweb.html"&gt;Bandwidth Conservation Society&lt;/a&gt; by Rekka Bellum.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://web.dev/content-visibility/"&gt;&lt;code&gt;content-visibility&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://web.dev/service-worker-caching-and-http-caching/"&gt;Service worker caching and HTTP caching&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;This is not my opinion. This is a fact. Articles like &lt;a href="https://devdojo.com/ramadan-byalk/useful-code-snippets-for-wordpress"&gt;this&lt;/a&gt; exist because of how bloated WP is. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/html-preload-prefetch"&gt;https://www.digitalocean.com/community/tutorials/html-preload-prefetch&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Suteba - Simple Request Bin in PHP</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Mon, 27 Jul 2020 14:49:24 +0000</pubDate>
      <link>https://forem.com/hxii/suteba-simple-request-bin-in-php-2co9</link>
      <guid>https://forem.com/hxii/suteba-simple-request-bin-in-php-2co9</guid>
      <description>&lt;p&gt;My day job requires me to perform troubleshooting on customer issues, some of which may include trying to understand what might be wrong in an API request payload.&lt;/p&gt;

&lt;p&gt;While I could have used a service like &lt;a href="http://requestbin.net/"&gt;requestbin.net&lt;/a&gt;, it wouldn't be much of a learning experience, would it?&lt;/p&gt;

&lt;p&gt;The solution, then, was to make my own little tool that I could use to help myself investigate these issues, which I have also decided to share with you guys, in case you might find it useful.&lt;/p&gt;

&lt;p&gt;I present &lt;em&gt;Suteba&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple and light. No trackers. No bullshit.&lt;/li&gt;
&lt;li&gt;You own all the data.&lt;/li&gt;
&lt;li&gt;Multiple bins/buckets/dumps/whatever.&lt;/li&gt;
&lt;li&gt;Listing feature of requests per bin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f7p__VoD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e62kighi7qgcmcinwoqi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f7p__VoD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e62kighi7qgcmcinwoqi.jpg" alt="Request list"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vi38P2ai--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y9f1a4f43rpktr5pj46o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vi38P2ai--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y9f1a4f43rpktr5pj46o.jpg" alt="Single Request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Features are sparse to make Suteba simple and light. You're more than welcome to suggest additions, but please keep the simplicity in mind.&lt;/p&gt;

&lt;p&gt;Available on&lt;br&gt;
&lt;a href="https://git.sr.ht/~hxii/suteba"&gt;SourceHut&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/hxii/suteba"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>php</category>
    </item>
    <item>
      <title>Pursuing Speed</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Mon, 20 Jul 2020 16:26:02 +0000</pubDate>
      <link>https://forem.com/hxii/pursuing-speed-oph</link>
      <guid>https://forem.com/hxii/pursuing-speed-oph</guid>
      <description>&lt;h2&gt;
  
  
  Originally posted on &lt;a href="https://0xff.nu/speed"&gt;my website&lt;/a&gt; which is also a demo.
&lt;/h2&gt;

&lt;p&gt;Continuing my rambling about &lt;a href="https://0xff.nu/pursuing-minimalism"&gt;pursuing minimalism&lt;/a&gt;, I'd like to also say a few words about speed having "finished" rewriting my own website engine, once again.&lt;/p&gt;

&lt;p&gt;Minimalism and speed (at least when it comes to the web) became a sort of obsession of mine, and I always (try to) stay up to date about any new developments in web development, optimization, caching and other ways of improving speed.&lt;/p&gt;

&lt;p&gt;Sure, WordPress can be made fast-er (&lt;a href="https://darryldias.me/2015/lightweight-wordpress-setup/"&gt;leaving&lt;/a&gt; &lt;a href="https://copyblogger.com/make-wordpress-faster/"&gt;ironic&lt;/a&gt; &lt;a href="https://www.sparringmind.com/speed-up-wordpress/"&gt;snake&lt;/a&gt; &lt;a href="https://www.collectiveray.com/speed-up-wordpress"&gt;oil&lt;/a&gt; &lt;a href="https://websitesetup.org/how-to-speed-up-wordpress/"&gt;aside&lt;/a&gt;), but once your base is bloated&lt;sup id="fnref1"&gt;1&lt;/sup&gt; you will never stop optimizing the behemoth by either removing built-in functionality&lt;sup id="fnref2"&gt;2&lt;/sup&gt;, installing more plugins e.g. cache, finding clean themes or a combination of the above.&lt;/p&gt;

&lt;p&gt;Why, then, not start from a clean base? That is exactly why more and more people nowadays are turning to WordPress alternatives such as &lt;a href="https://www.bludit.com/"&gt;Bludit&lt;/a&gt;, &lt;a href="https://ghost.org/"&gt;Ghost&lt;/a&gt;, &lt;a href="https://statamic.com/"&gt;Statamic&lt;/a&gt; and others as well as static website builders such as &lt;a href="https://www.11ty.dev/"&gt;11ty&lt;/a&gt;, &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; and &lt;a href="https://www.gatsbyjs.org/"&gt;Gatsby&lt;/a&gt; in which it is easier to control what is being loaded and served.&lt;/p&gt;

&lt;p&gt;You can of course take it one step further, if you're really inclined towards speed and controlling your own data and delivery. More on this later.&lt;/p&gt;

&lt;h2&gt;
  
  
  State of the web
&lt;/h2&gt;

&lt;p&gt;The thing is, there is, in my opinion a general disregard towards website sizes and loading speeds: huge fucking CSS frameworks, unoptimized images, unnecessary JS libraries and the list goes on.&lt;/p&gt;

&lt;p&gt;I recently found instant.page&lt;sup id="fnref3"&gt;3&lt;/sup&gt; and was immediately interested in the type of websites that might be using it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://resume.benc.io/"&gt;Here&lt;/a&gt; we have someone's resume that is just one page, but it loads 7 JS files, 1.7kb of CSS and 30kb of fonts. All for &lt;strong&gt;one page&lt;/strong&gt;, to a total of 100kb. At least the damn SVGs are inline instead of loading fucking FontAwesome.&lt;/p&gt;

&lt;p&gt;Or &lt;a href="https://onemin-impact.github.io/"&gt;this&lt;/a&gt; placeholder for someone's blog. How bad is it, you ask? 288kb transferred with no meaningful information presented.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0O12j0l4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.0xff.nu/Sick-b3061016e3697e0e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0O12j0l4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.0xff.nu/Sick-b3061016e3697e0e.jpg" alt="288kb for this? Really?" title="288kb for this? Really?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things are bad, but they don't have to be, as soon as people start caring without just throwing a bunch of shit together and calling it a website.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did I do?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Preface&lt;/strong&gt;: What I did was, in a sense, take things to the extreme. I do not think or am saying this level of minimalism is good for &lt;em&gt;everyone&lt;/em&gt;, but you can take inspiration and ideas from what I did and implement them in your platform of choice.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The base - Instead of using a ready and working platform I decided to write &lt;a href="https://paulglushak.com/saisho"&gt;my own&lt;/a&gt;. This allows me to control &lt;strong&gt;everything&lt;/strong&gt; without relying on third parties when it comes to features.
CSS was written by hand and only the necessary blocks are present.
This may not be for everyone, as for better or worse, making it work is up to you (so is security).&lt;/li&gt;
&lt;li&gt;The bloat - There is none. I am using nothing and loading nothing besides the necessities and the page content. No CSS frameworks, no JS libraries (sans instant.page&lt;sup id="fnref3"&gt;3&lt;/sup&gt;). Nothing.&lt;/li&gt;
&lt;li&gt;The speed - A couple of speed optimizations were done. Some of them may be placebo.

&lt;ul&gt;
&lt;li&gt;nginx serves everything with Brotli compression enabled.&lt;/li&gt;
&lt;li&gt;Pages are processed with PHP and the output buffer response is cached as HTML for 3600 seconds.&lt;/li&gt;
&lt;li&gt;Pages are cached in browser using:
&lt;code&gt;header('cache-control:public, max-age='.CACHE_TIME.', immutable');&lt;/code&gt;
Memcached was also considered.&lt;/li&gt;
&lt;li&gt;Last Modified header is also sent:
&lt;code&gt;header('Last-Modified:' . gmdate('D, d M Y H:i:s ', @filemtime($filePath)) . 'GMT');&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Links are prefetched once hovered using &lt;a href="https://github.com/instantpage/instant.page"&gt;instant.page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Images are optimized, dithered and stored locally.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All these allow pages on Saisho&lt;sup id="fnref4"&gt;4&lt;/sup&gt; to be loaded almost instantaneously, something I am really proud of.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can you do?
&lt;/h2&gt;

&lt;p&gt;Start caring.&lt;/p&gt;

&lt;p&gt;What you do from there is up to you - optimize WordPress to no end, use static website generators, write your custom engine, write static HTML websites or whatever else works for you.&lt;/p&gt;

&lt;p&gt;But care about what you're doing and take into account the people who will be visiting your website and their time.&lt;/p&gt;

&lt;p&gt;*[SVG]: Scalable Vector Graphics&lt;/p&gt;

&lt;p&gt;*[CSS]: Cascading Style Sheets&lt;/p&gt;

&lt;p&gt;*[HTML]: HyperText Markup Language&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Rightfully so as WordPress is a platform, not a tailor-made solution, but more often than not it's overkill for the average folk. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Functionality here is debatable. jQuery for example is loaded &lt;a href="https://wordpress.stackexchange.com/a/91734"&gt;all the time&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;instant.page: &lt;a href="https://github.com/instantpage/instant.page"&gt;https://github.com/instantpage/instant.page&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://paulglushak.com/saisho"&gt;Saisho&lt;/a&gt; is my semi-static website engine which you are seeing now. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>rant</category>
      <category>css</category>
      <category>performance</category>
    </item>
    <item>
      <title>Saishō — Very simple semi-static site engine</title>
      <dc:creator>Paul (hxii) Glushak</dc:creator>
      <pubDate>Sun, 17 May 2020 07:57:28 +0000</pubDate>
      <link>https://forem.com/hxii/saisho-very-simple-semi-static-site-engine-544a</link>
      <guid>https://forem.com/hxii/saisho-very-simple-semi-static-site-engine-544a</guid>
      <description>&lt;p&gt;After I made &lt;a href="https://paulglushak.com/u"&gt;micro&lt;/a&gt; (a minimalist theme for Bludit) and having recently acquired a bunch of spare time thanks to getting fired, I decided to challenge myself to create something I can actually &lt;a href="https://0xff.nu/"&gt;use&lt;/a&gt; and learn something in the process. I decided this is something I can share with you as well.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;最小 (Saishō)&lt;/em&gt; is a very simple semi-static site engine built for speed and simplicity in PHP. It reads all MD files in a folder, parses them, and serves the cached variant.&lt;br&gt;
It was made for those of you that require no bells nor whistles, but something that will allow you (and your visitors) to focus on your content.&lt;/p&gt;

&lt;p&gt;Feedback is &lt;strong&gt;more than welcome&lt;/strong&gt;. This is a learning experience. Do keep in mind that the entire point of &lt;em&gt;Saishō&lt;/em&gt; is to be quick and simple.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sho.glushak.net/"&gt;Demo&lt;/a&gt;&lt;br&gt;
&lt;a href="https://sho.glushak.net/guide"&gt;Guide&lt;/a&gt;&lt;br&gt;
&lt;a href="https://git.sr.ht/~hxii/saisho"&gt;Sourcehut&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/hxii/saisho"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>php</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
