Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bin/vm.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions examples/001-SimpleWeb/example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

/**
* Minimal web-style page: reads ?name= from $_GET and prints HTML.
* Run with: QUERY_STRING='name=World' php bin/vm.php examples/001-SimpleWeb/example.php
* Or: php bin/vm.php -q 'name=World' examples/001-SimpleWeb/example.php
*/
$name = $_GET['name'];
echo '<!DOCTYPE html><html><body>';
echo '<h1>Hello ', $name, "</h1>\n";
echo '</body></html>';
45 changes: 43 additions & 2 deletions lib/Block.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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;
}


}
23 changes: 23 additions & 0 deletions lib/VM/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@
use PHPCompiler\Frame;
use PHPCompiler\Func;
use PHPCompiler\Runtime;
use PHPCompiler\Web\Superglobals;

class Context {
public array $functions = [];
public array $classes = [];
private ?RunStackEntry $runStack = null;
public array $constants = [];

/** @var array<string, Variable> */
private array $superglobalVars = [];

public Runtime $runtime;


Expand Down Expand Up @@ -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;
Expand Down
66 changes: 66 additions & 0 deletions lib/Web/Superglobals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

/*
* This file is part of PHP-Compiler, a PHP CFG Compiler for PHP code
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/

namespace PHPCompiler\Web;

use PHPCompiler\VM\Context;
use PHPCompiler\VM\HashTable;
use PHPCompiler\VM\Variable;

/**
* Populate CGI-style superglobals for compiled PHP scripts (VM mode).
*/
final class Superglobals
{
public const NAMES = [
'_GET',
'_POST',
'_SERVER',
'_REQUEST',
'_COOKIE',
'_ENV',
'_FILES',
'_SESSION',
];

public static function isSuperglobalName(string $name): bool
{
return in_array($name, self::NAMES, true);
}

public static function populateFromEnvironment(Context $context, ?string $queryString = null): void
{
if (null === $queryString) {
$fromEnv = getenv('QUERY_STRING');
$queryString = false === $fromEnv ? '' : $fromEnv;
}
self::populateGet($context, $queryString);
}

private static function populateGet(Context $context, string $queryString): void
{
$get = $context->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);
}
}
}
7 changes: 7 additions & 0 deletions src/cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
$execCode = '<?php '.array_shift($opts);
$execFile = 'Command line code';

break;
case '-q':
if (empty($opts) || substr($opts[0], 0, 1) === '-') {
die("Option -q requires a query string argument\n");
}
$options['-q'] = array_shift($opts);

break;
default:
if (! empty($opts)) {
Expand Down
18 changes: 17 additions & 1 deletion test/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,27 @@ public function testCases(string $name, string $code, array $sections): void {
];
$pipes = [];
$repoRoot = \dirname(__DIR__, 2);
$env = null;
if (isset($sections['ENV'])) {
$env = [];
foreach (explode("\n", trim($sections['ENV'])) as $line) {
$line = trim($line);
if ('' === $line) {
continue;
}
$parts = explode('=', $line, 2);
if (2 !== count($parts)) {
throw new \LogicException("Invalid ENV line: {$line}");
}
$env[$parts[0]] = $parts[1];
}
}
$proc = proc_open(
array_merge($this->phpCommand(), [$this->BIN]),
$descriptorSepc,
$pipes,
$repoRoot
$repoRoot,
$env
);
fwrite($pipes[0], $code);
fclose($pipes[0]);
Expand Down
11 changes: 11 additions & 0 deletions test/real/cases/web_get.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Web: read query parameter from $_GET
--ENV--
QUERY_STRING=name=World&page=home
--FILE--
<?php
echo 'Hello ', $_GET['name'], "\n";
echo 'page=', $_GET['page'], "\n";
--EXPECT--
Hello World
page=home