Back to Blog
TypePHP on Mac: Compiling hello.php With tpc (And Where It Stops)
On Apple Silicon, default TypePHP bin mode stalls without embed PHP. With PHPX built and --nano + swoole/php-nano, hello.php compiles and runs. Here’s the honest path.
Back
Engineering
Table of contents
TypePHP on Mac: Compiling hello.php With tpc (And Where It Stops)
In the previous post I mapped TypePHP as an AOT compiler, not OPcache, and not “PHP becomes Go.” This one is the next question every PHP developer asks after the theory:
Can I actually compile hello.php on my Mac?
I tried that on 18 Sep 2026, on Apple Silicon, against TypePHP v0.9.0. Short version: the default binary path dies without PHP’s embed SAPI. The Nano path works.
Claims below are checked against the swoole/typephp README, the libphp installer doc, and a live Mac trial on that date.
Quick answer
| Step | Result on my Mac |
|---|---|
| PHP 8.4–8.5 available | Yes (Herd 8.4.23 day-to-day; Homebrew 8.5.2 for this trial) |
composer require --dev swoole/typephp | Works (pulls v0.9.0) |
vendor/bin/tpc.php --help | Works |
Official hello.php + main() shape | Clear from docs |
Build PHPX (libphpx.dylib) from Composer tree | Works (cmake) |
Prebuilt tpc_v0.9.0_macos_arm64.tar.gz | Fails at launch: dyld / @rpath/libphpx.dylib |
Default vendor/bin/tpc.php hello.php (bin / embed) | Fails: no libphp.dylib, no sapi/embed/php_embed.h (Homebrew PHP is CLI-only here; auto libphp builder is Linux-only) |
composer require swoole/php-nano + tpc.php hello.php --nano | Works → ./hello prints Hello World |
What the docs want you to run
The README Quick Start is simple on paper.
Create hello.php:
php
<?php
function main(): void
{
echo "Hello World!\n";
var_dump(PHP_VERSION);
var_dump(php_uname());
}Then compile and run (from a source checkout):
bash
bin/tpc.php hello.php
./helloOr, as a Composer dependency:
bash
composer require --dev swoole/typephp
vendor/bin/tpc.php hello.phpTwo rules matter more than the shell commands:
- Binary mode needs a global
main(). No top-level executable statements. Code belongs in a function or method. mainreturn type isvoid. It may take no args, ormain(int $argc, array $argv).
The Mac setup that actually worked
I ended up with this env (Herd’s php and Homebrew’s php-config disagree on version; TypePHP refuses that mismatch):
bash
export PATH="/opt/homebrew/opt/php/bin:/opt/homebrew/opt/php/sbin:$PATH"
hash -r
php -v # 8.5.2
php-config --version # must match
export PHP_HOME="$(php-config --prefix)"
export PHPX_HOME="$PWD/vendor/swoole/phpx"1. Composer + PHPX
bash
mkdir -p ~/tmp/typephp-try && cd ~/tmp/typephp-try
composer require --dev swoole/typephp
cd vendor/swoole/phpx
mkdir -p build && cd build
cmake ..
cmake --build . -j"$(sysctl -n hw.ncpu)"
cd ../../..
ls vendor/swoole/phpx/lib/libphpx.dylibYou also need CMake and the usual math libs on PATH (brew install cmake gmp mpfr). On this machine Homebrew PHP itself needed a couple of missing shared libs before php would even start (capstone, net-snmp). That is a local Homebrew hygiene issue, not TypePHP’s fault, but it blocked the trial until fixed.
2. Nano compile (the path that shipped a binary)
Default bin mode wants embed PHP. Homebrew’s bottle here only exposed a cli SAPI under include/php/sapi. TypePHP’s automatic libphp builder is documented for Linux interactive terminals, not macOS. So I switched to Nano:
bash
composer require swoole/php-nano
vendor/bin/tpc.php hello.php -f --nano
./helloReal output from this machine:
text
Hello World!
string(5) "8.5.2"
string(8) "PHP Nano"file ./hello reported a Mach-O 64-bit arm64 executable (~1.7MB after link). php_uname() printing PHP Nano is the tell that you are on the Nano runtime, not a full embed libphp binary.
Paths that failed (keep these in mind)
Prebuilt macOS arm64 tpc
bash
gh release download v0.9.0 --repo swoole/typephp --pattern 'tpc_v0.9.0_macos_arm64.tar.gz'The tpc binary aborted immediately:
text
dyld: Library not loaded: @rpath/libphpx.dylibSearch paths still pointed at CI / Homebrew ZTS layouts that are not inside the tarball. Treat “download the Mac build and go” as broken for v0.9.0 until packaging includes a matching PHPX (or fixed rpaths).
Default bin mode without embed
With PHPX present but no embed:
text
The `libphp.dylib` is not found
...
fatal error: 'sapi/embed/php_embed.h' file not foundThat matches the project docs: executable / shared-library modes need the PHP Embed SAPI. Extension mode (-m ext) is a different story and was not the goal of this hello trial.
Herd vs Homebrew php-config
Day-to-day which php was Herd 8.4.23. which php-config was Homebrew 8.5.2. TypePHP throws:
text
php-config reports PHP 8.5.2, but the running PHP is 8.4.23.
Set PHP_HOME to the matching PHP installation.Herd’s bin folder has php / php84 / FPM, but no php-config or phpize. Herd’s build also uses --disable-embed. For this trial, forcing Homebrew PHP first on PATH was the clean fix.
Why main() still matters
Even before the linker, TypePHP wants an explicit main, the same way a C program does:
- CLI tools and workers map cleanly to
main - A typical Laravel request lifecycle does not
- Top-level statements are a smell here, not a feature
Nano does not change that contract. It changes which runtime you link against.
My take
TypePHP’s Quick Start reads like a five-minute demo. On Mac with v0.9.0, plan for layers:
- One consistent PHP install with matching
php+php-config(Homebrew was easier than Herd for this) - Build PHPX yourself (
libphpx.dylib) - Either build embed PHP (painful on Mac; auto-installer is Linux-first) or use
--nano+swoole/php-nanofor a first binary
I am not writing TypePHP off. The compiler is real, the errors are specific, and ./hello on Apple Silicon is now a checked fact on my machine. I am also not pretending default bin mode is “brew install and done” on macOS yet.
If you are evaluating TypePHP for work: get a Nano hello working first, keep Laravel/FPM as the default app stack, and treat full embed binaries as a separate toolchain project (or prefer Linux until the Mac story is smoother).
Sources
- TypePHP README: Requirements, Installation, Quick Start (binary
main()rules) — github.com/swoole/typephp, checked 18 Sep 2026 - TypePHP libphp installer notes (Linux interactive auto-build; not used successfully on this Mac) —
docs/en/LIBPHP_INSTALLER.md, checked 18 Sep 2026 - Release
v0.9.0assettpc_v0.9.0_macos_arm64.tar.gz— GitHub Releases, downloaded 18 Sep 2026 - Live trial: Composer
swoole/typephp^0.9.0 +swoole/php-nano^1.0 on Apple Silicon; PHPX cmake build; successfulvendor/bin/tpc.php hello.php -f --nanoand./hellooutput above (18 Sep 2026) - Laravel Herd PHP 8.4.23 on this Mac: no
php-configin Herd bin; configure includes--disable-embed(checked 18 Sep 2026)
Comments
No comments yet
Loading comments...