PHP bridge
A stylesheet can reach php — but only through what the application explicitly handed it. There is no implicit access to any php function, to superglobals, to the filesystem or to the database: the registry is the trust boundary.
Authorizing a function
This page ran exactly that, for php::round, php::strtolower,
php::spacing, php::palette and php::cardPadding. Every example
below is compiled live against that registry.
Anything else is refused
A function that was never registered is a compile error, not a call — and the error says which name was refused.
php:: and context:: are reserved: any other namespace is a file of the
stylesheet (the language, section 11), so a
php function cannot be reached by inventing one.
Values in, values out
Arguments reach php as plain php (4, '16px', 'x',
true, [1, 2]), and what php returns becomes an scs value — a string that
looks like a length becomes one, an array becomes a list, an associative array becomes a map. A php
object with no adapter is refused rather than emitted as garbage.
The compilation context
The application injects values for a compilation; the stylesheet reads them with
context::name, dotted for nested ones. The context is read only: scs can never write back
into the application.
A value that was never injected is an error, never an empty declaration:
Context functions see the resolved selector
A context function receives an EvaluationContext: the compilation context, the current
scope, the source position and the resolved selector — the one that will really be emitted,
after nesting and component resolution. It is evaluated once per generated rule.
Sharing a calculation with the application
The same expression semantics and the same registry are available without generating css, so application code and stylesheet can share one calculation.
| Called on this page | Answered |
|---|---|
evaluate('php::cardPadding()', ['density' => 2], '.card') | 48px |
evaluate('php::cardPadding()', ['density' => 2], '.panel') | 16px |
evaluate('8px * 3') | 24px |
evaluate('php::spacing(4)') | 16px |
Reading a stylesheet back
Compiling @let $spacing = php::spacing(context::density) with
['density' => 3] gives the css below, and
$result->phpValue('spacing') returns
'12px' — note that no
--spacing custom property was emitted for it.
Keeping the boundary
- Expose deterministic, side effect free functions only: they run at compile time, with whatever arguments the stylesheet chooses, and never in the browser.
- Extract what a function needs in php and inject it in the context, rather than exposing a function
that reaches for
$_GET, the filesystem or a database. - Compiling a source the application does not control is not a supported use case: an
@importreads a file, and an error frame quotes the source it failed on. If it ever happens, compile withallowImportsoff, register no php function for that compilation, and report errors withgetError()rather thanreport().
See the reference for the full contract,
and tests/scs/PhpBridgeTest.php for the tests that hold it in place.