Documentation
¶
Index ¶
- Constants
- func Bindings() []func(*Runtime)
- func EnvFromContext(ctx context.Context) (map[string]string, bool)
- func RegisterBinding(installer func(*Runtime))
- type ArgumentCountError
- type Context
- type ExitError
- type ExprCache
- type FilenameObserver
- type HostPanicError
- type IncludeCache
- type IncludeFunc
- type IncludeObserver
- type Observer
- type Options
- type Runtime
- func (rt *Runtime) Callable(v any) (func(...any) (any, error), bool)
- func (rt *Runtime) ClassExists(name string, autoload bool) (bool, error)
- func (rt *Runtime) Const(name string) (any, bool)
- func (rt *Runtime) Context() context.Context
- func (rt *Runtime) DeclaredClasses() []string
- func (rt *Runtime) DefinedConstants() map[string]any
- func (rt *Runtime) DefinedFunctions() (internal, user []string)
- 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) FreezeStdlib()
- func (rt *Runtime) FunctionExists(name string) bool
- func (rt *Runtime) IncludeFile(path string) (any, error)
- func (rt *Runtime) IncludePath() string
- 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) LookupConstructor(name string) (any, bool)
- func (rt *Runtime) MethodExists(class, method string) bool
- func (rt *Runtime) Observe(observer Observer)
- func (rt *Runtime) OnError(fn func(error))
- func (rt *Runtime) Output() io.Writer
- func (rt *Runtime) OutputDepth() int
- func (rt *Runtime) PHPInfo() error
- func (rt *Runtime) PopOutput() bool
- func (rt *Runtime) PushOutput(w io.Writer)
- func (rt *Runtime) RegisterAutoloader(callback any, prepend bool)
- 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) RegisterInclude(path string, fn func() (any, error))
- func (rt *Runtime) RegisterShutdown(callback any)
- func (rt *Runtime) ResetSession(out io.Writer, stdin io.Reader)
- func (rt *Runtime) Run(p *model.Program) (err error)
- func (rt *Runtime) SAPI() string
- func (rt *Runtime) SPLAutoload(class string) error
- 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) SetIncludePath(value string) string
- func (rt *Runtime) SetIncludeResolver(fn IncludeFunc)
- func (rt *Runtime) Stdin() io.Reader
- func (rt *Runtime) Trace(message string, kind ...telemetry.Kind) *telemetry.Span
- func (rt *Runtime) UnregisterAutoloader(callback any) bool
- func (rt *Runtime) UpdateFilename(filename string)
- func (rt *Runtime) UpdateIncludedFiles(count int)
- func (rt *Runtime) UpdateStatus(state telemetry.State)
- func (rt *Runtime) WorkDir() string
- func (rt *Runtime) WritablePaths() []string
- type Scope
- type Transpiler
Examples ¶
Constants ¶
const DefaultMaxCacheSize = 10000
DefaultMaxCacheSize is the default upper bound on cached entries.
Variables ¶
This section is empty.
Functions ¶
func Bindings ¶ added in v0.2.4
func Bindings() []func(*Runtime)
Bindings returns the contributed installers in registration order.
func EnvFromContext ¶ added in v0.2.0
EnvFromContext returns the request-scoped environment attached to contexts auto-injected into registered Go callables.
func RegisterBinding ¶ added in v0.2.4
func RegisterBinding(installer func(*Runtime))
RegisterBinding contributes a runtime installer, which stdlib.Register runs on every Runtime it sets up. Binding packages call it from their init() (see stdlib/ps/init.go), so a package is wired in by importing it, the way a program imports a database/sql driver. The registry lives here, in the leaf package that owns Runtime, so that stdlib can blank-import its subpackages without the two importing each other.
Installers run in registration order, before the bindings a host passes to stdlib.Register directly.
Types ¶
type ArgumentCountError ¶ added in v0.3.0
ArgumentCountError reports a call that passed more arguments than the callable declares. PHP raises the same condition as ArgumentCountError; this runtime registers that name alongside every other throwable class, and a returned error is catchable whichever of them a script names.
func (*ArgumentCountError) Error ¶ added in v0.3.0
func (e *ArgumentCountError) Error() string
type Context ¶
type Context struct {
Get map[string]string
Post map[string]string
Path map[string]string
Cookie map[string]string
Server map[string]string
Env map[string]string
Headers map[string]string
Argv []string
// contains filtered or unexported fields
}
Context carries HTTP request data exposed to PHP as superglobals, header functions, and staged response headers.
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 RequestContext ¶ added in v0.2.2
RequestContext returns the request data registered on a runtime context. It is intended for request-aware Go bindings such as session management.
func (Context) AddResponseHeader ¶ added in v0.2.2
AddResponseHeader appends a response header without replacing existing values. It is useful for headers such as Set-Cookie that may occur more than once in a response.
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.
func (Context) ResponseStatus ¶ added in v0.2.0
ResponseStatus returns the status staged by header(), or zero when the host should retain its default status. A Location header defaults to 302 like PHP.
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 immutable compiled expression programs by transpiled source and optional flat bytecode by parsed program identity. Expression AST metadata stays runtime-local; flat bytecode retains its source Program for the lifetime of the explicitly shared cache. Cache capacity is bounded to prevent memory leaks.
func NewExprCache ¶
func NewExprCache() *ExprCache
NewExprCache returns an empty compiled expression cache with default capacity (10,000 entries).
func NewExprCacheWithCapacity ¶ added in v0.2.1
NewExprCacheWithCapacity returns an empty expression cache bounded to maxEntries.
func (*ExprCache) Clear ¶ added in v0.2.1
func (c *ExprCache) Clear()
Clear resets the cached compiled expressions.
type FilenameObserver ¶ added in v0.2.1
FilenameObserver optionally receives the entrypoint passed to LoadFile.
type HostPanicError ¶ added in v0.2.0
HostPanicError converts a panic raised by a registered Go constructor, function, or method into the runtime error path. PHP try/catch can therefore handle it like an error returned by the same host callable.
func (*HostPanicError) Error ¶ added in v0.2.0
func (e *HostPanicError) Error() string
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. Cache size is bounded to prevent memory leaks.
func NewIncludeCache ¶
func NewIncludeCache() *IncludeCache
NewIncludeCache returns an empty parsed include cache with default capacity (10,000 entries).
func NewIncludeCacheWithCapacity ¶ added in v0.2.1
func NewIncludeCacheWithCapacity(maxEntries int) *IncludeCache
NewIncludeCacheWithCapacity returns an empty include cache bounded to maxEntries.
func (*IncludeCache) Clear ¶ added in v0.2.1
func (c *IncludeCache) Clear()
Clear resets the cached entries.
func (*IncludeCache) Get ¶
func (c *IncludeCache) Get(path string) (*model.Program, bool)
Get returns the parsed program cached for path, if any.
func (*IncludeCache) Len ¶ added in v0.2.1
func (c *IncludeCache) Len() int
Len returns the number of currently cached programs.
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 IncludeObserver ¶ added in v0.2.1
IncludeObserver optionally receives the number of files included so far.
type Observer ¶ added in v0.2.1
type Observer interface {
UpdateStatus(context.Context, telemetry.State)
Trace(context.Context, string, ...telemetry.Kind) *telemetry.Span
}
Observer receives lifecycle updates for a Runtime. Implementations must be safe for use by concurrent runtimes. A span is returned so the interpreter can measure the region it just reported; a nil span is valid and every method on it does nothing, which is what an observer with no trace in the context returns.
type Options ¶
type Options struct {
// RootFS is the filesystem used to load PHP entrypoints and includes.
RootFS fs.FS `yaml:"-"`
// Stdin is exposed to scripts as the STDIN stream. A nil reader produces an
// empty stream; CLI hosts should pass os.Stdin explicitly.
Stdin io.Reader `yaml:"-"`
// SAPI provides output for `php_sapi_name`.
SAPI string `yaml:"-"`
// WorkDir is the directory inside RootFS used as the script working directory.
// Empty means the RootFS root.
WorkDir string `yaml:"work_dir"`
// WritablePaths optionally restricts filesystem writes. When empty, writes are
// left to normal OS/user permissions. Enforcement is done by filesystem shims.
WritablePaths []string `yaml:"writable_paths"`
}
Options configures a Runtime.
type Runtime ¶
type Runtime struct {
// Env is the environment visible to PHP for this Runtime. New snapshots the
// host environment so mutations remain local to a single request/runtime.
Env map[string]string
// contains filtered or unexported fields
}
Runtime executes parsed PHP statements and evaluates transpiled expressions with registered functions, classes, constructors, and runtime state.
func NewFlatStack ¶ added in v0.2.0
NewFlatStack returns a Runtime that executes supported programs through the flat bytecode backend and atomically falls back to the interpreter otherwise. Most callers should use flatstack.New, which preserves runner's public API.
func (*Runtime) Callable ¶ added in v0.3.0
Callable resolves a PHP `callable` value into the uniform func(...any) (any, error) signature the runtime invokes everywhere.
PHP accepts several spellings of a callable and library code written for stock PHP uses all of them, so `call_user_func`, `usort` and friends have to understand each one:
- a closure or any Go func already registered with the runtime,
- "function_name", naming a free function,
- "Class::method", naming a static method,
- array($object, "method"), the bound-method form,
- array("Class", "method"), the static form.
The second return reports whether v was callable at all; callers turn that into PHP's "not a valid callback" error with their own function name.
func (*Runtime) ClassExists ¶ added in v0.1.0
ClassExists reports whether a PHP or host-backed class exists. If autoload is true, registered autoloaders are given a chance to define a missing class.
func (*Runtime) DeclaredClasses ¶ added in v0.1.0
DeclaredClasses returns the names of PHP classes and host-backed constructor classes currently available to the runtime. PHP does not guarantee ordering; phpscript sorts the snapshot for deterministic diagnostics.
func (*Runtime) DefinedConstants ¶ added in v0.1.0
DefinedConstants returns a stable snapshot of all runtime constants.
func (*Runtime) DefinedFunctions ¶ added in v0.1.0
DefinedFunctions returns stable snapshots of registered host/internal and PHP user-defined function names.
func (*Runtime) Eval ¶
Eval transpiles e, binds the referenced variables from scope, and runs the resulting program through the expr-lang VM.
func (*Runtime) FreezeStdlib ¶ added in v0.3.0
func (rt *Runtime) FreezeStdlib()
FreezeStdlib snapshots constants after host bindings are registered so ResetSession can drop script-defined constants without losing the stdlib.
func (*Runtime) FunctionExists ¶ added in v0.3.0
FunctionExists reports whether name resolves to a host shim or a PHP user-defined function, backing `function_exists`. Template engines guard their generated block functions with it, so an always-false answer would redeclare them on every include.
func (*Runtime) IncludeFile ¶ added in v0.3.0
IncludeFile evaluates path as PHP in a fresh scope, the same way an `include` statement would. Hosts that resolve classes outside the interpreter (the composer autoloader) use it to pull a declaration file into the runtime.
func (*Runtime) IncludePath ¶ added in v0.1.0
IncludePath returns the current SPL include path.
func (*Runtime) IncludedFiles ¶
IncludedFiles returns the cleaned dirFS filenames included by this runtime.
func (*Runtime) LookupConstructor ¶ added in v0.3.0
LookupConstructor returns the host constructor registered for name.
func (*Runtime) MethodExists ¶ added in v0.3.0
MethodExists reports whether a declared PHP class has a method by that name, using PHP's case-insensitive method lookup. It answers only for interpreted classes; a host-backed one is reflected over by its caller.
func (*Runtime) Observe ¶ added in v0.2.1
Observe registers an observer and reports that this Runtime is starting.
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) Output ¶ added in v0.3.0
Output returns the writer script output goes to: echo statements, inline HTML, and any builtin that emits text of its own, such as die() with a message, so their text joins the response body in the order the script produced it.
While a redirection is active (PushOutput, which output buffering is built on) this is the innermost writer, so everything the script emits is captured rather than sent on.
func (*Runtime) OutputDepth ¶ added in v0.3.0
OutputDepth reports how many redirections are active.
func (*Runtime) PHPInfo ¶ added in v0.1.0
PHPInfo prints a compact phpinfo-style text report for the phpscript runtime. It intentionally reports runtime facts rather than PHP extensions that phpscript does not provide.
func (*Runtime) PopOutput ¶ added in v0.3.0
PopOutput ends the innermost redirection, reporting whether one was active. Output resumes going to the enclosing writer, which is what makes nested captures compose.
func (*Runtime) PushOutput ¶ added in v0.3.0
PushOutput redirects script output to w until the matching PopOutput. Pushes nest: the innermost writer receives the output, so a captured region can itself capture.
func (*Runtime) RegisterAutoloader ¶ added in v0.1.0
RegisterAutoloader appends or prepends a callback to the SPL autoload queue. The callback receives a fully-qualified class name without a leading slash.
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. When a direct variable assignment receives a constructed value that implements SetID(string), the runtime passes it the PHP variable name without the leading dollar sign. 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) RegisterInclude ¶ added in v0.3.0
RegisterInclude installs a host implementation of one include target: an include or require of path runs fn instead of parsing the file, and the script sees fn's return value where PHP would see the file's.
This is for files the runtime reimplements in Go. composer's generated vendor/autoload.php is the motivating case: it bootstraps a class loader through PHP features the interpreter does not support, while phpscript can read the same composer metadata natively. Binding it here rather than installing the loader at startup keeps the PHP semantics intact: nothing is autoloadable until the script has actually included the autoloader.
func (*Runtime) RegisterShutdown ¶ added in v0.2.1
RegisterShutdown appends a callback to run when the current program exits. Shutdown callbacks run in registration order, including after exit or error.
func (*Runtime) ResetSession ¶ added in v0.3.0
ResetSession prepares the runtime to execute another program: new output and stdin, empty globals and user declarations. Host functions, constructors and the expression/bytecode caches stay.
func (*Runtime) SPLAutoload ¶ added in v0.1.0
SPLAutoload implements PHP's default autoloader: lowercase the qualified class name and search each include_path entry for class.php.
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 source-keyed compiled-program cache that is safe to share across runtimes. AST-specific expression metadata remains runtime-local. Passing nil disables cross-runtime expression caching.
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. A cache must only be shared by runtimes whose include paths resolve within the same source-root namespace. Passing nil disables include caching.
func (*Runtime) SetIncludePath ¶ added in v0.1.0
SetIncludePath sets the path list used by the default SPL autoloader and returns its previous value.
func (*Runtime) SetIncludeResolver ¶
func (rt *Runtime) SetIncludeResolver(fn IncludeFunc)
SetIncludeResolver installs the include/require resolver.
func (*Runtime) Stdin ¶ added in v0.2.0
Stdin returns the input stream configured by the runtime host.
func (*Runtime) Trace ¶ added in v0.2.1
Trace publishes a trace span to registered observers and returns the first mutable span provided by one of them.
func (*Runtime) UnregisterAutoloader ¶ added in v0.3.0
UnregisterAutoloader removes a callback from the SPL autoload queue, matching it the way PHP does: by the function, object and method it names rather than by identity, since each `array($this, "loadClass")` is a fresh array.
func (*Runtime) UpdateFilename ¶ added in v0.2.1
UpdateFilename publishes the PHP entrypoint to observers that support filename updates.
func (*Runtime) UpdateIncludedFiles ¶ added in v0.2.1
UpdateIncludedFiles publishes the current number of included files.
func (*Runtime) UpdateStatus ¶ added in v0.2.1
UpdateStatus publishes a lifecycle phase to all registered observers. It is also available to hosts for phases that occur outside PHP execution.
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.
func ScopeFromContext ¶ added in v0.0.6
ScopeFromContext returns the active PHP execution frame attached to a context auto-injected into a registered free function.
func (*Scope) Defer ¶ added in v0.1.3
Defer registers a callback to run when the current PHP execution frame returns. Callbacks run in last-in, first-out order.
func (*Scope) DefinedVars ¶ added in v0.1.0
DefinedVars returns a snapshot of PHP-visible variables in this frame. Interpreter bookkeeping slots use a double-underscore prefix and are not PHP variables, so they are omitted.
type Transpiler ¶
type Transpiler struct {
// contains filtered or unexported fields
}
Transpiler lowers expression AST nodes into type-agnostic expr-lang source that delegates PHP-specific behavior to runtime helpers.
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 (*Transpiler) Calls ¶ added in v0.2.5
func (t *Transpiler) Calls() []string
Calls returns the function names the last Transpile emitted as bare env identifiers, deduplicated and in first-use order. Like Idents, it aliases the transpiler's own storage.
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. It is nil when the expression has none.
func (*Transpiler) Exprs ¶ added in v0.0.5
func (t *Transpiler) Exprs() map[string]model.Expr
Exprs returns the sub-expressions marked for deferred evaluation during the last Transpile. It is nil when the expression has none.
func (*Transpiler) Idents ¶ added in v0.2.5
func (t *Transpiler) Idents() []string
Idents returns the expr identifiers of the variables collected during the last Transpile, positionally matching its vars result.
func (*Transpiler) Reset ¶ added in v0.2.5
func (t *Transpiler) Reset()
Reset clears the state collected by the last Transpile, keeping the backing arrays of the variable slices.
func (*Transpiler) Transpile ¶
Transpile converts e into expr-lang source and returns the source plus the set of variable names it references, in first-use order.
The returned slice aliases the transpiler's own storage; copy it (or use Idents, which is kept in step with it) before the transpiler is reused.