RSSAmplifier

Blog

Ryan Chandler

Latest blog posts from Ryan Chandler.

ryangjchandler.co.ukRSS feed ↗125 posts

Latest posts

The skills that didn't go anywhere

A while back I wrote about AI anxiety , and I ended it with a line I still believe: your value isn't in writing every line of code, it's in knowing which lines should exist at all. It's the kind of thing that's easy to agree with but much harder to actually feel on a Tuesday afternoon, when you've spent the day describing problems and reading diffs and you're not sure you've built anything. So…

Writing code that feels native to Laravel

Once you become familiar with Laravel, certain patterns start to stand out. Classes get resolved out of the container through facades, you can swap out instances inside of tests, entry points are predictable, configuration lives in familiar places. Laravel does this on purpose, it's all about convention. Consistency is probably the framework's strongest selling point. When a new feature gets…

Overcoming AI anxiety

Writing code pays my bills. It also happens to be a hobby that I was fortunate enough to turn into a career. The satisfaction of solving a problem, the concept of building something from nothing, the small wins that add up over time, the sense of accomplishment. That's what I enjoy. For the longest time that felt like a stable foundation. If you could write good code and build solid software,…

Quickly deploy your Forge site from the terminal

Forge is the way that I deploy most of my projects but I've never been a big user of the official CLI . I instead use a Bash script to deploy my sites from the terminal. I figured it could be useful for somebody else so here's my script. #!/usr/bin/env bash echo "Deploying..." # You can get this from the "Deployments" page in your site settings.…

Easy Stripe webhook testing with Laravel

I've been working on a couple of new side projects recently and integrating with Stripe is super simple thanks to Cashier . Testing webhooks locally normally means using the Stripe CLI and that involves a couple of options to make sure it's configured correctly for your Laravel application and also proxying those webhooks to the correct endpoint. One of the handiest scripts I used whilst working…

2025: A year in review

2025 has officially ended and we are now in 2026. As I've done for the last 6 years, here is a blog post reviewing my year and looking ahead into the next. Open source Just like the last couple of years, the open source side of things has been pretty quiet. I'm still maintaining my packages and keeping them in check. The biggest thing I worked on open source wise is definitely Phiki 2.0 . It was a…

How to download a website for offline viewing with wget

I've been on my fair share of flights recently and as we all know, the Wi-Fi is a little hit or miss. It is getting better thanks to Starlink but it still isn't 100% reliable. I wanted to get some things done on a plane and one of my most visited websites is laravel.com . Despite being a Laravel developer and an engineer at Laravel, there are still things that I have to lookup sometimes:…

Creating custom Facade fakes in Laravel

I recently released a new version of my Cloudflare Turnstile package for Laravel . One of the features I added was the ability to test your Turnstile integrations using a fake implementation of the Turnstile client. it('validates data', function () { Turnstile::fake(); [code! focus] }); Under the hood, the package uses a custom Facade to call methods on the Turnstile client. This facade binds to a…

npm ci vs npm install

When working on a JavaScript project, there are two common commands that are used to install dependencies: npm install and npm ci . While they seem similar, they serve different purposes and can have an impact on your workflow. npm install npm install installs dependencies listed inside of package.json . If the version that gets installed is different to the version found inside of your…

My minimal Ghostty config

I've been using Ghostty as my main terminal for a week or so at this point (late to the party, I know!). I like to keep my tools fairly minimalistic, but consistent. That normally means: A light theme A nice monospace font Breathing room between lines and the window Ghostty's configuration system is insanely simple, especially compared to iTerm's deeply nested input fields. Here's my config and a…

Enabling WAL mode with SQLite in Laravel

SQLite's WAL (Write-ahead Logging) mode improves the reliability and performance of SQLite databases by changing where data is written to. Instead of writing changes directly to the database.sqlite file, they instead get written to a separate log file first. That log file then gets merged into the main file in one go, allowing you to write without blocking reads from the database. It's really easy…

Build your own software

You should build your own software. It's one of the most effective ways of growing as a developer, even if you're just building small, personal tools. Early in the software development journey we're often curious – "how does X work?", "why does Y do this specific thing?", etc. Once we start working full-time and spend less time exploring things, that aspect of software development tends to fade…

Preventing scrollbar layout shifts

Have you ever been on a website and navigated between pages, only to notice that the layout shifts slightly because the scrollbar suddenly appears on longer pages? I have, and it kind of bugs me. It turns out you can add a tiny bit of CSS to your website to prevent this from happening. Better yet? The CSS is actually supported in all major browsers. html { scrollbar-gutter: stable; } Here's a…

Rusty string formatting in PHP

As a PHP developer who likes to occasionally dabble in Rust, I find myself missing the developer experience of the format!() family of macros when I come back to writing PHP. If you're not familiar with format!() or print!() macros in Rust, they're essentially the equivalent of PHP's sprintf() and printf() . The main difference between the two languages is that Rust's macros don't inherit the…

Registering global arguments and options in Laravel Zero

I recently ran into a scenario where I wanted to register a couple of global options inside of a Laravel Zero project. These options need to be available for all commands inside of my project, but I didn't want to extend a custom base Command class. I tried a couple of things and eventually landed on the following: use Illuminate\Console\Application; use…

Saying goodbye to PXP

Over the past couple of years, PXP has been a passion project of mine – something I've poured countless hours into, driven by the idea of pushing the PHP ecosystem and development experience in new and exciting ways. What started as way of learning Rust 3 or 4 years ago eventually became this mammoth of a project, in terms of code and ambition. It's been incredibly rewarding to see the ideas…

Goto, or not to goto go

The goto statement – feared, ridiculed, yet still lurking around in PHP. Newer PHP developers probably don't even know that it exists and those who do often try to pretend that it doesn't. But is goto really as bad as its reputation suggests? The goto statement lets you jump from one part of your code to another. You mark a point in the code with a label and then reference it to jump. goto syke;…

Don't subscribe to complexity

tl;dr Complexity is a silent killer. It sneaks into projects under the guise of best practices, cutting-edge frameworks, and future-proofing. More often than not, it actually causes more problems than it solves. Here's the problem with complexity. Many developers build solutions for problems they don't have yet. They anticipate scaling issues, edge cases, or future requirements that will likely…

2024: A year in review

2024 is coming to a close and as I've done for the last 4 years, I'm here again with a blog post talking about the year and how it's been. Open-source Quite like 2023, I've been quite quiet on the open-source front. I've continued to maintain a few packages and some Filament plugins, but I didn't really do anything too groundbreaking. The PXP project sat there, dormant, for a few months.…

Adding dark mode to my website

After absolutely zero demand, I've added dark mode to my website. The front end is all styled with Tailwind so it didn't take too long, thankfully. Add a couple of dark: classes in various Blade templates and it just works. The one issue I did have was with syntax highlighted code blocks. The light version of the site uses a light theme for syntax highlighting. It's nice and consistent with the…

Fast and powerful syntax highlighting in PHP with Phiki

tl;dr I wrote a syntax highlighter . It's pretty good and I had a tonne of fun working on it. Preface Syntax highlighting is something that we've all had to add to a site before, probably. Whether it's documentation or our personal sites, figuring out what package or service to use can be a nightmare. I faced this problem too. Way back when my personal site used Highlight.php, a PHP port of the…

Embedding Blade inside of Markdown content

If you haven't read my blog post "Revitalising my site for 2025" , I'd recommend going back to read it since I cover some of the cool things I'm doing to make my website perfect for my own workflow. One of the things that I added was the ability to embed Blade template code inside of a Markdown file, allowing me to build custom Blade components for blog posts and then add them into the Markdown…

Revitalising my site for 2025

If you've read some of my posts recently, you might have noticed that this one looks a little different. I've redesigned my site and made some serious upgrades. The previous iteration of my site was built on top of an old Laravel application from about 4 years ago. It was still running on Laravel 10.x and had a load of old, unreachable code that I wanted to get rid of. Now I could have gone…

Setting up the command-line interface (Rebuilding Composer in Rust)

Welcome to my new series of blog posts where I go through the various steps that it takes to rebuild Composer in Rust. This project is purely for fun – I don't intend to release this as an officially supported tool. If you've read some of my more recent blog posts, you can probably tell I'm in my "programming is fun, right?" phase. So, the first thing that I need to do is get a command-line…

Downloading files from the web (Rebuilding Composer in Rust)

Welcome back to the series. If this is your first time here, I recommend starting from the beginning so you don't miss out on cool stuff. In this post, I'm going to explain how composer require works at a very high level and then start to implement the foundations of my own require command. How Composer requires packages So what happens when you run composer require ? I'm glad you asked! Let's say…

Fetching package information from Packagist (Rebuilding Composer in Rust)

Welcome back! If you're here for the first step, I recommend starting from the beginning so you don't get lost. This post is going to focus on interacting with the Packagist API to retrieve information about a given package. To grab information about a package from Packagist, you can use a public API endpoint as below. https://repo.packagist.org/p2/ryangjchandler/blade-cache-directive.json This…

Fun with Blade directives

Let's start this off by explaining how the Blade engine works with a few bullet points. You choose a Blade template to render. The engine uses a series of regular expressions to parse and compile the template. The engine produces a plain PHP file and writes it to disk (so that it's cached for future renders). The PHP file is included and output buffers are used to capture the generated HTML. The…

Some cool SQLite things

Strict tables For those of you who don't know, SQLite is like a dynamically-typed language. There's no restrictions on what you can and can't store inside of a column. That means if you have a INTEGER column in your database table, there's nothing stopping you from storing a string inside of it. That is where strict tables come into play. You can enable "strict mode" when creating your table by…

Using Bunny Fonts in Laravel

I've been using Bunny Fonts for quite a while now, after switching away from Google Fonts due to GDPR requirements and concerns. But here's something that has always bothered me – adding a new font to an existing <link /> tag. You either need to reselect your existing fonts in Bunny's web interface or merge the two generated URLs manually. So I've built a new package for Laravel to solve this…

Disabling Composer's default timeout inside of scripts

I like to use the scripts key inside of my composer.json to write reusable runner commands – normally things like background processes, build scripts, etc. The problem is Composer has a default execution timeout of 30 seconds, so when I've got a long-running process like a queue worker running from a Composer script, it times out after 30 seconds. Thankfully, there is a way to disable the timeout…

Enums in Rust

Rust is my second favourite programming languages. There are lots of things that I love about it and if I could take some of those features I love and squidge them into PHP, I would. One of those features in Rust's powerful enumerations (enums). Yes, we have had enums in PHP since PHP 8.1, but they are more or less just glorified class constants. The only two real benefits they provide over class…

Preserve uploaded file names in Laravel

In a lot of scenarios, I want to maintain the name of a file when a user uploads it to a site, rather than using a randomnly generated one. I sometimes forget the methods I need to call to make this work though, so here is a little code snippet for future me and anyone else who happens to come across this on the internet. $request->file('upload')->storeAs('uploads',…

Get a random element from a JavaScript array

To get a random element from an array, you can use a couple of Math functions to generate a random index and retrieve a value. function random(items) { return items[Math.floor(Math.random() * items.length)] } How it works Math.random() generates a random float between 0 and 1 . The generated float is always less than 1 , so when we multiply it by the length of the array, it's always going to be…

What are UTM tags?

UTM tags, short for "Urchin tracking module" tags, are parameters that can be added to URLs to provide more accurate traffic information to analytics providers. They were initially introduced by the predecessor to Google Analytics named "Urchin", hence the name. Since their introduction, near enough all analytics software provides support for breaking down traffic results by these tags. As…

My favourite Alpine.js plugins

I've been using Alpine.js since it came out and have spent countless hours developing my own plugins and using other peoples plugins, so here's a list of some of my favourites. @ryangjchandler/alpine-clipboard I'll start the list off with a biased choice. This is a package that I built to make copying things to the clipboard super easy. You can use the $clipboard() magic function to copy some text…

Removing icons from table actions in Filament

I'm not a huge fan of adding icons to actions inside of my Filament tables. The default set of actions that ship with Filament, such as EditAction and DeleteAction , come pre-configured with sensible icons from the Heroicons icon pack. They're just not to my taste and become somewhat of a visual distraction, especially when they're repeated across all rows and those rows already have a good amount…

2023: A year in review

2023 has been a bit of a weird year for me. The last couple of years I've spent a tonne of time on open-source projects, tweeting, and writing blog posts. 2023 was kind of a quiet one on that front. Programming In terms of the stuff that I did work on: Developed a couple of Filament plugins. Started building a static analyser and language server for PHP & PXP projects. Started re-writing the…

How to find your php.ini file

What is the php.ini file? PHP uses an INI file for all of it's configuration. It gets loaded when PHP starts up and is used to configure extensions and various PHP features. How you locate your php.ini file all depends on what sort of access you have to your server. I'll detail a couple of different methods below. Command-line access If you have access to php through the command-line (inside of…

Autocomplete Tailwind classes inside of Blade class directives

Tailwind's Intellisense plugin for Visual Studio Code is great, but as somebody who uses Laravel Blade I am slowed down massively when writing Blade components or dynamic CSS class lists with the @class directive. If you open up your settings.json file inside of Visual Studio Code, you can use an experimental feature in the Tailwind extension to get autocomplete inside of $attributes->class() and…

Scroll to the first error message with Livewire

When submitting a form with Livewire, you probably want to render out some error messages and show them to your user. A nice enhancement is automatically scrolling to the first error message on the page. First, make sure that the element that renders the error message has some sort of "marker" class or data attribute that marks it as an error message. @error('name') <p class="error-message…

Understanding serialisation in PHP

Serialisation, or serialization, is a process that takes in a piece of data and generates a storable or transportable representation of the data. The serialised representation of data can be achieved with various formats. JSON is very common as most languages have some form of JSON encoding and decoding. You could also use XML, YAML or even a raw string of bytes. You're probably familiar with…

Rendering & Echo (Build your own template engine in PHP)

Let's build a tiny template engine for PHP! This post will focus on rendering the template and echoing out data that can be escaped with htmlspecialchars() . Before we start writing code we need to take care of the most important part of any programming project — giving the project a name. I'm going to call it Stencil. The templates themselves will all be plain PHP. We won't be creating any…

Write a lexer in PHP with Lexical

I recently released a new PHP package called "Lexical". It provides a set of attributes and objects to build regular expression based lexers in PHP. If you want to skip the blog post and go straight to the package, the repository is public on GitHub . As somebody who enjoys experimenting with parsers and text processing, writing lexers has become a somewhat regular task for me. The first step in…

Minimal container (Build your own service container in PHP)

In this mini series, you'll learn how to build your own service container for dependency injection in PHP. I'll start with the simplest PSR-11 compliant container and then add various features until we have a powerful, general purpose container. What is a "service container"? A service container is a PHP object that is responsible for the instatiation of other objects. You tell the container how…

Let's write an "esolang" in PHP

An esolang (esoteric language) is a programming language designed to be used as a joke or test the limits of what can be achieved with a particular language syntax. In this post, I'll show you how to write your own esolang similar to Brainf*ck in PHP. Syntax Brainf*ck is a minimal esolang and the original implementation only consisted of 8 symbols — > , < , + , - , . , , , [ and ] . The language…

PXP Progress Report #1

After a busy month or so moving house and working, I've picked the project back up and figured a progress report was worth writing since there are a lot of people interested in the project. The first and most important update - the project isn't dead! It's still very much alive, but time is precious resource and since this is just a hobby project it's hard for me to justify spending lots of time…

Automatically running commands in Visual Studio Code projects

Modern web development generally involves running various scripts in your terminal to build assets, analyse your code and more. It's incredibly annoying to run those commands manually everytime you open a project. Thankfully it's possible to automate that process in Visual Studio Code using tasks.json file. Let's say that I'm working on a Laravel project and want to have Vite running. The command…

Applying a colored overlay to a background image

I recently worked on a project that had a background image covering the entire body of the page. One of the differences was that the background also needed a colored overlay, in this case a black overlay with slight opacity. I would normally do this using an absolutely positioned element stretching to all 4 corners of the page with a z-index lower than the <main> content of the page, but then you…

Blazingly fast Markdown parsing in PHP using FFI and Rust

Parsing and rendering Markdown as HTML is very common on the modern web. The content found on this very blog is written in Markdown and rendered to HTML using the league/commonmark package and a few plugins. I'm currently in the process of writing a book to accompany a video course. All of the content for that book is also written in Markdown, but I've started to notice a slowdown in build speed…

Disabling Composer's script process timeout

For repetitive processes in my projects, I typically use Composer's script functionality to make running scripts and executing binaries simpler for myself and my team. One thing that I run into often is long-running processes, such as watcher scripts or background processes, exceeding Composer's default script timeout of 300 seconds. An example of where I see this most often is large test suites…