runner

package
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewException

func NewException(message string) string

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

func FromRequest(r *http.Request) Context

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 NewContext

func NewContext() Context

NewContext returns an allocated empty Context value.

func (Context) GetAllHeaders

func (c Context) GetAllHeaders() *model.Array

GetAllHeaders implements PHP getallheaders(): an associative array of the incoming request headers keyed by canonical header name.

func (Context) Header

func (c Context) Header(header string, opts ...any)

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

func (c Context) Register(rt *Runtime)

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

func (c Context) ResponseHeaders() http.Header

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.

func IsExit

func IsExit(err error) (*ExitError, bool)

IsExit reports whether err was caused by PHP die()/exit().

func (*ExitError) Error

func (e *ExitError) Error() string

Error returns a formatted PHP exit status.

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.

func (*ExprCache) Get

func (c *ExprCache) Get(e model.Expr) (*compiledExpr, bool)

Get returns the compiled expression cached for e, if any.

func (*ExprCache) GetSource

func (c *ExprCache) GetSource(src string) (*compiledExpr, bool)

GetSource returns the compiled expression cached for src, if any.

func (*ExprCache) Set

func (c *ExprCache) Set(e model.Expr, ce *compiledExpr)

Set stores ce for e and its transpiled source.

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.

func (*IncludeCache) Get

func (c *IncludeCache) Get(path string) (*model.Program, bool)

Get returns the parsed program cached for path, if any.

func (*IncludeCache) Set

func (c *IncludeCache) Set(path string, prog *model.Program)

Set stores prog for path.

type IncludeFunc

type IncludeFunc func(path string) (*model.Program, error)

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 New

func New(w io.Writer, opts Options) *Runtime

New returns a Runtime that writes echo output to w (defaults to os.Stdout).

func (*Runtime) Const

func (rt *Runtime) Const(name string) (any, bool)

Const returns a registered constant value and whether it is defined.

func (*Runtime) Eval

func (rt *Runtime) Eval(e model.Expr, scope *Scope) (any, error)

Eval transpiles e, binds the referenced variables from scope, and runs the resulting program through the expr-lang VM.

func (*Runtime) Exit

func (rt *Runtime) Exit(code int) error

Exit interrupts execution with a PHP exit status.

func (*Runtime) FS

func (rt *Runtime) FS() fs.FS

FS returns the configured source root (or nil).

func (*Runtime) IncludedFiles

func (rt *Runtime) IncludedFiles() []string

IncludedFiles returns the cleaned dirFS filenames included by this runtime.

func (*Runtime) Load

func (rt *Runtime) Load(src string) (*model.Program, error)

Load parses PHP source into a program.

func (*Runtime) LoadFile

func (rt *Runtime) LoadFile(path string) (*model.Program, error)

LoadFile reads and parses a PHP file from the runtime source FS.

func (*Runtime) OnError

func (rt *Runtime) OnError(fn func(error))

OnError installs an error handler (register_error_handler). When set, runtime evaluation errors are routed here instead of aborting the caller.

func (*Runtime) RegisterClass

func (rt *Runtime) RegisterClass(c *model.Class)

RegisterClass adds a resolved class to the class table so `new Name` works.

func (*Runtime) RegisterConstructor

func (rt *Runtime) RegisterConstructor(name string, ctor any)

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

func (rt *Runtime) RegisterFunc(name string, fn any)

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) Run

func (rt *Runtime) Run(p *model.Program) error

Run executes a whole program in the global scope.

func (*Runtime) SAPI

func (r *Runtime) SAPI() string

SAPI returns the configured SAPI string.

func (*Runtime) SetConst

func (rt *Runtime) SetConst(name string, val any)

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

func (rt *Runtime) SetContext(ctx context.Context)

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

func (rt *Runtime) SetExprCache(cache *ExprCache)

SetExprCache installs a shared compiled-expression cache. Passing nil disables AST-node expression caching for this runtime.

func (*Runtime) SetGlobal

func (rt *Runtime) SetGlobal(name string, val any)

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) WorkDir

func (rt *Runtime) WorkDir() string

WorkDir returns the configured working directory inside the source root.

func (*Runtime) WritablePaths

func (rt *Runtime) WritablePaths() []string

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.

func NewScope

func NewScope() *Scope

NewScope returns an empty scope.

func (*Scope) Get

func (s *Scope) Get(name string) (any, bool)

Get returns the value of name and whether it is set.

func (*Scope) Set

func (s *Scope) Set(name string, val any)

Set stores name=val.

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]

func NewTranspiler

func NewTranspiler() *Transpiler

NewTranspiler returns a fresh transpiler.

func (*Transpiler) Closures

func (t *Transpiler) Closures() map[string]*model.Closure

Closures returns the anonymous functions collected during the last Transpile, keyed by their env identifier.

func (*Transpiler) Transpile

func (t *Transpiler) Transpile(e model.Expr) (src string, vars []string, err error)

Transpile converts e into expr-lang source and returns the source plus the sorted set of variable names it references.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL