Documentation
¶
Index ¶
- func NewException(message string) string
- type Context
- type ExitError
- type ExprCache
- type IncludeCache
- type IncludeFunc
- type Options
- type Runtime
- func (rt *Runtime) Const(name string) (any, bool)
- func (rt *Runtime) Eval(e model.Expr, scope *Scope) (any, error)
- func (rt *Runtime) Exit(code int) error
- func (rt *Runtime) FS() fs.FS
- func (rt *Runtime) IncludedFiles() []string
- func (rt *Runtime) Load(src string) (*model.Program, error)
- func (rt *Runtime) LoadFile(path string) (*model.Program, error)
- func (rt *Runtime) OnError(fn func(error))
- func (rt *Runtime) RegisterClass(c *model.Class)
- func (rt *Runtime) RegisterConstructor(name string, ctor any)
- func (rt *Runtime) RegisterFunc(name string, fn any)
- func (rt *Runtime) Run(p *model.Program) error
- func (r *Runtime) SAPI() string
- func (rt *Runtime) SetConst(name string, val any)
- func (rt *Runtime) SetContext(ctx context.Context)
- func (rt *Runtime) SetExprCache(cache *ExprCache)
- func (rt *Runtime) SetGlobal(name string, val any)
- func (rt *Runtime) SetIncludeCache(cache *IncludeCache)
- func (rt *Runtime) SetIncludeResolver(fn IncludeFunc)
- func (rt *Runtime) WorkDir() string
- func (rt *Runtime) WritablePaths() []string
- type Scope
- type Transpiler
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewException ¶
NewException returns message as the native Exception value.
Types ¶
type Context ¶
type Context struct {
Get map[string]string
Post map[string]string
Path map[string]string
Headers map[string]string
// contains filtered or unexported fields
}
Context carries the per-request state extracted from an *http.Request and exposes it to transpiled PHP. It is the bridge between Go's HTTP layer and the PHP execution space: the README's "$_SERVER gray area" made concrete and scoped to one request.
The maps mirror PHP's request superglobals:
- Get -> $_GET (URL query string parameters)
- Post -> $_POST (form body parameters)
- Path -> route path values, e.g. "/users/{id}" with id => "42"
Headers holds the incoming request headers (canonical name -> value) so PHP code can read them via getallheaders(); response holds headers staged by the PHP header() function for the host to flush onto the http.ResponseWriter.
The whole "API" the README wants forwarded into go-expr lives on this object: Register wires the request-aware functions (getallheaders, header) into a Runtime and seeds the superglobals, so PHP can call them and read the values from the Go execution space directly (see runner.Eval / RegisterFunc).
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/titpetric/phpscript/runner"
)
func main() {
req := httptest.NewRequest(http.MethodGet, "/users/42?tab=profile", nil)
req.Header.Set("X-Request-Id", "abc123")
req.Pattern = "GET /users/{id}"
req.SetPathValue("id", "42")
ctx := runner.FromRequest(req)
ctx.Header("X-Powered-By: phpscript")
fmt.Println(ctx.Get["tab"])
fmt.Println(ctx.Path["id"])
fmt.Println(ctx.Headers["X-Request-Id"])
fmt.Println(ctx.ResponseHeaders().Get("X-Powered-By"))
}
Output: profile 42 abc123 phpscript
func FromRequest ¶
FromRequest builds a Context from an HTTP request. Query and form values are flattened to their first value (PHP's scalar superglobal shape); path values are pulled out of the matched ServeMux pattern via r.PathValue.
func (Context) GetAllHeaders ¶
GetAllHeaders implements PHP getallheaders(): an associative array of the incoming request headers keyed by canonical header name.
func (Context) Header ¶
Header implements PHP header($header[, $replace[, $code]]): it parses a "Name: value" line and stages it on the response header set. replace controls whether an existing header of the same name is overwritten (default true).
func (Context) Register ¶
Register installs the request-aware PHP functions onto rt and seeds the request superglobals. After this, transpiled PHP can call getallheaders() / header() and read $_GET, $_POST, $_PATH — all backed by this Context.
func (Context) ResponseHeaders ¶
ResponseHeaders returns the headers staged by the PHP header() function so a host handler can copy them onto the http.ResponseWriter after execution.
type ExitError ¶
type ExitError struct {
Code int
}
ExitError is returned when PHP die()/exit() interrupts script execution.
type ExprCache ¶
type ExprCache struct {
// contains filtered or unexported fields
}
ExprCache stores compiled expression programs by AST expression node and by transpiled expr source. The node cache is the fastest path for reused parsed programs. The source cache lets freshly parsed but identical PHP expression trees reuse compiled expr bytecode across load+execute cycles.
func NewExprCache ¶
func NewExprCache() *ExprCache
NewExprCache returns an empty compiled expression cache.
type IncludeCache ¶
type IncludeCache struct {
// contains filtered or unexported fields
}
IncludeCache stores parsed include/require programs by cleaned filesystem path. Parsed programs are treated as immutable by Runtime.Run/exec: hoisting copies declarations into runtime maps, while statement execution only reads the AST, so cached *model.Program values can be shared safely by callers that do not mutate ASTs themselves.
func NewIncludeCache ¶
func NewIncludeCache() *IncludeCache
NewIncludeCache returns an empty parsed include cache.
type IncludeFunc ¶
IncludeFunc resolves an include/require path to a parsed program. Wiring this from the host keeps the runner free of file-system and parser dependencies.
type Options ¶
type Options struct {
// RootFS is the filesystem used to load PHP entrypoints and includes.
RootFS fs.FS
// SAPI provides output for `php_sapi_name`.
SAPI string
// WorkDir is the directory inside RootFS used as the script working directory.
// Empty means the RootFS root.
WorkDir string
// WritablePaths optionally restricts filesystem writes. When empty, writes are
// left to normal OS/user permissions. Enforcement is done by filesystem shims.
WritablePaths []string
}
Options configures a Runtime.
type Runtime ¶
type Runtime struct {
// contains filtered or unexported fields
}
Runtime is the abstraction over the expr-lang VM. It owns everything the transpiled expressions need to run:
- the output sink (echo target: stdout/stderr for CLI, a buffer for HTTP),
- the symbol table of forwarded/registered functions (the "bring your own stdlib" mechanism from the README — register_function),
- the class table (resolved ClassDecls), and
- an optional error handler (register_error_handler).
A Runtime evaluates a model.Expr by transpiling it to expr source, assembling an env (helpers + functions + the current scope's variables) and running it through a cached *vm.Program. Statements (control flow, mutation) are driven by the interpreter in runner.go, which calls back into Eval for leaf values.
func (*Runtime) Eval ¶
Eval transpiles e, binds the referenced variables from scope, and runs the resulting program through the expr-lang VM.
func (*Runtime) IncludedFiles ¶
IncludedFiles returns the cleaned dirFS filenames included by this runtime.
func (*Runtime) OnError ¶
OnError installs an error handler (register_error_handler). When set, runtime evaluation errors are routed here instead of aborting the caller.
func (*Runtime) RegisterClass ¶
RegisterClass adds a resolved class to the class table so `new Name` works.
func (*Runtime) RegisterConstructor ¶
RegisterConstructor binds a class name to a Go constructor so `new Name` in PHP instantiates a native Go value. The constructor may take a leading context.Context (auto-injected) and may return a trailing error, which is surfaced to the interpreter as a thrown error. Example:
rt.RegisterConstructor("Storage", func(ctx context.Context) (Storage, error) { ... }).
// PHP: $storage = new Storage; // == storage, err := NewStorage(ctx).
func (*Runtime) RegisterFunc ¶
RegisterFunc forwards a Go function (or any callable) into the VM under name. This is the shim mechanism: e.g. rt.RegisterFunc("strlen", func(s string) int { return len(s) }) makes `strlen($x)` work in transpiled code.
func (*Runtime) SetConst ¶
SetConst registers a PHP constant (e.g. define("FOO", 1) or a built-in like T_VARIABLE). Constants are visible in every scope, including inside functions and methods — unlike globals, which PHP confines to the global scope.
func (*Runtime) SetContext ¶
SetContext installs the lifecycle context auto-injected into registered Go callables whose first parameter is a context.Context (constructors, methods, functions). Defaults to context.Background().
func (*Runtime) SetExprCache ¶
SetExprCache installs a shared compiled-expression cache. Passing nil disables AST-node expression caching for this runtime.
func (*Runtime) SetGlobal ¶
SetGlobal seeds a variable into the global scope before execution. Useful for injecting request data (the README's $_SERVER gray area) or, in tests, an input value.
func (*Runtime) SetIncludeCache ¶
func (rt *Runtime) SetIncludeCache(cache *IncludeCache)
SetIncludeCache installs a shared include cache. Passing nil disables include caching.
func (*Runtime) SetIncludeResolver ¶
func (rt *Runtime) SetIncludeResolver(fn IncludeFunc)
SetIncludeResolver installs the include/require resolver.
func (*Runtime) WritablePaths ¶
WritablePaths returns the configured writable path whitelist.
type Scope ¶
type Scope struct {
// contains filtered or unexported fields
}
Scope is a flat variable table for one execution frame.
PHP has no block scoping: variables introduced inside if/for/foreach bodies live in the enclosing function scope. Each function call gets a fresh Scope; the file body runs in the global Scope.
There is intentionally no `global` keyword implemented.
type Transpiler ¶
type Transpiler struct {
// contains filtered or unexported fields
}
Transpiler lowers a model.Expr tree into a go-expr (expr-lang) source string.
Why transpile at all instead of evaluating the AST directly? expr-lang is a fast, sandboxed, Go-native expression VM. By emitting expr source for the "pure expression" parts of PHP we get, for free:
- operator semantics and precedence,
- safe, terminating evaluation,
- direct invocation of Go functions/methods on values from the stack (the whole point noted in the README — PHP's huge stdlib becomes a set of forwarded Go symbols rather than a reimplementation).
expr-lang is expression-only: no loops, no mutation, no statements. Those live in the runner's statement interpreter (runner.go). The transpiler also never tries to encode PHP-specific value semantics inline; instead it emits calls to a small set of runtime helpers (see helpers.go) so the generated expression stays type-agnostic:
$a . $b -> __concat(v_a, v_b)
$arr[$k] -> __index(v_arr, v_k)
$obj->field -> __get(v_obj, "field")
$obj->m($x) -> __call(v_obj, "m", v_x)
new Foo($x) -> __new("Foo", v_x)
array(1, "k"=>2) -> __array(__pair(null, 1), __pair("k", 2))
Variables are referenced as v_<name> and supplied through the expr env at run time, which is also how registered/forwarded functions become callable.
Example ¶
package main
import (
"fmt"
"github.com/titpetric/phpscript/model"
"github.com/titpetric/phpscript/runner"
)
func main() {
t := runner.NewTranspiler()
src, vars, err := t.Transpile(&model.Binary{
Op: ".",
Left: &model.Var{
Name: "greeting",
},
Right: &model.Lit{
Value: " world",
},
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(src)
fmt.Println(vars)
}
Output: __concat(v_greeting, " world") [greeting]