From cf00c9f447566ace25684b85309a2684208b1f53 Mon Sep 17 00:00:00 2001 From: PurHur Date: Mon, 18 May 2026 09:35:51 +0000 Subject: [PATCH] Add $_GET superglobal support for VM web-style scripts. Populate query parameters from QUERY_STRING or bin/vm.php -q so compiled pages can read request input like normal PHP. Includes a SimpleWeb example and an integration PHPT with ENV section support in the test harness. Co-authored-by: Cursor --- bin/vm.php | 3 ++ examples/001-SimpleWeb/example.php | 13 ++++++ lib/Block.php | 45 +++++++++++++++++++- lib/VM/Context.php | 23 +++++++++++ lib/Web/Superglobals.php | 66 ++++++++++++++++++++++++++++++ src/cli.php | 7 ++++ test/BaseTest.php | 18 +++++++- test/real/cases/web_get.phpt | 11 +++++ 8 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 examples/001-SimpleWeb/example.php create mode 100644 lib/Web/Superglobals.php create mode 100644 test/real/cases/web_get.phpt diff --git a/bin/vm.php b/bin/vm.php index 30ead9bff86..9c7d599919c 100755 --- a/bin/vm.php +++ b/bin/vm.php @@ -10,10 +10,13 @@ */ use PHPCompiler\Runtime; +use PHPCompiler\Web\Superglobals; function run(string $filename, string $code, array $options): void { $runtime = new Runtime(); + $queryString = $options['-q'] ?? null; + Superglobals::populateFromEnvironment($runtime->vmContext, is_string($queryString) ? $queryString : null); $block = $runtime->parseAndCompile($code, $filename); if (! isset($options['-l'])) { $runtime->run($block); diff --git a/examples/001-SimpleWeb/example.php b/examples/001-SimpleWeb/example.php new file mode 100644 index 00000000000..a6b2b7fbb5b --- /dev/null +++ b/examples/001-SimpleWeb/example.php @@ -0,0 +1,13 @@ +'; +echo '

Hello ', $name, "

\n"; +echo ''; diff --git a/lib/Block.php b/lib/Block.php index 0226439c1ca..161225b291b 100755 --- a/lib/Block.php +++ b/lib/Block.php @@ -14,8 +14,12 @@ use PHPCfg\Func; use PHPCfg\Block as CfgBlock; use PHPCfg\Operand; +use PHPCfg\Operand\Literal; +use PHPCfg\Operand\Temporary; +use PHPCfg\Operand\Variable as VarOperand; use PHPCompiler\VM\Context; use PHPCompiler\VM\Variable; +use PHPCompiler\Web\Superglobals; class Block { @@ -117,8 +121,8 @@ public function getFrame(Context $context, ?Frame $frame = null): Frame { if (!$found) { throw new \LogicException("Could not resolve argument"); } - } else { - $scope[$pos] = new Variable(Variable::TYPE_NULL); + } else { + $scope[$pos] = self::initialVariableForOperand($op, $context); } } @@ -129,5 +133,42 @@ public function getFrame(Context $context, ?Frame $frame = null): Frame { return $return; } + private static function initialVariableForOperand(Operand $op, Context $context): Variable + { + $name = self::resolveVariableName($op); + if (null !== $name && Superglobals::isSuperglobalName($name)) { + $existing = $context->getSuperglobal($name); + if (null !== $existing) { + return $existing; + } + + return $context->ensureSuperglobal($name); + } + + return new Variable(Variable::TYPE_NULL); + } + + private static function resolveVariableName(Operand $op): ?string + { + while ($op instanceof Temporary) { + if (null === $op->original) { + return null; + } + $op = $op->original; + } + if (!$op instanceof VarOperand) { + return null; + } + $nameOp = $op->name; + if (!$nameOp instanceof Literal) { + return null; + } + if (Variable::mapFromType($nameOp->type) !== Variable::TYPE_STRING) { + return null; + } + + return $nameOp->value; + } + } diff --git a/lib/VM/Context.php b/lib/VM/Context.php index d9c655d1b40..dcccbc75363 100755 --- a/lib/VM/Context.php +++ b/lib/VM/Context.php @@ -12,6 +12,7 @@ use PHPCompiler\Frame; use PHPCompiler\Func; use PHPCompiler\Runtime; +use PHPCompiler\Web\Superglobals; class Context { public array $functions = []; @@ -19,6 +20,9 @@ class Context { private ?RunStackEntry $runStack = null; public array $constants = []; + /** @var array */ + private array $superglobalVars = []; + public Runtime $runtime; @@ -58,6 +62,25 @@ public function declareFunction(Func $func): void { $this->functions[$lcname] = $func; } + public function ensureSuperglobal(string $name): Variable + { + if (!Superglobals::isSuperglobalName($name)) { + throw new \InvalidArgumentException("Unknown superglobal: {$name}"); + } + if (!isset($this->superglobalVars[$name])) { + $var = new Variable(Variable::TYPE_ARRAY); + $var->array(new HashTable()); + $this->superglobalVars[$name] = $var; + } + + return $this->superglobalVars[$name]; + } + + public function getSuperglobal(string $name): ?Variable + { + return $this->superglobalVars[$name] ?? null; + } + public function save(Frame $frame): RunStackEntry { $this->push($frame); $return = $this->runStack; diff --git a/lib/Web/Superglobals.php b/lib/Web/Superglobals.php new file mode 100644 index 00000000000..f5576f37cbf --- /dev/null +++ b/lib/Web/Superglobals.php @@ -0,0 +1,66 @@ +ensureSuperglobal('_GET'); + if ('' === $queryString) { + return; + } + $params = []; + parse_str($queryString, $params); + $ht = $get->toArray(); + foreach ($params as $key => $value) { + if (!is_string($key) || is_array($value)) { + continue; + } + $v = new Variable(Variable::TYPE_STRING); + $v->string((string) $value); + $ht->add($key, $v); + } + } +} diff --git a/src/cli.php b/src/cli.php index f77bc02477f..98e2c7ea0ea 100755 --- a/src/cli.php +++ b/src/cli.php @@ -54,6 +54,13 @@ $execCode = 'phpCommand(), [$this->BIN]), $descriptorSepc, $pipes, - $repoRoot + $repoRoot, + $env ); fwrite($pipes[0], $code); fclose($pipes[0]); diff --git a/test/real/cases/web_get.phpt b/test/real/cases/web_get.phpt new file mode 100644 index 00000000000..bdd17645503 --- /dev/null +++ b/test/real/cases/web_get.phpt @@ -0,0 +1,11 @@ +--TEST-- +Web: read query parameter from $_GET +--ENV-- +QUERY_STRING=name=World&page=home +--FILE-- +