r/PHP 2h ago

Article Thoughts about TypePHP

Thumbnail stitcher.io
Upvotes

r/PHP 16h ago

I built an attendance tool in 2023 to escape a paper register. I rewrote it last week, and past me made four real mistakes.

Upvotes

In 2022 I started teaching programming to schoolchildren. On the first day I was handed the curriculum for the web track. Nobody could tell me who had written it. After a full year of lessons, the students were supposed to produce an HTML page with a background image and some pictures on it.

I threw it out and wrote a new one.

The other half of the job was the register. Paper, one sheet per child. On every sheet you marked attendance, wrote in the topic of the lesson and the number of hours. That was every group, every week, by hand.

At some point I had enough and built something. It was 2023, I knew PHP, and I wrote it the way you write things at eleven at night: procedural, one file per action. Some colleagues started using it too. Five commits in a single day, two of them named "Add files via upload".

Last week I opened that repository for the first time in three years.

What past me got right

More than I expected. Passwords went through password_hash and password_verify. Eighty-nine prepared statements, and not one place where $_GET was pasted into SQL. For something written to stop filling in paper, that is not bad.

What past me got wrong

htmlspecialchars appears zero times in the whole codebase. Every value from the database goes straight into the page.

The upload handler takes the extension from the filename the browser sent and writes the file into a served directory. You can upload a .php file and then request it. That is remote code execution.

Attendance was stored as (student_id, date, status). No group. A student who came to two different tracks was one row per day, and there was no way to say which of the two they had missed.

A student had a single group_id, so anyone attending two tracks could not be recorded at all.

Teachers and students lived in two tables with two separate login handlers. Anything that concerned both had to be written twice.

The rewrite

Laravel 13 and Postgres. The domain survived and nothing else did.

The interesting part is what the 2023 version never had: tasks that grade themselves.

I do not run student code. It goes to Wandbox, which has been running untrusted code for years and does nothing else. On a project this size, running my own sandbox would mean one person keeping isolation correct in their spare time, and I would rather that person were not me.

I expected to stitch several backends together, the way these things usually go: Wandbox for most languages, the Go playground for Go, the Rust playground for Rust. Then I read the Go playground's compile endpoint. It takes the program body and a version. There is no field for standard input. I posted stdin and input alongside the program anyway, in case the field simply went undocumented, and the program read EOF both times.

Without stdin there is no test case with input data, and without that there is no assessment worth the name. Every task collapses into "print this constant". So I use one backend. Wandbox covers fourteen languages, Rust, Pascal and SQL included.

Grading runs on a queue, because I measured it. One Go build on Wandbox takes about twenty seconds. Five test cases one after another would be a minute and a half. I send them in parallel, four at a time, which got it down to sixty-two seconds. Still far too long to hold an HTTP request open, so submitting queues the work and the page polls for the result.

Some test cases are hidden. For open cases the student sees the input and the expected answer. For hidden ones they see only whether it passed. Show everything and the solution gets fitted to the known answers instead of made to work.

Three findings that cost me an afternoon each

Wandbox's Rust compiler rejects the warning option set, and does not say so. It returns exit status 1 with empty output, which is indistinguishable from a program that printed nothing.

Mono prints non-ASCII as question marks until you set Console.OutputEncoding, and once you do, it prefixes the output with a byte order mark. Comparing that against the expected answer fails on an invisible character. The dotnetcore image on Wandbox does not build at all, so mono is the only option.

Laravel's trans_choice() falls back to the fallback locale in cases where __() returns the key unchanged. My source language is Russian and my fallback was English, so Russian pages rendered English text in exactly the places where a count was involved. No test caught it; I saw it in a screenshot.

What I am not sure about

Leaning on a free public service at the moment a student submits an exam is the obvious weak point, and I do not have a good answer for the day Wandbox is down. My reasoning is that the alternative is worse for a project this size. If you have run the other way and maintained your own isolation, I would like to hear how that went.

MIT, Laravel 13, PHP 8.3+, Postgres, no keys or accounts needed to try it. The seed builds a demo school with four tracks and a month of attendance.

https://github.com/dripips/ItCube


r/PHP 22h ago

Foundation YouMeOS Microverse: Running FrankenPHP + SQLite as an embedded, zero-Docker desktop runtime

Upvotes

Hey r/PHP, I wanted to share a practical implementation of FrankenPHP and native SQLite powering a self-hosted personal WebTop: YouMeOS Microverse. A major problem with self-hosted PHP applications is distribution: telling non-technical users to install Docker, Nginx, and MySQL creates massive friction.

How We Utilized FrankenPHP:

  • Embedded Desktop Engine: In addition to standard Docker Compose, our Electron desktop app bundles a portable FrankenPHP binary with SQLite.
  • Zero External Dependencies: Users download the .exe, .dmg, or .AppImage, and it runs a local Caddy + PHP 8.3 server process in the background without Docker or local PHP installations.
  • Resource Footprint: Cold starts take ~1 second, with idle RAM usage significantly lower than traditional Apache/Nginx + PHP-FPM + MySQL stacks.
  • Caddy Worker Integration: Takes advantage of FrankenPHP's fast application handling directly from local storage.

Livesite: www.youmeos.com but the microverse can be ran locally using our docker container or binaries.

Source code and configs: https://github.com/YouMeOS/youmeos-microverse
Release builds: https://github.com/YouMeOS/youmeos-microverse/releases

Feedback on our FrankenPHP runner and Caddyfile setup is welcome.


r/PHP 14h ago

mousr/templates: Static analysis-friendly native PHP templates

Upvotes

Because PHP templates are back! https://github.com/mousr/templates


r/PHP 2h ago

Symfony Messenger vs Ecotone: The Real Difference

Thumbnail blog.ecotone.tech
Upvotes

r/PHP 18h ago

Discussion HQTUI — Terminal UI for TypeScript, Rust, Go, Python and Zig

Thumbnail hqtui.com
Upvotes