A Bukkit/Spigot plugin that embeds JPHP (a PHP-to-JVM compiler), letting you write Minecraft plugin logic in PHP.
TL;DR: Lower your expectations, shoot cows.
This project is really just an experiment born out of curiousity on whether I could implement the long-abandoned JPHP project and make it into a Bukkit plugin. As such, this is by no means a plugin you should go ahead and install on your server "just cuz". There will be bugs, as I didn't concern myself with the user experience, just whether I could do it or not. I did end up implementing various survival-friendly mechanics, like taking the enchantments on your bow into account and creating a crafting recipe for the arrows, but at the root of it all was just my curiousity.
This experiment runs on 1.20.4, that's all I tested it on. I used the Java version I had installed so as long as you have a JDK 17-compatible Minecraft server running, you should be good to go. I used the latest 1.20.4 Paper build (#499).
Both methods require Java 17+.
./gradlew shadowJarYou really should just use the Simple™️ method, this is just added cuz I really like DevEnv xd..
Anyway, install DevEnv.
Then, do the same thing as with the Simple Method, but longer:
devenv shell -- gradle shadowJarDevEnv really only starts to shine if you want to manage dependencies in one place and develop your own projects.. on maybe multiple devices? Idk but it's cool tho!
Output: build/libs/cowcannon-<version number>.jar
CowCannon bootstraps jphp inside CowCannonPlugin.onEnable(), registers a set of bukkit\* PHP wrapper classes, then executes php/main.php. Closures registered in that file are stored in-memory and dispatched by CowCannonEventListener when Bukkit events fire.
So basically, for every action/API you want to use which is native to Bukkit, you have to create a wrapper which exposes it so that PHP can use it.
Here's the main.php!
Tests are written in PHP and live in src/test/resources/php/. An "artisanal" TestRunner class (no PHPUnit cuz JPHP is a potato) collects pass/fail results. PhpTestHarness.java bootstraps a full JPHP environment with a mock Bukkit facade, loads the real main.php, then loads each test file. JUnit 5 surfaces each PHP test as a named dynamic test so results show up normally in your test runner.
Tests also always run when compiling the final jar, so either it works or it works.. Good luck!
./gradlew testI couldn't believe that the tests I wrote actually all passed on the first try, so I made some which would deliberately fail as a check that my TestRunner class wasn't just hyping me up for nothing. I didn't want to remove them because they were a valuable learning experience, so I spent some time in the Gradle docs figuring out if and how I could add a custom argument so that the sanity checks could be triggered conditionally.
Here's how to trigger/verify your own sanity:
./gradlew test -PsanityTests| Class | Description |
|---|---|
bukkit\Bukkit |
Static utility — register commands and event callbacks |
bukkit\CommandSender |
Wraps CommandSender |
bukkit\Player |
Wraps Player |
bukkit\Entity |
Wraps Entity |
bukkit\Location |
Wraps Location |
bukkit\Vector |
Wraps Vector |
bukkit\World |
Wraps World |
