<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Stut.dev</title><link>https://stut.dev/</link><description>Recent content on Stut.dev</description><generator>Hugo</generator><language>en-gb</language><lastBuildDate>Tue, 06 Oct 2026 00:00:00 +0100</lastBuildDate><atom:link href="https://stut.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>narc: GitHub Action Runners on Nomad</title><link>https://stut.dev/narc</link><pubDate>Tue, 06 Oct 2026 00:00:00 +0100</pubDate><guid>https://stut.dev/narc</guid><description>&lt;p&gt;I ran out of GitHub Actions minutes. Public repositories get hosted runners for free, but private ones draw from a monthly allowance, and between the &lt;a href="https://stut.dev/my-server-setup-2026"&gt;platform&lt;/a&gt; and a handful of projects I was burning through it well before the month was out. The obvious fix was to pay for more. The less obvious fix was sitting right there: a Nomad cluster that spends most of its time with CPU and memory to spare.&lt;/p&gt;</description></item><item><title>My Server Setup, 2026 Edition</title><link>https://stut.dev/my-server-setup-2026</link><pubDate>Fri, 21 Aug 2026 08:00:00 +0100</pubDate><guid>https://stut.dev/my-server-setup-2026</guid><description>&lt;p&gt;Back in 2023 I wrote &lt;a href="https://stut.dev/my-server-setup-part-1"&gt;a five-part series&lt;/a&gt; describing the infrastructure that hosts my websites and projects. Almost none of it is true any more. The workloads are mostly the same; nearly everything underneath them has been replaced. This is the update.&lt;/p&gt;
&lt;h2 id="then-and-now"&gt;Then and now&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;2023&lt;/th&gt;
					&lt;th&gt;2026&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Hosting&lt;/td&gt;
					&lt;td&gt;3 Scaleway VPSes&lt;/td&gt;
					&lt;td&gt;4 Hetzner Cloud VPSes + a home cluster&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;OS&lt;/td&gt;
					&lt;td&gt;Ubuntu 22.04&lt;/td&gt;
					&lt;td&gt;NixOS&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Config management&lt;/td&gt;
					&lt;td&gt;Ansible + Terraform&lt;/td&gt;
					&lt;td&gt;Nix flakes (the whole machine is the repo)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Clusters&lt;/td&gt;
					&lt;td&gt;One&lt;/td&gt;
					&lt;td&gt;Two, fully independent (cloud and home)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Secrets&lt;/td&gt;
					&lt;td&gt;Vault&lt;/td&gt;
					&lt;td&gt;sops-nix&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Deployment&lt;/td&gt;
					&lt;td&gt;DockerHub webhooks + Noman&lt;/td&gt;
					&lt;td&gt;A home-grown GitOps reconciler (cd)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Admin access&lt;/td&gt;
					&lt;td&gt;SSH bastion&lt;/td&gt;
					&lt;td&gt;Tailscale&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Certificates&lt;/td&gt;
					&lt;td&gt;Aleff + Let&amp;rsquo;s Encrypt&lt;/td&gt;
					&lt;td&gt;cloudflared (TLS terminates at Cloudflare)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Static sites&lt;/td&gt;
					&lt;td&gt;Docker containers in the cluster&lt;/td&gt;
					&lt;td&gt;Mostly Cloudflare Pages&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The scheduler survived: it&amp;rsquo;s still Consul and Nomad, and I still think that&amp;rsquo;s the right amount of orchestration for this scale. Everything else went.&lt;/p&gt;</description></item><item><title>My Claude Code Skills</title><link>https://stut.dev/my-claude-code-skills</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0100</pubDate><guid>https://stut.dev/my-claude-code-skills</guid><description>&lt;p&gt;I&amp;rsquo;ve published &lt;a href="https://github.com/stut/skills"&gt;a small collection of Claude Code skills&lt;/a&gt; as a plugin. There are two so far: one that runs a technical interview against you and then tells you the truth about how it went, and one that walks you through configuring a GitHub repository properly.&lt;/p&gt;
&lt;p&gt;Neither of these is technically impressive. Both exist because of the same observation: there are things I know I &lt;em&gt;should&lt;/em&gt; do, and things I know would be &lt;em&gt;good for me&lt;/em&gt;, and in both cases the friction of getting started is what stops me. Skills turn out to be a nice way of packaging up &amp;ldquo;the version of me that has already thought about this&amp;rdquo; so that future me can just invoke it.&lt;/p&gt;</description></item><item><title>imgsrv: Serving images the lazy way</title><link>https://stut.dev/serving-images-the-lazy-way</link><pubDate>Tue, 21 Jul 2026 00:00:00 +0100</pubDate><guid>https://stut.dev/serving-images-the-lazy-way</guid><description>&lt;p&gt;&lt;a href="https://github.com/stut/imgsrv"&gt;imgsrv&lt;/a&gt; is a small open source Go service that resizes and transcodes images on demand, sitting behind nginx. There&amp;rsquo;s nothing revolutionary about the idea: nginx serves everything it can straight from disk, and only a cache miss ever reaches the Go service. When that happens it generates the requested version of the image, writes it to disk, and returns it. Every request after that is a plain static file serve that never touches the Go code at all.&lt;/p&gt;</description></item><item><title>Introducing Wakuwi</title><link>https://stut.dev/introducing-wakuwi</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0100</pubDate><guid>https://stut.dev/introducing-wakuwi</guid><description>&lt;p&gt;&lt;a href="https://github.com/stut/wakuwi"&gt;Wakuwi&lt;/a&gt; is an open source, lightweight, read-only web interface for exploring Kubernetes clusters. It runs locally, uses the contexts already present in your kubeconfig, and requires no additional configuration. Install it, run it, and point your browser at localhost. It can also be run in your Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;The name stands for:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;W&lt;/strong&gt;hy &lt;strong&gt;A&lt;/strong&gt;nother &lt;strong&gt;KU&lt;/strong&gt;bernetes &lt;strong&gt;W&lt;/strong&gt;eb &lt;strong&gt;I&lt;/strong&gt;nterface?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which is a perfectly reasonable question.&lt;/p&gt;
&lt;h2 id="it-started-with-port-forwarding"&gt;It started with port forwarding&lt;/h2&gt;
&lt;p&gt;Wakuwi did not begin as an attempt to build a general Kubernetes interface.&lt;/p&gt;</description></item><item><title>My Server Setup Part 5: Eliminating the Downtime</title><link>https://stut.dev/my-server-setup-part-5</link><pubDate>Sat, 11 Feb 2023 00:00:00 +0000</pubDate><guid>https://stut.dev/my-server-setup-part-5</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This series is now very out of date; a lot has changed since it was written back in 2023. Read the update: &lt;a href="https://stut.dev/my-server-setup-2026"&gt;My Server Setup, 2026 Edition&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this series of posts I am describing the infrastructure that hosts all of my websites and development projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-1"&gt;Part 1: Hosting and Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-2"&gt;Part 2: Nomad Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-3"&gt;Part 3: Nomad Jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-4"&gt;Part 4: Continuous Delivery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-5"&gt;Part 5: Eliminating the Downtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;In the Outstanding Issues section of part 4 I noted that the update mechanism results in a few seconds of downtime for a site when it&amp;rsquo;s updated. This is clearly unacceptable.&lt;/li&gt;
&lt;li&gt;The resolution for this was to separate out the web server from the content.&lt;/li&gt;
&lt;li&gt;Docker images are no longer built containing the site content.&lt;/li&gt;
&lt;li&gt;Instead a sidecar service clones and periodically pulls the repo into a directory on disk shared by the sidecar and the web server containers.&lt;/li&gt;
&lt;li&gt;Downtime has been almost completely eliminated (miniscule window per file updated).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-nomad-enables-this-solution"&gt;How Nomad enables this solution&lt;/h2&gt;
&lt;p&gt;When a Docker-based Nomad job is started a number of directories are created and auto-mounted into the containers. One of these is shared across all tasks in a group, known as the &lt;code&gt;alloc&lt;/code&gt; directory. This directory is what enabled this solution to work.&lt;/p&gt;</description></item><item><title>My Server Setup Part 4: Continuous Delivery</title><link>https://stut.dev/my-server-setup-part-4</link><pubDate>Wed, 04 Jan 2023 00:00:00 +0000</pubDate><guid>https://stut.dev/my-server-setup-part-4</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This series is now very out of date; a lot has changed since it was written back in 2023. Read the update: &lt;a href="https://stut.dev/my-server-setup-2026"&gt;My Server Setup, 2026 Edition&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this series of posts I am describing the infrastructure that hosts all of my websites and development projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-1"&gt;Part 1: Hosting and Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-2"&gt;Part 2: Nomad Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-3"&gt;Part 3: Nomad Jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-4"&gt;Part 4: Continuous Delivery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-5"&gt;Part 5: Eliminating the Downtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;All services exist in their own GitHub repository.&lt;/li&gt;
&lt;li&gt;Each repository contains a Dockerfile in the root that will build the Docker image.&lt;/li&gt;
&lt;li&gt;Each one has a GitHub action that&amp;rsquo;s triggered by merges to master and will tag the repo with the current date and time.&lt;/li&gt;
&lt;li&gt;Each repository on GitHub has a matching repository on DockerHub.&lt;/li&gt;
&lt;li&gt;The DockerHub repository watches for new tags and will trigger a build.&lt;/li&gt;
&lt;li&gt;A webhook will inform Noman of a new image being available.&lt;/li&gt;
&lt;li&gt;Noman will update the image tag in Consul which will trigger Nomad to update the job.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="github"&gt;GitHub&lt;/h2&gt;
&lt;p&gt;This is where I&amp;rsquo;ve chosen to keep the source for my projects. Each service has its own repository, and the common feature is a Dockerfile in the root that will build the deployable image. No other build steps can be required, everything needs to happen in the Dockerfile for this process to work.&lt;/p&gt;</description></item><item><title>My Server Setup Part 3: Nomad Jobs</title><link>https://stut.dev/my-server-setup-part-3</link><pubDate>Tue, 03 Jan 2023 00:00:00 +0000</pubDate><guid>https://stut.dev/my-server-setup-part-3</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This series is now very out of date; a lot has changed since it was written back in 2023. Read the update: &lt;a href="https://stut.dev/my-server-setup-2026"&gt;My Server Setup, 2026 Edition&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this series of posts I am describing the infrastructure that hosts all of my websites and development projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-1"&gt;Part 1: Hosting and Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-2"&gt;Part 2: Nomad Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-3"&gt;Part 3: Nomad Jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-4"&gt;Part 4: Continuous Delivery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-5"&gt;Part 5: Eliminating the Downtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;All services I currently run are deployed as &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; containers.&lt;/li&gt;
&lt;li&gt;Various static HTTP sites based on plain HTML, &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;, &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt;, and custom Python generators.&lt;/li&gt;
&lt;li&gt;A number of development projects running everything from &lt;a href="https://go.dev/"&gt;Go&lt;/a&gt; to &lt;a href="https://www.php.net/"&gt;PHP&lt;/a&gt; to &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Supporting services: &lt;a href="https://fabiolb.net/"&gt;Fabiolb&lt;/a&gt;, &lt;a href="https://aleff.dev/"&gt;Aleff&lt;/a&gt;, &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt;, &lt;a href="https://prometheus.io/docs/alerting/latest/alertmanager/"&gt;Alertmanager&lt;/a&gt;, Noman (custom component).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="docker-containers"&gt;Docker containers&lt;/h2&gt;
&lt;p&gt;Nomad supports deploying a wide range of packages, from raw binaries through Docker containers to Java jars. For my pusposes I&amp;rsquo;ve only needed to use Docker container so far, but it&amp;rsquo;s a comfort knowing there are other options available in the ecosystem I&amp;rsquo;m using.&lt;/p&gt;</description></item><item><title>My Server Setup Part 2: Nomad</title><link>https://stut.dev/my-server-setup-part-2</link><pubDate>Mon, 02 Jan 2023 00:00:00 +0000</pubDate><guid>https://stut.dev/my-server-setup-part-2</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This series is now very out of date; a lot has changed since it was written back in 2023. Read the update: &lt;a href="https://stut.dev/my-server-setup-2026"&gt;My Server Setup, 2026 Edition&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this series of posts I am describing the infrastructure that hosts all of my websites and development projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-1"&gt;Part 1: Hosting and Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-2"&gt;Part 2: Nomad Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-3"&gt;Part 3: Nomad Jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-4"&gt;Part 4: Continuous Delivery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-5"&gt;Part 5: Eliminating the Downtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Each server runs &lt;a href="https://www.nomadproject.io"&gt;Nomad&lt;/a&gt; in both client and server modes.&lt;/li&gt;
&lt;li&gt;Each server also runs instances of &lt;a href="https://www.consul.io"&gt;Consul&lt;/a&gt; and &lt;a href="https://www.vaultproject.io"&gt;Vault&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fabiolb.net"&gt;Fabiolb&lt;/a&gt; is used for external proxying and load balancing.&lt;/li&gt;
&lt;li&gt;A custom, open-sourced tool called &lt;a href="https://aleff.dev"&gt;Aleff&lt;/a&gt; is used to automatically obtain and renew certificates from &lt;a href="https://letsencrypt.org"&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="nomad"&gt;Nomad&lt;/h2&gt;
&lt;p&gt;As mentioned in the previous post I was using Kubernetes for a while, but since the company I work for has standardised on Nomad I&amp;rsquo;ve switched my personal setup so I can learn more about it and the whole Hashicorp stack. The experience was less than smooth but after a few false starts I now have a stable cluster and everything is working as well as it did with Kubernetes.&lt;/p&gt;</description></item><item><title>My Server Setup Part 1: Hosting and Configuration</title><link>https://stut.dev/my-server-setup-part-1</link><pubDate>Sun, 01 Jan 2023 00:00:00 +0000</pubDate><guid>https://stut.dev/my-server-setup-part-1</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This series is now very out of date; a lot has changed since it was written back in 2023. Read the update: &lt;a href="https://stut.dev/my-server-setup-2026"&gt;My Server Setup, 2026 Edition&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this series of posts I will describe the infrastructure that hosts all of my websites and development projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-1"&gt;Part 1: Hosting and Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-2"&gt;Part 2: Nomad Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-3"&gt;Part 3: Nomad Jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-4"&gt;Part 4: Continuous Delivery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stut.dev/my-server-setup-part-5"&gt;Part 5: Eliminating the Downtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Hosted at &lt;a href="https://www.scaleway.com/"&gt;Scaleway&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;3 cheap development-grade virtual servers.&lt;/li&gt;
&lt;li&gt;Running &lt;a href="https://ubuntu.com/"&gt;Ubuntu 22.04.1 LTS&lt;/a&gt;, &lt;a href="https://www.nomadproject.io"&gt;Nomad&lt;/a&gt;, &lt;a href="https://www.consul.io"&gt;Consul&lt;/a&gt;, and &lt;a href="https://www.vaultproject.io"&gt;Vault&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Server-to-server communication over a host-provided private network.&lt;/li&gt;
&lt;li&gt;Server configuration managed with &lt;a href="https://www.ansible.com"&gt;Ansible&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Nomad job configuration is managed with &lt;a href="https://www.terraform.io"&gt;Terraform&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Only HTTP, and HTTPS are exposed on public IPs.&lt;/li&gt;
&lt;li&gt;Access is provided via SSH by a secure bastion server connected to the private network.&lt;/li&gt;
&lt;li&gt;DNS is provided by &lt;a href="https://gandi.net/"&gt;Gandi&lt;/a&gt;, and all domains have A records for all public IPs in the cluster.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="hosting"&gt;Hosting&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been running publicly available servers since 1998, including running my own hosting company for nearly 10 years, and the main thing I&amp;rsquo;ve learned is that nobody does it all well. In recent years I&amp;rsquo;ve used &lt;a href="https://aws.amazon.com"&gt;AWS&lt;/a&gt;, &lt;a href="https://www.digitalocean.com"&gt;DigitalOcean&lt;/a&gt;, &lt;a href="https://www.vultr.com"&gt;Vultr&lt;/a&gt;, and a number of other smaller hosts, and each one had strengths and weaknesses. I currently use Scaleway and I&amp;rsquo;m very happy with their services so far.&lt;/p&gt;</description></item><item><title>Template-based DNS zone management</title><link>https://stut.dev/template-based-dns-zone-management</link><pubDate>Mon, 03 Dec 2012 00:00:54 +0000</pubDate><guid>https://stut.dev/template-based-dns-zone-management</guid><description>&lt;p&gt;I own nearly 100 domain names, which is a pretty modest number against some people and organisations I know. Most of them are currently registered with &lt;a href="https://www.gandi.net/"&gt;Gandi.net&lt;/a&gt; and I’ve always found their service to be good value for money and that’s still true today.&lt;/p&gt;
&lt;p&gt;Most of the DNS for those domain names is also hosted with Gandi. Why? Because it’s included in the registration cost. However, their zone management tool, while it had a major overhaul in the last year which has made it a lot better, it’s still a major pain to use. Unfortunately this seems to be a common theme with DNS management interfaces.&lt;/p&gt;</description></item><item><title>Snippet: HTTP authentication in PHP</title><link>https://stut.dev/snippet-http-authentication-in-php</link><pubDate>Sun, 11 Nov 2012 00:00:32 +0000</pubDate><guid>https://stut.dev/snippet-http-authentication-in-php</guid><description>&lt;p&gt;HTTP authentication is the easiest way to make a page or area of a website secure. It’s very easy to accomplish with pure PHP, so no web server configuration is required making it a lot more portable.&lt;/p&gt;
&lt;p&gt;This function implements a very simple HTTP Basic Auth authentication system. Simply call it before you do anything else in your script, pass it an array of valid users (username =&amp;gt; password), an optional description of what’s being secured and it will do the rest.&lt;/p&gt;</description></item><item><title>Handling email notifications in PHP</title><link>https://stut.dev/handling-email-notifications-in-php</link><pubDate>Sat, 12 Nov 2011 00:00:49 +0000</pubDate><guid>https://stut.dev/handling-email-notifications-in-php</guid><description>&lt;p&gt;Twitter sends notifications of new followers and direct messages by email. While you can use the API to keep track of those things it wastes hits and it’s incredibly inefficient especially for accounts with minimal activity.&lt;/p&gt;
&lt;p&gt;I run several Twitter-based services but they all share the same email address. This allowed me to use the same email handling setup to process all email coming in from Twitter. So far it’s working really well but it means this script is a bit more involved than it needs to be.&lt;/p&gt;</description></item><item><title>Fortune's Tower in PHP</title><link>https://stut.dev/fortunes-tower-in-php</link><pubDate>Tue, 16 Aug 2011 00:00:08 +0100</pubDate><guid>https://stut.dev/fortunes-tower-in-php</guid><description>&lt;p&gt;When I get bored while working I choose something from a list of things I want to have a go at someday. Some things are “read this blog post”, or “learn about this technology”, but most are along the lines of “make this”. Today I picked a “make this” item… to implement the game Fortune’s Tower.&lt;/p&gt;
&lt;p&gt;I came across this game in the &lt;a href="http://en.wikipedia.org/wiki/Fable_II_Pub_Games"&gt;Fable® II Pub Games&lt;/a&gt; on my Xbox. I have never actually played Fable® II, and I’ve barely touched the other pub games in the Xbox Live Arcade Pub Games, err, game, but I keep returning to Fortune’s Tower. While playing it I’ve tried various strategies but always wanted an automated way to test whether my strategies are actually any good. The best way to do that is to write some code that can play the game, so that’s what I did.&lt;/p&gt;</description></item><item><title>Snippet: Array element access in PHP</title><link>https://stut.dev/snippet-array-element-access-in-php</link><pubDate>Tue, 12 Apr 2011 00:00:39 +0100</pubDate><guid>https://stut.dev/snippet-array-element-access-in-php</guid><description>&lt;p&gt;A common logic pattern that’s seen when dealing with GET and POST parameters in PHP is to check whether the array element exists, then set another variable to that or a default. It usually looks something like this…&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$var &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;isset&lt;/span&gt;($_GET[&lt;span style="color:#e6db74"&gt;&amp;#39;var&amp;#39;&lt;/span&gt;]) &lt;span style="color:#f92672"&gt;?&lt;/span&gt; $_GET[&lt;span style="color:#e6db74"&gt;&amp;#39;var&amp;#39;&lt;/span&gt;]) &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you don’t develop with notices turned on (WHICH YOU SHOULD!) you probably haven’t seen the problem that this code gets around. Simply referring to &lt;code&gt;$_GET['var']&lt;/code&gt;will produce a notice if it does not exist.&lt;/p&gt;</description></item><item><title>Twitter TOS changes</title><link>https://stut.dev/twitter-tos-changes</link><pubDate>Sun, 13 Mar 2011 00:00:11 +0000</pubDate><guid>https://stut.dev/twitter-tos-changes</guid><description>&lt;p&gt;A couple of days ago, Ryan Sarver of Twitter sent an &lt;a href="https://groups.google.com/forum/?hl=en_US&amp;amp;pli=1#!topic/twitter-api-announce/yCzVnHqHIWo"&gt;email to the API developers mailing list&lt;/a&gt; notifying us of changes to their &lt;a href="http://dev.twitter.com/pages/api_terms"&gt;terms of service&lt;/a&gt;. As per usual the community have exaggerated the implications of these changes to the point where it’s now pure comedy. As has happened every time Twitter make any changes that may affect developers, developers are panicking, and for no good reason that I can find.&lt;/p&gt;</description></item><item><title>Misleading broadband ads</title><link>https://stut.dev/misleading-broadband-ads</link><pubDate>Mon, 24 Jan 2011 00:00:48 +0000</pubDate><guid>https://stut.dev/misleading-broadband-ads</guid><description>&lt;p&gt;My thoughts on ISPs and how they’re whiney little kids who like to throw their toys out of the pram have been &lt;a href="https://stut.dev/when-will-isps-realise-theyre-supposed-to-be-service-providers/"&gt;previously documented on this site&lt;/a&gt;. Apparently the advertising industry is set to get involved, and, according to NMA, is &lt;a href="http://www.nma.co.uk/3022636.article"&gt;poised to ask the public for their thoughts on how broadband suppliers advertise their services&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here are my thoughts on this prospect.&lt;/p&gt;
&lt;p&gt;That article states that…&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Concerns centre on claims made by suppliers about the maximum speed customers can expect to receive from their broadband service against the speeds actually experienced.&lt;/p&gt;</description></item><item><title>TweetMeme live test video from April 2009</title><link>https://stut.dev/tweetmeme-live-test-video-from-april-2009</link><pubDate>Fri, 19 Feb 2010 00:00:03 +0000</pubDate><guid>https://stut.dev/tweetmeme-live-test-video-from-april-2009</guid><description>&lt;p&gt;The other day I was continuing my long-term quest to sort through all the flotsam and jetsam of accumulated crap on my hard drive when I came across the following video (click through if you don’t see the video below). Recorded in April 2009, it shows the first version of the TweetMeme Live functionality without any rate or minimum retweet restrictions. As you can see it moved pretty quickly.&lt;/p&gt;
&lt;iframe width="459" height="344" src="https://www.youtube.com/embed/IdzvCoNncgY?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;Bear in mind that these are unique URLs, not simply repeats of URLs already seen. Between seeing a URL in a tweet and it appearing on this page the system has followed any redirects, grabbed and parsed the contents of the page and stuffed it into a database, and it does it all nearly instantly. Pretty impressive stuff if you ask me.&lt;/p&gt;</description></item><item><title>PHP job queue</title><link>https://stut.dev/php-job-queue</link><pubDate>Fri, 29 May 2009 00:00:31 +0100</pubDate><guid>https://stut.dev/php-job-queue</guid><description>&lt;p&gt;One of the pillars of a scalable website is ensuring that only activity which is required to build a page should be performed during the processing of a page request. Activities that fall under this category commonly include sending emails, recording statistics and general housekeeping such as removing temporary files.&lt;/p&gt;
&lt;p&gt;Back when I started working on sites big enough for these activities to cause a problem I went down the obvious route of making a PHP CLI script for each job that needed doing and getting it to run using cron. This worked for a while but as the sites I was working on got bigger and more complex it quickly became clear that this was becoming difficult to manage, so I started to consider alternatives.&lt;/p&gt;</description></item><item><title>Twitter unfollows: Too much information?</title><link>https://stut.dev/twitter-unfollows-too-much-information</link><pubDate>Sat, 31 Jan 2009 00:00:09 +0000</pubDate><guid>https://stut.dev/twitter-unfollows-too-much-information</guid><description>&lt;p&gt;A couple of days ago I released a new &lt;a href="http://twitapps.com/"&gt;TwitApps&lt;/a&gt; tool called &lt;a href="http://twitapps.com/follows/"&gt;Follows&lt;/a&gt;. I know I haven’t talked about TwitApps on this blog yet – that post is coming – but if you &lt;a href="http://twitter.com/stut"&gt;follow me on Twitter&lt;/a&gt; you should be aware of it.&lt;/p&gt;
&lt;p&gt;The Follows service monitors your followers on Twitter and sends you a daily, weekly or monthly email telling you who’s followed and who’s unfollowed you since your last email. Twitter itself has the facility to send you an email every time someone starts following you but does not offer any sort of notification when they stop.&lt;/p&gt;</description></item><item><title>OAuth and Twitter: Realistic expectations</title><link>https://stut.dev/oauth-and-twitter-realistic-expectations</link><pubDate>Sat, 24 Jan 2009 00:00:51 +0000</pubDate><guid>https://stut.dev/oauth-and-twitter-realistic-expectations</guid><description>&lt;p&gt;&lt;a href="http://twitter.com/"&gt;Twitter&lt;/a&gt; promised OAuth support a l-o-o-ong time ago, and it would appear to finally be here. &lt;a href="http://al3x.net/"&gt;Alex Payne&lt;/a&gt; sent a “&lt;a href="http://groups.google.com/group/twitter-development-talk/browse_thread/thread/42486bd3d7d136d0/f0e89b742bf0033e?show_docid=f0e89b742bf0033e&amp;amp;pli=1"&gt;Call for OAuth beta participants&lt;/a&gt;” to the developers list yesterday and had an overwhelming response. This predictably triggered a deluge of tweets and blog posts, but the one that caught my eye was on &lt;a href="http://www.readwriteweb.com/"&gt;ReadWriteWeb&lt;/a&gt; titled “&lt;a href="http://www.readwriteweb.com/archives/why_twitters_new_oauth_matters.php"&gt;Why Twitter’s New Security Solution Could Pave the Way to a Future Web of Mashups&lt;/a&gt;”.&lt;/p&gt;
&lt;p&gt;A number of things disturbed me about this post so I posted &lt;a href="http://www.readwriteweb.com/archives/why_twitters_new_oauth_matters.php#comment-124285"&gt;a comment or two&lt;/a&gt; but I believe the issues involved deserve more attention.&lt;/p&gt;</description></item><item><title>Twitorfit</title><link>https://stut.dev/twitorfit</link><pubDate>Wed, 17 Dec 2008 00:00:20 +0000</pubDate><guid>https://stut.dev/twitorfit</guid><description>&lt;p&gt;My latest project went live on Monday this week. It’s another &lt;a href="http://twitter.com/stut"&gt;Twitter&lt;/a&gt;-based toy along the
same lines as &lt;a href="http://www.hotornot.com/"&gt;hotornot&lt;/a&gt; but it uses your Twitter account and profile picture. Since
launch it’s proved very successful and has spread quickly due to its viral nature.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Screenshot lost :(&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The site is called &lt;a href="http://www.twitorfit.com/"&gt;Twitorfit&lt;/a&gt; and was the brainchild of a Twitter-based conversation between
&lt;a href="http://www.nickhalstead.com/"&gt;Nick Halstead&lt;/a&gt; of &lt;a href="http://fav.or.it/"&gt;fav.or.it&lt;/a&gt; and
&lt;a href="http://www.huddle.net/"&gt;Andy McLoughlin&lt;/a&gt; and &lt;a href="http://girlaboutweb.com/"&gt;Zuzanna Pasierbinska&lt;/a&gt; from
&lt;a href="http://www.huddle.net/"&gt;Huddle&lt;/a&gt;. You can read the full story on the
&lt;a href="http://blog.twitorfit.com/2008/12/15/twitorfit-%EF%BF%BD-the-story/"&gt;Twitorfit blog&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Snippet: Simple templates with PHP</title><link>https://stut.dev/snippet-simple-templates-with-php</link><pubDate>Tue, 28 Oct 2008 00:00:27 +0000</pubDate><guid>https://stut.dev/snippet-simple-templates-with-php</guid><description>&lt;p&gt;Several templating systems exist for PHP, but since PHP was originally created as a templating language I’ve never understood why more developers don’t use pure PHP in their view layer. The following snippet is a small function that facilitates precisely that.&lt;/p&gt;
&lt;p&gt;The only setup required is to simply define TPL_DIR to point to the root directory of your templates. You can then call the function, passing it the relative path to a template file in that directory, an array of variables to be used by the template and you can optionally have it return the results rather than sending them to the browser.&lt;/p&gt;</description></item><item><title>Snippet: Password generator for PHP</title><link>https://stut.dev/snippet-password-generator-for-php</link><pubDate>Thu, 23 Oct 2008 00:00:24 +0100</pubDate><guid>https://stut.dev/snippet-password-generator-for-php</guid><description>&lt;p&gt;Really short snippet today – a password generator. Not much to say about this one since it’s pretty simple. Pass it the length of password you want and optionally a string of valid characters it can use and it’ll give you a random string back.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;GeneratePassword&lt;/span&gt;($len, $allowedchars &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($allowedchars &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $allowedchars &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;abcdefghijklmnopqrstuvwxyz01234567890&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $retval &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $maxidx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strlen&lt;/span&gt;($allowedchars) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; ($i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; $i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; $len; $i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $retval &lt;span style="color:#f92672"&gt;.=&lt;/span&gt; $allowedchars[&lt;span style="color:#a6e22e"&gt;rand&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, $maxidx)];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; $retval;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I’ve got something a bit meatier lined up for the next snippet so I recommend subscribing to &lt;a href="https://stut.dev/index.xml"&gt;the RSS feed&lt;/a&gt; to make sure you don’t miss it. As always comments, questions, suggestions and requests are welcomed.&lt;/p&gt;</description></item><item><title>Snippet: Singletons with PHP</title><link>https://stut.dev/snippet-singletons-with-php</link><pubDate>Mon, 20 Oct 2008 00:00:25 +0100</pubDate><guid>https://stut.dev/snippet-singletons-with-php</guid><description>&lt;p&gt;Today’s snippet describes the singleton pattern. The singleton pattern is a method of creating a class that statically maintains an instance of itself and ensures that no other instances can be created. This is useful for classes that implement app-wide services such as logging, DB access and other shared resources.&lt;/p&gt;
&lt;p&gt;The basic requirements for a singleton class are as follows…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The constructor is private.&lt;/strong&gt; This ensures that nothing outside the class can create instances.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A static method exists that returns &lt;em&gt;the&lt;/em&gt; instance.&lt;/strong&gt; When first called the method creates a statically stored instance of the class. Subsequent calls return the existing instance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Magic methods to prevent the instance from being cloned or serialized &lt;em&gt;(PHP-specific).&lt;/em&gt;&lt;/strong&gt; PHP provides functionality to clone and serialize objects. By adding simple implementations of the &lt;em&gt;__clone&lt;/em&gt; and &lt;em&gt;__wakeup&lt;/em&gt; magic methods we can prevent this from happening.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s basically it. Here’s a simple example&amp;hellip;&lt;/p&gt;</description></item><item><title>Snippet: Time class for PHP</title><link>https://stut.dev/snippet-time-class-for-php</link><pubDate>Fri, 17 Oct 2008 00:00:21 +0100</pubDate><guid>https://stut.dev/snippet-time-class-for-php</guid><description>&lt;p&gt;Pretty boring snippet today, but I find it immensely useful. My time class provides representations of periods from OneMinute to OneYear. It only has one method which will calculate an absolute time by adding a given time period to the current time. You can optionally provide a format intended for use by the date function to return a formatted string rather than a timestamp.&lt;/p&gt;
&lt;p&gt;Hope you find it useful too.&lt;/p&gt;</description></item><item><title>Snippet: Cookie class for PHP</title><link>https://stut.dev/snippet-cookie-class-for-php</link><pubDate>Thu, 16 Oct 2008 00:00:20 +0100</pubDate><guid>https://stut.dev/snippet-cookie-class-for-php</guid><description>&lt;p&gt;Today’s snippet is a complete class for normalising cookie usage. In addition to providing methods to set and get cookies you can also check to see if a cookie exists, is empty and delete them without needing to concern yourself with more detail than necessary. Features include…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cookies Set during a request are immediately available via the Get method.&lt;/li&gt;
&lt;li&gt;Deleted cookies immediately cease to exist within the current request.&lt;/li&gt;
&lt;li&gt;Pre&lt;/li&gt;
&lt;li&gt;defined time spans allow for simple cookie management.&lt;/li&gt;
&lt;li&gt;Sensible defaults throughout.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Cookie&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Session&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;OneDay&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;86400&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SevenDays&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;604800&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ThirtyDays&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2592000&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SixMonths&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;15811200&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;OneYear&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;31536000&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Lifetime&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// 2030-01-01 00:00:00
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * Returns true if there is a cookie with this name.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @return bool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exists&lt;/span&gt;($name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;isset&lt;/span&gt;($_COOKIE[$name]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * Returns true if there no cookie with this name or it&amp;#39;s empty, or 0,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * or a few other things. Check http://php.net/empty for a full list.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @return bool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;IsEmpty&lt;/span&gt;($name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;empty&lt;/span&gt;($_COOKIE[$name]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * Get the value of the given cookie. If the cookie does not exist the value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * of $default will be returned.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $default
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @return mixed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Get&lt;/span&gt;($name, $default &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;isset&lt;/span&gt;($_COOKIE[$name]) &lt;span style="color:#f92672"&gt;?&lt;/span&gt; $_COOKIE[$name] &lt;span style="color:#f92672"&gt;:&lt;/span&gt; $default);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * Set a cookie. Silently does nothing if headers have already been sent.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param mixed $expiry
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $path
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $domain
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @return bool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Set&lt;/span&gt;($name, $value, $expiry &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;self&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;OneYear&lt;/span&gt;, $path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;, $domain &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $retval &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;headers_sent&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($domain &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $domain &lt;span style="color:#f92672"&gt;=&lt;/span&gt; $_SERVER[&lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_HOST&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($expiry &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $expiry &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1893456000&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// Lifetime = 2030-01-01 00:00:00
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;elseif&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;is_numeric&lt;/span&gt;($expiry)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $expiry &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;time&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $expiry &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strtotime&lt;/span&gt;($expiry);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $retval &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;setcookie&lt;/span&gt;($name, $value, $expiry, $path, $domain);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($retval) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $_COOKIE[$name] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; $value;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; $retval;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * Delete a cookie.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $path
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param string $domain
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @param bool $remove_from_global Set to true to remove this cookie from this request.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; * @return bool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Delete&lt;/span&gt;($name, $path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;, $domain &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;, $remove_from_global &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $retval &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;headers_sent&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($domain &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $domain &lt;span style="color:#f92672"&gt;=&lt;/span&gt; $_SERVER[&lt;span style="color:#e6db74"&gt;&amp;#39;HTTP_HOST&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $retval &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;setcookie&lt;/span&gt;($name, &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;time&lt;/span&gt;() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3600&lt;/span&gt;, $path, $domain);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($remove_from_global) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;unset&lt;/span&gt;($_COOKIE[$name]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; $retval;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some example usage…&lt;/p&gt;</description></item><item><title>Snippet: Page expiry in PHP</title><link>https://stut.dev/snippet-page-expiry-in-php</link><pubDate>Wed, 15 Oct 2008 00:00:39 +0100</pubDate><guid>https://stut.dev/snippet-page-expiry-in-php</guid><description>&lt;p&gt;This is the first of what I hope will be a series of posts showing snippets of code from my personal library. My intention is to highlight useful techniques I’ve developed or picked up over the years. Comments, questions, suggestions and requests are welcomed.&lt;/p&gt;
&lt;p&gt;This first snippet is a simple function to set appropriate expiry headers. Simply pass it a time period in seconds or a unix timestamp to set the expiry to some time in the future, or 0 to expire the page immediately.&lt;/p&gt;</description></item><item><title>Sessionless sessions</title><link>https://stut.dev/sessionless-sessions</link><pubDate>Sat, 26 Jul 2008 00:00:54 +0100</pubDate><guid>https://stut.dev/sessionless-sessions</guid><description>&lt;p&gt;Ok, so the title is a little misleading, but hopefully only until I explain what I mean. The first &lt;em&gt;session&lt;/em&gt; refers to PHP sessions, and the second refers to the concept of sessions. Maybe it needs a little more explanation than that.&lt;/p&gt;
&lt;h2 id="do-what"&gt;Do what?&lt;/h2&gt;
&lt;p&gt;There aren’t many PHP applications (or indeed web applications) out there that don’t need to maintain some form of user state between page requests. This functionality is commonly known as sessions, and most web development languages provide a built-in mechanism for managing them, and PHP is no exception. It’s probably safe to assume that most people reading this probably know what sessions are and how they work, but I think it’s worth taking a moment to spell it out.&lt;/p&gt;</description></item><item><title>Sending email</title><link>https://stut.dev/sending-email</link><pubDate>Wed, 18 Jun 2008 00:00:52 +0100</pubDate><guid>https://stut.dev/sending-email</guid><description>&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; If you’re using PHP you want &lt;a href="http://phpmailer.sf.net/"&gt;PHPMailer&lt;/a&gt;. If you’re using something else there’s probably an equivalent library (&lt;a href="http://www.google.com/"&gt;Google is your friend&lt;/a&gt;). However, regardless of how you do it you should always consider the impact of your email activities on your users.&lt;/p&gt;
&lt;p&gt;Unwanted email is one of the biggest problems on the Internet today and the more you can do to present your website as a responsible sender of email the better.&lt;/p&gt;</description></item><item><title>Is it harder to create usable digital interfaces?</title><link>https://stut.dev/is-it-harder-to-create-usable-digital-interfaces</link><pubDate>Sun, 13 Apr 2008 00:00:22 +0100</pubDate><guid>https://stut.dev/is-it-harder-to-create-usable-digital-interfaces</guid><description>&lt;p&gt;While catching up on &lt;a href="http://twitter.com/stut"&gt;Twitter&lt;/a&gt; this morning I came across a blog post by &lt;a href="http://twitter.com/davidcushman/statuses/787951651"&gt;David Cushman&lt;/a&gt; on &lt;a href="http://fasterfuture.blogspot.com/2008/04/definition-of-user-friendly-it-ainrt.html"&gt;the simplicity of riding a bicycle compared to using ebay or iTunes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I kinda see what he’s saying, but as he points out I think it has more to do with what people are used to. His daughter has almost certainly seen people riding bikes so I would suggest she picked up the general idea through observation rather than it being particularly intuitive. While it’s not likely to happen I’d love to see how someone who’s never seen a bike would go about working out how to ride it.&lt;/p&gt;</description></item><item><title>When will ISPs realise they're supposed to be "service providers"?</title><link>https://stut.dev/when-will-isps-realise-theyre-supposed-to-be-service-providers</link><pubDate>Wed, 09 Apr 2008 00:00:48 +0100</pubDate><guid>https://stut.dev/when-will-isps-realise-theyre-supposed-to-be-service-providers</guid><description>&lt;p&gt;This is really daft. It’s been widely covered recently that the &lt;a href="http://www.bbc.co.uk/iplayer/"&gt;BBC iPlayer&lt;/a&gt; service is taking off in a big way which is great news for the future of TV. Unfortunately some &lt;a href="http://news.bbc.co.uk/1/hi/technology/7336940.stm"&gt;ISPs are throwing their toys out of the pram&lt;/a&gt; and complaining that the increased bandwidth usage is costing them a lot of money and that &lt;a href="http://www.bbc.co.uk/"&gt;the BBC&lt;/a&gt; should help cover the cost.&lt;/p&gt;
&lt;p&gt;This strikes me as an extension of the equally stupid claim that it’s ok to have &lt;a href="http://www.weeklygripe.co.uk/a458.asp"&gt;fair usage limits on unlimited broadband packages&lt;/a&gt;. The ISPs provide connectivity to their users and it’s up to them to set their terms of use. If they feel they need to bring in more cash to cover the additional costs incurred by these additional services they should price their packages accordingly, but they should also do so as transparently as possible and not try to pass the cost on to content providers and certainly not to the tax payer.&lt;/p&gt;</description></item><item><title>Web hosting - a mug's game</title><link>https://stut.dev/web-hosting-a-mugs-game</link><pubDate>Sun, 11 Nov 2007 00:00:21 +0000</pubDate><guid>https://stut.dev/web-hosting-a-mugs-game</guid><description>&lt;blockquote&gt;
&lt;p&gt;It is with great regret that I must inform you that SharedServer.net will cease trading at the end of this year. This has been a difficult decision to make, but for various reasons it is not practical to continue.&lt;br&gt;
&lt;strong&gt;Me, In An Email To My Customers, Early November 2007&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I wrote this in an email to my customers last weekend. After 10 years of running a web hosting service as “a hobby” I’ve decided that it’s no longer practical to continue with it. I’ve had several customers reply wanting to know why, most also saying that they were very happy with the service which is always nice to hear. I can’t go into some of the reasons yet – I’ll post those when I am able to – but hopefully the following will go some way to explaining my decision.&lt;/p&gt;</description></item><item><title>Extracting the map from social networks</title><link>https://stut.dev/extracting-the-map-from-social-networks</link><pubDate>Wed, 10 Oct 2007 00:00:45 +0100</pubDate><guid>https://stut.dev/extracting-the-map-from-social-networks</guid><description>&lt;p&gt;One thought keeps spinning round my head and has been for a while now. There are too many social networks for global participation to be practical. The core problem is that each site has their own list of your friends. This list is usually referred to as the social map and for the idea of social networking to really become a permanent part of the Web this aspect needs to be extracted and standardised.&lt;/p&gt;</description></item><item><title>Amazon S3 SLA</title><link>https://stut.dev/amazon-s3-sla</link><pubDate>Tue, 09 Oct 2007 00:00:34 +0100</pubDate><guid>https://stut.dev/amazon-s3-sla</guid><description>&lt;p&gt;Amazon have &lt;a href="http://aws.typepad.com/aws/2007/10/amazon-s3-at-yo.html"&gt;announced an SLA for their S3 service&lt;/a&gt; which is, you know, good news. But like every other company on the planet they place responsibility for monitoring whether they meet it on the customer.&lt;/p&gt;
&lt;p&gt;By definition an SLA is a company committing to a level of service. If they then expect you to monitor their availability they are essentially saying they’ll only get punished for breaching it if you notice that they’ve breached it, and even then only if you “apply” for compensation. I’d be interested to know what percentage of people monitor their interaction with S3 to the level required to catch breaches, and how many will be adding it based on the arrival of the SLA.&lt;/p&gt;</description></item></channel></rss>