<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet href="/feed.xsl" type="text/xsl" ?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en" xml:base="https://chrisburnell.com">
	<title>Chris Burnell · Php</title>
	<subtitle>All of my posts tagged “php”.</subtitle>
	<id>https://chrisburnell.com/tag/php/</id>
	<link href="https://chrisburnell.com/tag/php.xml" rel="self" />
	<link href="https://chrisburnell.com/tag/php/" rel="alternate" />
	<author>
		<name>Chris Burnell</name>
		<uri>https://chrisburnell.com/</uri>
		<email>me@chrisburnell.com</email>
	</author>
	<logo>https://chrisburnell.com/images/raven.svg</logo>
	<image>https://chrisburnell.com/images/avatar@4x.jpeg</image>
	<updated>2026-05-09T12:49:02+00:00</updated>
	<generator>Eleventy v3.1.0</generator>
	<entry>
		<id>https://chrisburnell.com/article/fresh-pixels/</id>
		<link href="https://chrisburnell.com/article/fresh-pixels/" />
		<title>Fresh pixels</title>
		<published>2026-05-09T09:49:02-03:00</published>
		<publishedFriendly>9 May 2026</publishedFriendly>
		<updated>2026-05-09T09:49:02-03:00</updated>
		<category term="article" scheme="https://chrisburnell.com/article/" label="Article" />
		<summary>Status updates and enforcing the crispiest-possible 88x31 badges across the web</summary>
		<content xml:lang="en" type="html">
			&lt;p&gt;Status updates and enforcing the crispiest-possible 88x31 badges across the web&lt;/p&gt;
				&lt;hr&gt;
			
			
			&lt;p&gt;Yesterday afternoon, I spent some time creating a new &lt;a href=&quot;/88x31/&quot;&gt;88x31 badge&lt;/a&gt; for my website footer.&lt;/p&gt;
&lt;p&gt;In case you missed &lt;a href=&quot;/article/new-year-new-server-2/&quot;&gt;my last post&lt;/a&gt;, I’m in the midst of migrating servers, and one of the things I migrated was &lt;a href=&quot;https://status.chrisburnell.com&quot;&gt;my status page&lt;/a&gt;, which pings each of my websites once per minute and reports whether the request was successful, and if so, how long the request took. Each website is represented on the front end by a graph of response times across the last hour as well as 60 red or green-coloured &lt;em&gt;bloops&lt;/em&gt;, which represent the successes of the last hour’s pings.&lt;/p&gt;
&lt;p&gt;So the idea was to create a dynamic 88x31 badge that relayed information from my status page by displaying some number of the most recent &lt;em&gt;bloops&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I set out achieving this by first creating the 88x31 image with some blank areas that I could fill in later with green or red, depending on the status of each &lt;em&gt;bloop&lt;/em&gt;, and the next step was to create a short PHP script to consume this image, fill in the colours of the &lt;em&gt;bloops&lt;/em&gt;, and spit out the result.&lt;/p&gt;
&lt;p&gt;Fortunately, generating images with PHP is one of the first things I ever learned how to do outside of HTML and CSS early on in my web development journey, nearly 20 years ago, so this was a familiar task!&lt;/p&gt;
&lt;p&gt;And the result (blown up for clarity):&lt;/p&gt;
&lt;figure&gt;
    &lt;img src=&quot;https://chrisburnell.com/images/uptime.gif&quot; alt=&quot;Server uptime badge&quot; width=&quot;352&quot; height=&quot;124&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
&lt;/figure&gt;
&lt;details&gt;
    &lt;summary&gt;Backend code, if you’re interested&lt;/summary&gt;
&lt;p&gt;Here’s what the JSON file of minute-by-minute data looks like:&lt;/p&gt;
&lt;pre data-language=&quot;json&quot; data-language-friendly=&quot;JSON&quot; data-filename=&quot;uptime.json&quot;&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
	&lt;span&gt;{&lt;/span&gt;
		&lt;span&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;1778260261&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;time&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;0.406&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;response&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;200&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;http_version&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;&quot;2&quot;&lt;/span&gt;
	&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
	&lt;span&gt;{&lt;/span&gt;
		&lt;span&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;1778260321&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;time&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;0.446&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;response&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;200&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;http_version&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;&quot;2&quot;&lt;/span&gt;
	&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
	&lt;span&gt;{&lt;/span&gt;
		&lt;span&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;1778260381&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;time&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;0.807&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;response&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;200&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
		&lt;span&gt;&quot;http_version&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;&quot;2&quot;&lt;/span&gt;
	&lt;span&gt;}&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here’s the gist of the PHP that generates the dynamic image:&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot; data-filename=&quot;uptime.php&quot;&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;?php&lt;/span&gt;
&lt;p&gt;&lt;span&gt;$image&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecreatefromgif&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;uptime.gif&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$green&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecolorallocate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;102&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;204&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;51&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$red&lt;/span&gt;   &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecolorallocate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;153&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;51&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$cacheFile&lt;/span&gt;  &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&quot;cache.json&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$uptimeFile&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&quot;uptime.json&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$numBloops&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;9&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;file_exists&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$cacheFile&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;||&lt;/span&gt; &lt;span&gt;filemtime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$cacheFile&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt; &lt;span&gt;time&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;-&lt;/span&gt; &lt;span&gt;60&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$uptimeData&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;json_decode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;file_get_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$uptimeFile&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;usort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$uptimeData&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$a&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$b&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; &lt;span&gt;$b&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span&gt;-&lt;/span&gt; &lt;span&gt;$a&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$recent&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;array_slice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$uptimeData&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$numBloops&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$cache&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;&lt;br&gt;
&lt;span&gt;&quot;timestamp&quot;&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; &lt;span&gt;$recent&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;br&gt;
&lt;span&gt;&quot;bloops&quot;&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; &lt;span&gt;array_map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;br&gt;
&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$item&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; &lt;span&gt;$item&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;time&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;0&lt;/span&gt; &lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span&gt;$item&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;response&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span&gt;&amp;gt;=&lt;/span&gt; &lt;span&gt;200&lt;/span&gt; &lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span&gt;$item&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;response&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span&gt;&amp;lt;=&lt;/span&gt; &lt;span&gt;299&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$recent&lt;/span&gt;&lt;br&gt;
&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;br&gt;
&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;file_put_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$cacheFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;json_encode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$cache&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;}&lt;/span&gt; &lt;span&gt;else&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$cache&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;json_decode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;file_get_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$cacheFile&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;$bloops&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;array_reverse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$cache&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;bloops&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;foreach&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span&gt;$bloops&lt;/span&gt; &lt;span&gt;as&lt;/span&gt; &lt;span&gt;$i&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; &lt;span&gt;$ok&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$color&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$ok&lt;/span&gt; &lt;span&gt;?&lt;/span&gt; &lt;span&gt;$green&lt;/span&gt; &lt;span&gt;:&lt;/span&gt; &lt;span&gt;$red&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagefilledrectangle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;4&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$color&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Content-type: image/gif&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Cache-Control: public, max-age=60&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;imagegif&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;And while we’re at it, here’s how I’m serving my PHP file as if it’s a GIF in &lt;em&gt;nginx&lt;/em&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;nginx&quot; data-language-friendly=&quot;nginx&quot; data-filename=&quot;/etc/nginx/sites-available/example.com&quot;&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;location&lt;/span&gt; = /images/uptime.gif&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;&lt;span&gt;include&lt;/span&gt; fastcgi_params&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
    &lt;span&gt;&lt;span&gt;fastcgi_param&lt;/span&gt; SCRIPT_FILENAME /absolute/path/to/uptime.php&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
    &lt;span&gt;&lt;span&gt;fastcgi_pass&lt;/span&gt; unix:/run/php/php8.4-fpm.sock&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;p&gt;In building this new 88x31, I was struck with another idea.&lt;/p&gt;
&lt;p&gt;Because I so fiercely believe that (most) 88x31 badges look best on the web when they have &lt;code&gt;image-rendering: pixelated;&lt;/code&gt; applied to them, why not enforce my belief across (almost) the entire web?&lt;/p&gt;
&lt;p id=&quot;using-css&quot;&gt;Using CSS&lt;/p&gt;
&lt;p&gt;I use the &lt;a href=&quot;https://add0n.com/stylus.html&quot;&gt;Stylus&lt;/a&gt; extension in my browser (&lt;a href=&quot;https://github.com/openstyles/stylus#releases&quot;&gt;get it here&lt;/a&gt;) to apply my own CSS to websites and pages. My custom styles are mostly to fix bugs, improve shaky layouts, or to add little quality-of-life stuff, and this seems like an ideal place for me to throw a small chunk of CSS and have my browser apply it across every page that I visit.&lt;/p&gt;
&lt;p&gt;The CSS I came up with is pretty minimal, and although it relies on the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes being present, many websites that I visit that &lt;em&gt;do&lt;/em&gt; have 88x31 badges at least have the attributes present, if not the desired CSS property applied as well:&lt;/p&gt;
&lt;pre data-language=&quot;css&quot; data-language-friendly=&quot;CSS&quot;&gt;&lt;code&gt;&lt;span&gt;:where(img[width=&quot;88&quot;][height=&quot;31&quot;])&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
	&lt;span&gt;image-rendering&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; pixelated&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s how it looks in &lt;em&gt;Stylus&lt;/em&gt;. Note that I’m applying this CSS to &lt;q&gt;Everything&lt;/q&gt;, which means &lt;em&gt;every&lt;/em&gt; webpage that you visit:&lt;/p&gt;
&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;/images/built/stylus-pixelate-88x31s-500.avif 500w, /images/built/stylus-pixelate-88x31s-800.avif 800w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/images/built/stylus-pixelate-88x31s-500.webp 500w, /images/built/stylus-pixelate-88x31s-800.webp 800w&quot; sizes=&quot;100vw&quot;&gt;&lt;img alt=&quot;Stylus UI when adding styles for all websites to pixelate 88x31 badges&quot; title=&quot;Stylus UI when adding styles for all websites to pixelate 88x31 badges&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;/images/built/stylus-pixelate-88x31s-500.png&quot; width=&quot;800&quot; height=&quot;128&quot; srcset=&quot;/images/built/stylus-pixelate-88x31s-500.png 500w, /images/built/stylus-pixelate-88x31s-800.png 800w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;
&lt;/figure&gt;
&lt;p id=&quot;using-js&quot;&gt;Using JavaScript&lt;/p&gt;
&lt;p&gt;Alternatively, if you want to achieve this for &lt;em&gt;all&lt;/em&gt; 88x31 images, whether they have the correct attributes or not, there’s the &lt;a href=&quot;https://www.tampermonkey.net/&quot;&gt;Tampermonkey&lt;/a&gt; extension (&lt;a href=&quot;https://www.tampermonkey.net/#download&quot;&gt;get it here&lt;/a&gt;) that works much like Stylus but with JavaScript instead of CSS. Here’s my script that checks the width and height of images &lt;em&gt;whether their attributes are set or not&lt;/em&gt; and appropriately applies the pixelated effect to 88x31 images:&lt;/p&gt;
&lt;pre data-language=&quot;javascript&quot; data-language-friendly=&quot;JavaScript&quot;&gt;&lt;code&gt;&lt;span&gt;// ==UserScript==&lt;/span&gt;
&lt;span&gt;// @name         Pixelate 88x31s&lt;/span&gt;
&lt;span&gt;// @match        *://*/*&lt;/span&gt;
&lt;span&gt;// @run-at       document-idle&lt;/span&gt;
&lt;span&gt;// ==/UserScript==&lt;/span&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;document&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;img&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;br&gt;
&lt;span&gt;const&lt;/span&gt; &lt;span&gt;pixelate&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;br&gt;
&lt;span&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;img&lt;span&gt;.&lt;/span&gt;naturalWidth &lt;span&gt;===&lt;/span&gt; &lt;span&gt;88&lt;/span&gt; &lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt; img&lt;span&gt;.&lt;/span&gt;naturalHeight &lt;span&gt;===&lt;/span&gt; &lt;span&gt;31&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;br&gt;
img&lt;span&gt;.&lt;/span&gt;style&lt;span&gt;.&lt;/span&gt;imageRendering &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&quot;pixelated&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;}&lt;/span&gt;&lt;br&gt;
&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
img&lt;span&gt;.&lt;/span&gt;complete &lt;span&gt;?&lt;/span&gt; &lt;span&gt;pixelate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;:&lt;/span&gt; img&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;load&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; pixelate&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now properly-attributed 88x31 images will always appear nice and sharp, everywhere on the web, including but not limited to the ones in the footer of my website (though they already have the &lt;em&gt;correct&lt;/em&gt; &lt;code&gt;image-rendering&lt;/code&gt;)!&lt;/p&gt;
			
			
			
			
				&lt;hr&gt;
				&lt;p&gt;Thanks for subscribing and reading this post via RSS!&lt;/p&gt;
				&lt;p&gt;You can read &lt;a href=&quot;https://chrisburnell.com/article/fresh-pixels/&quot;&gt;this post&lt;/a&gt; and others &lt;a href=&quot;https://chrisburnell.com/posts/&quot;&gt;on my website&lt;/a&gt;.&lt;/p&gt;
		</content>
		
		
	</entry>
	<entry>
		<id>https://chrisburnell.com/article/obfuscating-api-calls/</id>
		<link href="https://chrisburnell.com/article/obfuscating-api-calls/" />
		<title>How I’m obfuscating API calls in my front end JavaScript</title>
		<published>2024-05-09T22:43:00+08:00</published>
		<publishedFriendly>9 May 2024</publishedFriendly>
		<updated>2024-05-09T22:43:00+08:00</updated>
		<category term="article" scheme="https://chrisburnell.com/article/" label="Article" />
		<summary>I use a rudimentary Web Component on my website to pull in data from the LastFM API, but I needed a way to prevent exposing my API key. To get around this, I put a short PHP script on my server that makes the API calls for me.</summary>
		<content xml:lang="en" type="html">
			&lt;p&gt;I use a rudimentary Web Component on my website to pull in data from the LastFM API, but I needed a way to prevent exposing my API key. To get around this, I put a short PHP script on my server that makes the API calls for me.&lt;/p&gt;
				&lt;hr&gt;
			
			
			&lt;p&gt;On my &lt;a href=&quot;/listening/&quot;&gt;Listening&lt;/a&gt; and &lt;a href=&quot;/now/&quot;&gt;/now&lt;/a&gt; pages, I’m displaying data from the &lt;a href=&quot;https://www.last.fm/api&quot;&gt;LastFM API&lt;/a&gt;: recently-listened tracks, top artists of the week, and top albums of week. I’m pulling in that information as part of my Eleventy build using &lt;a href=&quot;https://github.com/11ty/eleventy-fetch&quot;&gt;eleventy-fetch&lt;/a&gt;, so the contents of those pages are refreshed whenever my site builds and an hour has elapsed since the previous build. This means, for visitors without JavaScript enabled (or where it fails), those pages aren’t completely empty.&lt;/p&gt;
&lt;p&gt;Because my website only pulls in fresh data on rebuilds, this means that the data could be up to 6 hours old, and I’d like to show as up-to-date data in those places if I can! So when JavaScript &lt;em&gt;is&lt;/em&gt; enabled, a slap-dash Web Component that I built, &lt;code&gt;&amp;lt;lastfm-listening&amp;gt;&lt;/code&gt;, makes the same call to the LastFM API with a client-side &lt;code&gt;fetch&lt;/code&gt; in order to pull in fresh data.&lt;/p&gt;
&lt;p&gt;You have to be careful here, as it’s required to include an API key as a URL parameter with any requests to the API. This isn’t an issue for the Eleventy build side of things because I store my API keys in an &lt;code&gt;.env&lt;/code&gt; file which isn’t included in &lt;a href=&quot;https://github.com/chrisburnell/chrisburnell.com&quot;&gt;the source code on GitHub&lt;/a&gt;, and I’m able to read and use the values of those keys using &lt;a href=&quot;https://nodejs.org/api/process.html#process_process_env&quot;&gt;Node’s &lt;code&gt;process.env&lt;/code&gt; property&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, this isn’t possible in front end JavaScript, so anyone could look at my website’s source code, their browser’s Dev Tools, or even view the source of my website’s JavaScript to see where the &lt;code&gt;fetch&lt;/code&gt; request comes from and grab my API key from there.&lt;/p&gt;
&lt;pre data-language=&quot;javascript&quot; data-language-friendly=&quot;JavaScript&quot;&gt;&lt;code&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracksuser&amp;amp;api_key=njJql0lKXnotreal4x3Wmd&amp;amp;username=chrisburnell&amp;amp;format=json&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If my API key was &lt;code&gt;njJql0lKXnotreal4x3Wmd&lt;/code&gt; (which it isn’t &lt;c-icon&gt;😜&lt;/c-icon&gt;), malicious people could abuse the LastFM API by using my key, and &lt;em&gt;I&lt;/em&gt; would be penalised (probably by having my key revoked).&lt;/p&gt;
&lt;p&gt;To circumvent this issue, I built a short snippet of PHP code that lives on my server to make the same API calls on my behalf:&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;?php&lt;/span&gt;
&lt;p&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;$api_key&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&quot;njJql0lKXnotreal4x3Wmd&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;$response&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;file_get_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;a href=&quot;https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&amp;amp;amp;api_key=&quot;&gt;https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&amp;amp;amp;api_key=&lt;/a&gt;&quot;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$api_key&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;&quot;&amp;amp;format=json&amp;amp;user=&quot;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$_GET&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;username&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$response&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now I can replace the &lt;code&gt;fetch&lt;/code&gt; request in my front end JavaScript to point at the URL of my PHP file, which makes the API request for me, obfuscating the API key, and passes on the data for me to consume as I was before.&lt;/p&gt;
&lt;pre data-language=&quot;javascript&quot; data-language-friendly=&quot;JavaScript&quot;&gt;&lt;code&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;https://chrisburnell.com/get-lastfm-recenttracks?user=chrisburnell&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also recommend putting some security in place here, because there’s still a gap in the armour. What prevents someone from using the PHP file on my server? There are a few ways to mitigate this.&lt;/p&gt;
&lt;p&gt;One option is to set up some rules on the server to only accept connections to this file from certain referers (i.e. your own websites), e.g. with the &lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_referer_module.html&quot;&gt;&lt;code&gt;ngx_http_referer_module&lt;/code&gt; in nginx&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another option is to do the same but inside the same chunk of PHP with something like this:&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;?php&lt;/span&gt;
&lt;p&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;$api_key&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&quot;njJql0lKXnotreal4x3Wmd&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;$allowed_host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&quot;&lt;a href=&quot;http://example.com&quot;&gt;example.com&lt;/a&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$referer_host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;parse_url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$_SERVER&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&#39;HTTP_REFERER&#39;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;PHP_URL_HOST&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;substr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$referer_host&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;0&lt;/span&gt; &lt;span&gt;-&lt;/span&gt; &lt;span&gt;strlen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$allowed_host&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;==&lt;/span&gt; &lt;span&gt;$allowed_host&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$response&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;file_get_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;a href=&quot;https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&amp;amp;amp;api_key=&quot;&gt;https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&amp;amp;amp;api_key=&lt;/a&gt;&quot;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$api_key&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;&quot;&amp;amp;format=json&amp;amp;user=&quot;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$_GET&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;username&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$response&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;}&lt;/span&gt;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I’ve seen quite a bit of chatter online about pulling data like this into our websites recently, and I’ve had some great conversations with friends about it too. I hope you find this useful if you’re inclined to progressively enhance your website with fresh data from APIs you love!&lt;/p&gt;
			
			
			&lt;hr&gt;
&lt;div id=&quot;weblogpomo&quot;&gt;
	&lt;p&gt;This is post #9 of &lt;a href=&quot;/article/weblogpomo2024-wrap-up/&quot;&gt;#WeblogPoMo2024 / Weblog Posting Month 2024&lt;/a&gt;, a one-month challenge to post as often as possible (daily, ideally) for the duration of May 2024!&lt;/p&gt;
	&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20240420081004/https://weblog.anniegreens.lol/weblog-posting-month-2024&quot; rel=&quot;external noopener&quot;&gt;Learn more on Anne Sturdivant’s website&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
			
				&lt;hr&gt;
				&lt;p&gt;Thanks for subscribing and reading this post via RSS!&lt;/p&gt;
				&lt;p&gt;You can read &lt;a href=&quot;https://chrisburnell.com/article/obfuscating-api-calls/&quot;&gt;this post&lt;/a&gt; and others &lt;a href=&quot;https://chrisburnell.com/posts/&quot;&gt;on my website&lt;/a&gt;.&lt;/p&gt;
		</content>
		
		
	</entry>
	<entry>
		<id>https://chrisburnell.com/article/revisiting-php-images/</id>
		<link href="https://chrisburnell.com/article/revisiting-php-images/" />
		<title>Revisiting PHP-Generated Images</title>
		<published>2024-05-04T21:02:10+08:00</published>
		<publishedFriendly>4 May 2024</publishedFriendly>
		<updated>2024-05-04T21:02:10+08:00</updated>
		<category term="article" scheme="https://chrisburnell.com/article/" label="Article" />
		<summary>Generating images using PHP was a bit like magic to me back in 2007. In this post, I’m going to revisit the technique to create an image that shows dynamically-updating information about my website content.</summary>
		<content xml:lang="en" type="html">
			&lt;p&gt;Generating images using PHP was a bit like magic to me back in 2007. In this post, I’m going to revisit the technique to create an image that shows dynamically-updating information about my website content.&lt;/p&gt;
				&lt;hr&gt;
			
			
			&lt;div&gt;
    &lt;p&gt;Jump to the code: &lt;a href=&quot;#build&quot;&gt;Building a PHP image&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;history&quot;&gt;A short history&lt;/h2&gt;
&lt;p&gt;In my teenage years, forums were &lt;em&gt;all the jam&lt;/em&gt;. It’s a bit of a shame that their popularity has waned since social media entered the scene because forums foster slower and more thoughtful conversations, in &lt;em&gt;my&lt;/em&gt; opinion, at least. I was a member of many forums dedicated to many topics, including Nintendo games, The Legend of Zelda, Star Wars, and pixel-art. I made some of my first online friends through these forums.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;Despite their not being as popular nowadays, forums aren’t dead! I’m a member of some wonderful forums that I’m happy to say are not just alive and kicking but buzzing with activity and growing. The &lt;a href=&quot;https://discourse.32bit.cafe&quot; rel=&quot;external noopener&quot;&gt;32-Bit Café forum&lt;/a&gt;, in particular, is full of great people, and I visit so often that it’s one of the very few tabs that are pinned in my browser!&lt;/p&gt;&lt;/aside&gt;
&lt;p&gt;Back in 2007, around the beginning of my journey into web development, I co-founded a small community of folks passionate about video games and creating media about them, and I was in charge of our website that hosted all of our community’s content, a completely-bespoke and hand-written mish-mash of HTML, CSS, MySQL databases, and PHP.&lt;/p&gt;
&lt;p&gt;Because me and the other founders/admins wanted an easy way for our community members to keep abreast of the latest content, the top-right of the website featured an area that listed and linked to the most-recently published videos, podcasts, and songs being made by us and our members.&lt;/p&gt;
&lt;p&gt;Now, it’s worth mentioning that a not-insignificant part of forum life back then (and even to this day) was having a cool signature, a section underneath each member’s posts where one could put fun little quips, quotes, and even images. Many forums had &lt;q&gt;graphics shops&lt;/q&gt; where requests could be made of talented individuals to create avatars, banners, signature images, etc. for members who asked nicely.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As a means of promoting what was going on in our video game media community, me and the other founders often had linked images going out to our website. I even went to the effort of putting a bit of text alongside the image in my signature that stated what content had been recently published on our website. Across multiple forums, this was a pretty time-consuming task that I went through whenever something new was published.&lt;/p&gt;
&lt;p&gt;Well, I got pretty tired of manually editing my forum signatures, and that’s when I discovered that it was possible to create images with PHP code! Instead of manually editing my signatures, I could create a PHP image hosted on our website that linked up with the database of published work and automatically pulled in fresh content.&lt;/p&gt;
&lt;p&gt;So that’s exactly what I did! Using my website’s content as an example, I’m going to walk through how to generate a PNG image using PHP.&lt;/p&gt;
&lt;h2 id=&quot;build&quot;&gt;Building a PHP image&lt;/h2&gt;
&lt;figure&gt;
    &lt;img src=&quot;https://api.chrisburnell.com/dynamic-banner.php&quot; alt=&quot;a small banner showing statistical information about content on chrisburnell.com&quot; width=&quot;300&quot; height=&quot;80&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;Before we start, it’s important to know that we’ll be working with a coordinate system as we build our image, where &lt;code&gt;0, 0&lt;/code&gt; is at the top-left, so &lt;code&gt;X&lt;/code&gt; increases to the right and &lt;code&gt;Y&lt;/code&gt; increases downwards.&lt;/p&gt;
&lt;p&gt;Now let’s write some code.&lt;/p&gt;
&lt;p&gt;First, we need to dictate the dimensions of the image using the &lt;a href=&quot;https://www.php.net/manual/en/function.imagecreatetruecolor.php&quot;&gt;&lt;code&gt;imagecreatetruecolor&lt;/code&gt;&lt;/a&gt; function, and we’ll enable transparency in case we put anything in our image that isn’t completely opaque with the &lt;a href=&quot;https://www.php.net/manual/en/function.imagealphablending.php&quot;&gt;&lt;code&gt;imagealphablending&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;$width&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;300&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$height&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;80&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$spacing&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;6&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;$image&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecreatetruecolor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$width&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$height&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagealphablending&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;We’ll come back to the &lt;code&gt;$spacing&lt;/code&gt; variable later.&lt;/p&gt;
&lt;p&gt;If we wanted to set a solid colour as the background of our image, we could use the &lt;a href=&quot;https://www.php.net/manual/en/function.imagecolorallocate.php&quot;&gt;&lt;code&gt;imagecolorallocate&lt;/code&gt;&lt;/a&gt; function, passing in a reference to our &lt;code&gt;$image&lt;/code&gt; and represent our desired colour using RGB components. The &lt;a href=&quot;https://www.php.net/manual/en/function.imagefill.php&quot;&gt;&lt;code&gt;imagefill&lt;/code&gt;&lt;/a&gt; function fills our image with our desired colour, starting from the given coordinates (&lt;code&gt;0, 0&lt;/code&gt; in this case).&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;$backgroundColor&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecolorallocate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;6&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;6&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;6&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;imagefill&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$backgroundColor&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;However, in my case, I want to apply a static image for the background. For that, we can use the &lt;a href=&quot;https://www.php.net/manual/en/function.imagecreatefrompng.php&quot;&gt;&lt;code&gt;imagecreatefrompng&lt;/code&gt;&lt;/a&gt; function to reference the static image file. We’ll use the &lt;a href=&quot;https://www.php.net/manual/en/function.imagecopy.php&quot;&gt;&lt;code&gt;imagecopy&lt;/code&gt;&lt;/a&gt; function to copy the static image into our PHP image.&lt;/p&gt;
&lt;p&gt;With this function, we need to specify the destination coordinates, the coordinates of the static image to place at that destination, and the dimensions of the image. This allows us copy in only a part of a static image and stretch/shrink it if desired.&lt;/p&gt;
&lt;p&gt;In this case, the static image I’m setting as the background has the same dimensions as my PHP image, and I want to place the top-left of my static image in the top-left of my PHP image.&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;$backgroundImage&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecreatefrompng&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&#39;./static-image.png&#39;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;p&gt;&lt;span&gt;$destinationX&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$destinationY&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$sourceX&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$sourceY&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;imagecopy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$backgroundImage&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$destinationX&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$destinationY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$sourceX&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$sourceY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$width&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$height&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;For the purpose of pulling in fresh data, I’ve created &lt;a href=&quot;/dynamic-data.json&quot;&gt;a small JSON file&lt;/a&gt; that my website generates whenever it builds, so I’ll grab the data from that file using the &lt;a href=&quot;https://www.php.net/manual/en/function.file-get-contents.php&quot;&gt;&lt;code&gt;file_get_contents&lt;/code&gt;&lt;/a&gt; function and decode the JSON into usable data using the &lt;a href=&quot;https://www.php.net/manual/en/function.json-decode.php&quot;&gt;&lt;code&gt;json_decode&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;$json&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;file_get_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&#39;./generated-data.json&#39;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$data&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;json_decode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$json&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step is to prepare to print out some text, so let’s set the text colour using the &lt;code&gt;imagecolorallocate&lt;/code&gt; function again, set up a reference to a TTF font file, and decide on a font size to use.&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;$textColor&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecolorallocate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;249&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;249&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;249&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$fontFile&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&#39;./my-font.ttf&#39;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$fontSize&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;11&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wow we’re ready to start using the data and print out some text on top of the image using the &lt;a href=&quot;https://www.php.net/manual/en/function.imagettftext.php&quot;&gt;&lt;code&gt;imagettftext&lt;/code&gt;&lt;/a&gt; function. Like with the background image above, we need to pass in some destination coordinates, and we can also print the text at an angle.&lt;/p&gt;
&lt;p&gt;One &lt;q&gt;gotcha&lt;/q&gt; here is that the &lt;code&gt;Y&lt;/code&gt; coordinate of our text represents the text’s &lt;strong&gt;baseline&lt;/strong&gt;, so we need to take into account the height of the text when assigning it’s &lt;code&gt;Y&lt;/code&gt; coordinate.&lt;/p&gt;
&lt;p&gt;In order to make things align nicely, we can use the &lt;a href=&quot;https://www.php.net/manual/en/function.imagettfbbox.php&quot;&gt;&lt;code&gt;imagettfbbox()&lt;/code&gt;&lt;/a&gt; function to figure out how much space the text has taken up. This function returns an array with eight values, representing the coordinates of the bottom-left, bottom-right, top-right, and top-left corners of the bounding box of our text.&lt;/p&gt;
&lt;p&gt;In my case, I want the text to be printed horizontally and slightly-offset from the edge, so I’ll make the angle, &lt;code&gt;X&lt;/code&gt;, and &lt;code&gt;Y&lt;/code&gt; values explicit.&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;$angle&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;p&gt;&lt;span&gt;$latestBlogText&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&#39;Latest Post Date: &#39;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$data&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;latest_post_date&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;$latestBlogSpace&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagettfbbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$latestBlogText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;$x&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$spacing&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$y&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$spacing&lt;/span&gt; &lt;span&gt;+&lt;/span&gt; &lt;span&gt;$latestBlogSpace&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;imagettftext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$x&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$y&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$textColor&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$latestBlogText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;We can use the &lt;code&gt;Y&lt;/code&gt; value of the above text to figure out what the &lt;code&gt;Y&lt;/code&gt; value for our next line of text should be, repeating these steps for each line of text in our image.&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;$blogCountText&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&#39;No. Blog Posts: &#39;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$data&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;blog_count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;p&gt;&lt;span&gt;$blogCountSpace&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagettfbbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$blogCountText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;$y&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$y&lt;/span&gt; &lt;span&gt;+&lt;/span&gt; &lt;span&gt;$spacing&lt;/span&gt; &lt;span&gt;+&lt;/span&gt; &lt;span&gt;$blogCountSpace&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;imagettftext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$x&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$y&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$textColor&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$blogCountText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The last step is to set the &lt;a href=&quot;https://www.php.net/manual/en/functions.header.php&quot;&gt;&lt;code&gt;header&lt;/code&gt;&lt;/a&gt; of our PHP file so it can be interpreted as a PNG image when requested in the browser and output the image we’ve created with the &lt;a href=&quot;https://www.php.net/manual/en/function.imagepng.php&quot;&gt;&lt;code&gt;imagepng&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;
&lt;p&gt;We’ll also use the &lt;a href=&quot;https://www.php.net/manual/en/function.imagedestroy.php&quot;&gt;&#39;imagedestroy&#39;&lt;/a&gt; function to free up any memory used by referencing and creating the two images we’ve dealt with in our code. &lt;em&gt;Note: this function is not required in PHP 8.0.0 and above.&lt;/em&gt;&lt;/p&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&#39;Content-type: image/png&#39;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;imagepng&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;imagedestroy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagedestroy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$backgroundImage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;And that’s it! We can now reference our PHP image in our front end code and treat it like a PNG image.&lt;/p&gt;
&lt;pre data-language=&quot;html&quot; data-language-friendly=&quot;HTML&quot;&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span&gt;src&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;dynamic-image.php&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span&gt;alt&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;...&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span&gt;width&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;300&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span&gt;height&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;80&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
    &lt;img src=&quot;https://api.chrisburnell.com/dynamic-banner.php&quot; alt=&quot;a small banner showing statistical information about content on chrisburnell.com&quot; width=&quot;300&quot; height=&quot;80&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;Keep in mind that the server serving the PHP image &lt;em&gt;does&lt;/em&gt; have to do some computational work in order to output the PNG, and there are some caching considerations that have to be made if the data being presented in the image should be fresh.&lt;/p&gt;
&lt;details&gt;
    &lt;summary&gt;The full code&lt;/summary&gt;
&lt;pre data-language=&quot;php&quot; data-language-friendly=&quot;PHP&quot;&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;?php&lt;/span&gt;
&lt;span&gt;// Set up some variables for later&lt;/span&gt;
&lt;span&gt;$width&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;300&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$height&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;80&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$spacing&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;6&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$angle&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;$x&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$spacing&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;p&gt;&lt;span&gt;// Create the image at our specified dimensions&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$image&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecreatetruecolor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$width&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$height&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;// Enable transparency&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagealphablending&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;// Set a solid colour as the background&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$backgroundColor&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecolorallocate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;6&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;6&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;6&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagefill&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$backgroundColor&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;// Set a static image as the background&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$backgroundImage&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecreatefrompng&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&#39;./static-image.png&#39;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$destinationX&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$destinationY&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$sourceX&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$sourceY&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagecopy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$backgroundImage&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$destinationX&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$destinationY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$sourceX&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$sourceY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$width&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$height&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;// Grab data from a JSON file&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$json&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;file_get_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&#39;./generated-data.json&#39;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$data&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;json_decode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$json&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;// Prepare to print out some text&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$textColor&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagecolorallocate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;249&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;249&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;249&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$fontFile&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&#39;./my-font.ttf&#39;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$fontSize&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;11&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;// Latest Post Date text&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$latestBlogText&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&#39;Latest Post Date: &#39;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$data&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;latest_post_date&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$latestBlogSpace&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagettfbbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$latestBlogText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$x&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$spacing&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$y&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$spacing&lt;/span&gt; &lt;span&gt;+&lt;/span&gt; &lt;span&gt;$latestBlogSpace&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagettftext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$x&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$y&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$textColor&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$latestBlogText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;// Blog Count text&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$blogCountText&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&#39;No. Blog Posts: &#39;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$data&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;blog_count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$blogCountSpace&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagettfbbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$blogCountText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$y&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$y&lt;/span&gt; &lt;span&gt;+&lt;/span&gt; &lt;span&gt;$spacing&lt;/span&gt; &lt;span&gt;+&lt;/span&gt; &lt;span&gt;$blogCountSpace&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagettftext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$x&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$y&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$textColor&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$blogCountText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;// Projects Count text&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$projectsCountText&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;&#39;No. Projects: &#39;&lt;/span&gt; &lt;span&gt;.&lt;/span&gt; &lt;span&gt;$data&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;projects_count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$projectsCountSpace&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;imagettfbbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$projectsCountText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;$y&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;$y&lt;/span&gt; &lt;span&gt;+&lt;/span&gt; &lt;span&gt;$spacing&lt;/span&gt; &lt;span&gt;+&lt;/span&gt; &lt;span&gt;$projectsCountSpace&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagettftext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$angle&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$x&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$y&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$textColor&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$fontFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;$projectsCountText&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&#39;Content-type: image/png&#39;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagepng&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span&gt;imagedestroy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$image&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span&gt;imagedestroy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$backgroundImage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;&lt;/pre&gt;&lt;br&gt;
&lt;/details&gt;&lt;p&gt;&lt;/p&gt;
			
			
			&lt;hr&gt;
&lt;div id=&quot;weblogpomo&quot;&gt;
	&lt;p&gt;This is post #4 of &lt;a href=&quot;/article/weblogpomo2024-wrap-up/&quot;&gt;#WeblogPoMo2024 / Weblog Posting Month 2024&lt;/a&gt;, a one-month challenge to post as often as possible (daily, ideally) for the duration of May 2024!&lt;/p&gt;
	&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20240420081004/https://weblog.anniegreens.lol/weblog-posting-month-2024&quot; rel=&quot;external noopener&quot;&gt;Learn more on Anne Sturdivant’s website&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
			
				&lt;hr&gt;
				&lt;p&gt;Thanks for subscribing and reading this post via RSS!&lt;/p&gt;
				&lt;p&gt;You can read &lt;a href=&quot;https://chrisburnell.com/article/revisiting-php-images/&quot;&gt;this post&lt;/a&gt; and others &lt;a href=&quot;https://chrisburnell.com/posts/&quot;&gt;on my website&lt;/a&gt;.&lt;/p&gt;
		</content>
		
		
	</entry>
	
</feed>
