This is an experiment using the WebAssembly version of Trealla Prolog and Spin to host websites. It is a retrofuturistic combination of Prolog/C and Rust/WebAssembly.
Status: Slowly approaching stability? Still in cowboy mode. 🤠
Head on over to our beautiful homepage at php.energy. Source code examples included.
Reconsider whether you really want to do this.- Install Spin.
- Clone this repo.
- Configure the www root in
spin.toml
- Put PHP scripts or Prolog programs in
public_html
.
- Run server with
make
orspin up
. - Or use
make watch
for hot reloading.
As of recently, you can deploy to the beta version of Fermyon cloud for free. Neat. More info here.
- Get set up
spin deploy
See: Spin docs on OCI images. Or MacGyver an image with nixpacks and make container
(or make ARCH=aarch64 container
for ARM).
It just runs the WebAssembly version of Trealla Prolog and writes CGI (RFC 3875) output to stdout
handles the Spin HTTP wasm component.
See the README in the www folder for more info on the file structure.
This currently uses the CGI mode of Spin, but with a little bit of effort we could use the fancy APIs and get outgoing HTTP and Redis and whatnot. (Done?)
It'd be cool to get some kind of magic persistence going.
Put PHP templates here and they will show up in your root.
Files ending in .html
will be interpreted as PHP templates with the following special syntax.
<?- current_prolog_flag(version, Ver), write(Ver) ?>
<?php ... ?>
You can use the <?- Goal. ?>
(alias: <?php Goal. ?>
) expression to execute Prolog (of course).
By default, all output from these blocks will be escaped to avoid XSS attacks. You can also use unsafe queries, see below.
You can use the <?unsafe Goal. ?>
expression to execute Prolog code without escaping its output (dangerous!).
Avoid using this if you can.
For example, this renders a table of the numbers 1-10 and their squares:
<h3>Math</h3>
<table>
<tr><th>N</th><th>N²</th></tr>
<?unsafe
bagof([N, Square], (between(1, 10, N), Square is N^2)), Flags),
maplist(format("<tr><td>~w</td><td>~w</td></tr>"), Flags)
?>
</table>
<?if current_prolog_flag(dialect, X). ?>
You are using: <?=X ?>
<?end ?>
Conditionally executes the block if Goal succeeds. Only runs once.
<table>
<?findall current_prolog_flag(Key, Value). ?>
<tr><td><?=Key ?></td><td><?=Value ?></td></tr>
<?end ?>
</table>
Works like if blocks, but with findall behavior.
<?* member(X, [1, 2, 3]), write(X). ?>
Works the same as query, but findall behavior instead of once behavior.
1+1 = <?=X X is 1+1. ?>
<input type="text" name="ask" value="<?=Param query_param(ask, Param) ?>">
Works the same as query, but echoes (writes) the variable bound to Var. Escapes output.
<?
% declare rules and facts
best_web_framework(php).
good_enough(X) :- between(1, 640, X).
% "directives" get executed upon evaluation
:- echo("hello!").
:- succ(68, X), write(X).
?>
The web framework of the future is <?=Framework best_web_framework(Framework). ?>
Assert facts and rules as if consulting a Prolog program. Directive syntax will call the given goal.
<?prolog ... ?>
is an alias for this.
See: spin.pl.
:- module(spin, [
% Inbound HTTP
% Request routing
http_handler/4,
% Request info
current_http_uri/1, current_http_method/1, current_http_body/1,
current_http_param/2, current_http_header/2,
% Response output
http_header_set/2,
http_body_output/1,
html_content/0, html_content/1,
text_content/0, text_content/1,
prolog_content/0, prolog_content/1,
% Outbound HTTP
http_fetch/3,
% Key-value store
store_open/1, store_open/2,
store_close/1,
store_get/3, store_exists/2,
store_keys/2,
store_set/3, store_delete/2,
% PostgreSQL
postgres_open/3,
postgres_open_url/2,
postgres_execute/4,
postgres_query/5,
% SQLite
sqlite_open/2,
sqlite_query/5,
sqlite_close/1,
...
]).
PHP grammar.
phpinfo.
Writes HTML output, dumping the current environment and flags.
render(+Filename).
Renders the given PHP file.
html_escape(-Raw:string, +Sanitized:string).
Escapes string using htmlspecialentities//1
.
Why not?