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
33 changes: 33 additions & 0 deletions ext/standard/JitHighlight.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,44 @@ public static function highlightFile(Context $context, string $functionName, JIT
// Arity guarded by highlight_file/show_source::call via requireArgCountRangeJit (#30689).
$argc = \count($args);

$pathLit = $args[0]->compileTimeString ?? null;
$blockedEarly = JitStreamIncludeOpen::rejectCompileTimeBlockedScriptOpen(
$context,
$pathLit,
$functionName,
true,
true
);
if (null !== $blockedEarly) {
return $blockedEarly;
}

// Z_PARAM_PATH then empty-path: Zend E_WARNING then ValueError (#30514).
$pathStr = JitStringBuiltinArg::lowerPath($context, $args[0], $functionName, 0, 'filename');
self::rejectEmptyPathWithHighlightWarning($context, $args[0], $pathStr, $functionName);
StringFileGetContents::implement($context);
JitNativeString::ensureInsertBlock($context);

return JitStreamIncludeOpen::wrapWithRuntimeBlockedGuard(
$context,
$pathStr,
$functionName,
true,
static fn (Context $ctx): Value => self::emitMissingFileResult($ctx, $args, $argc, $functionName),
static fn (Context $ctx): Value => self::lowerReadAndHighlight($ctx, $pathStr, $args, $argc, $functionName)
);
}

/**
* @param list<JITVariable> $args
*/
private static function lowerReadAndHighlight(
Context $context,
Value $pathStr,
array $args,
int $argc,
string $functionName
): Value {
$contents = $context->builder->call(
$context->lookupFunction('__compiler_file_get_contents'),
$pathStr
Expand Down
142 changes: 142 additions & 0 deletions ext/standard/JitStreamIncludeOpen.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\standard;

use PHPCompiler\JIT\BasicBlockHelper;
use PHPCompiler\JIT\Builtin\StreamIncludeOpen;
use PHPCompiler\JIT\Builtin\StringTriggerError;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitValueBox;
use PHPCompiler\VM\ErrorReporter;
use PHPLLVM\Builder;
use PHPLLVM\Value;

/** Shared JIT lowering for script/include opens blocked by allow_url_include (#32104). */
final class JitStreamIncludeOpen
{
/**
* @return Value|null early-return value when blocked at compile time; null to continue
*/
public static function rejectCompileTimeBlockedScriptOpen(
Context $context,
?string $pathLit,
string $function,
bool $forHighlight,
bool $returnFalseOnBlock
): ?Value {
if (null === $pathLit || !VmStreamIncludeOpenPolicy::blockedForScriptOpen($pathLit, null)) {
return null;
}
self::emitCompileTimeBlockedWarnings($context, $function, $pathLit, $forHighlight);

return $returnFalseOnBlock
? self::materializeFalse($context)
: self::materializeEmptyString($context);
}

/**
* Emit runtime guard: if blocked → return $makeBlockedReturn(); else run $continue callback.
*
* @param callable(Context): Value $makeBlockedReturn
* @param callable(Context): Value $continue
*/
public static function wrapWithRuntimeBlockedGuard(
Context $context,
Value $pathStr,
string $function,
bool $forHighlight,
callable $makeBlockedReturn,
callable $continue
): Value {
StreamIncludeOpen::ensureLinked($context);
$i1 = $context->getTypeFromString('i1');
$i32 = $context->getTypeFromString('int32');
$blocked = $context->builder->call(
StreamIncludeOpen::helperFunction($context),
$pathStr,
$context->builder->load($context->constantStringFromString($function)),
$i32->constInt($forHighlight ? 1 : 0, false)
);
$isBlocked = $context->builder->icmp(Builder::INT_NE, $blocked, $i1->constInt(0, false));
$blockedBb = BasicBlockHelper::append($context, $function.'_url_include_blocked');
$okBb = BasicBlockHelper::append($context, $function.'_url_include_ok');
$doneBb = BasicBlockHelper::append($context, $function.'_url_include_done');
$context->builder->branchIf($isBlocked, $blockedBb, $okBb);

$context->builder->positionAtEnd($blockedBb);
$blockedVal = $makeBlockedReturn($context);
$blockedEnd = $context->builder->getInsertBlock();
$context->builder->branch($doneBb);

$context->builder->positionAtEnd($okBb);
$okVal = $continue($context);
$okEnd = $context->builder->getInsertBlock();
$context->builder->branch($doneBb);

$context->builder->positionAtEnd($doneBb);
$ptrTy = $context->getTypeFromString('__value__*');
$phi = $context->builder->phi($ptrTy);
$phi->addIncoming($blockedVal, $blockedEnd);
$phi->addIncoming($okVal, $okEnd);

return $phi;
}

public static function materializeEmptyString(Context $context): Value
{
$slot = JitValueBox::alloc($context);
$ptr = JitValueBox::pointer($context, $slot);
$owned = $context->builder->call(
$context->lookupFunction('__string__separate'),
$context->builder->load($context->constantStringFromString(''))
);
$context->builder->call($context->lookupFunction('__value__writeString'), $ptr, $owned);

return $ptr;
}

public static function materializeFalse(Context $context): Value
{
$slot = JitValueBox::alloc($context);
$ptr = JitValueBox::pointer($context, $slot);
JitValueBox::writeBool($context, $slot, $context->constantFromBool(false));

return $ptr;
}

private static function emitCompileTimeBlockedWarnings(
Context $context,
string $function,
string $path,
bool $forHighlight
): void {
self::emitWarning($context, VmStreamIncludeOpenPolicy::wrapperDisabledMessage($function, $path));
self::emitWarning($context, VmStreamIncludeOpenPolicy::failedToOpenMessage($function, $path));
if ($forHighlight) {
self::emitWarning(
$context,
VmStreamOpenFailure::highlightFailedOpeningMessage($function, $path)
);
}
}

private static function emitWarning(Context $context, string $message): void
{
StringTriggerError::ensureLinked($context);
$i8p = $context->getTypeFromString('int8*');
$i32 = $context->getTypeFromString('int32');
$sizeT = $context->getTypeFromString('size_t');
$msgPtr = $context->builder->pointerCast($context->constantFromString($message), $i8p);
$emptyFile = $context->builder->pointerCast($context->constantFromString(''), $i8p);
$context->builder->call(
$context->lookupFunction('__compiler_trigger_error'),
$msgPtr,
$sizeT->constInt(\strlen($message), false),
$i32->constInt(ErrorReporter::E_WARNING, false),
$emptyFile,
$i32->constInt(0, false)
);
}
}
38 changes: 24 additions & 14 deletions ext/standard/JitStripWhitespace.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ public static function invoke(Context $context, JITVariable ...$args): Value
}

$pathLit = $args[0]->compileTimeString ?? null;
$blockedEarly = JitStreamIncludeOpen::rejectCompileTimeBlockedScriptOpen(
$context,
$pathLit,
'php_strip_whitespace',
false,
false
);
if (null !== $blockedEarly) {
return $blockedEarly;
}

if (null !== $pathLit) {
if ('' === $pathLit) {
throw new \ValueError(PathSupport::EMPTY_PATH_VALUE_ERROR_MESSAGE);
Expand All @@ -41,8 +52,20 @@ public static function invoke(Context $context, JITVariable ...$args): Value

StripWhitespace::ensureLinked($context);
StringFileGetContents::implement($context);

$pathStr = JitStreamPath::lowerNonEmptyPath($context, $args[0], 'php_strip_whitespace', 0, 'filename');

return JitStreamIncludeOpen::wrapWithRuntimeBlockedGuard(
$context,
$pathStr,
'php_strip_whitespace',
false,
static fn (Context $ctx): Value => JitStreamIncludeOpen::materializeEmptyString($ctx),
static fn (Context $ctx): Value => self::lowerReadAndStrip($ctx, $pathStr)
);
}

private static function lowerReadAndStrip(Context $context, Value $pathStr): Value
{
$contents = $context->builder->call(
$context->lookupFunction('__compiler_file_get_contents'),
$pathStr
Expand Down Expand Up @@ -76,19 +99,6 @@ public static function invoke(Context $context, JITVariable ...$args): Value
return $ptr;
}

private static function emptyString(Context $context): Value
{
$slot = JitValueBox::alloc($context);
$ptr = JitValueBox::pointer($context, $slot);
$context->builder->call(
$context->lookupFunction('__value__writeString'),
$ptr,
self::emptyOwnedString($context)
);

return $ptr;
}

private static function emptyOwnedString(Context $context): Value
{
return $context->builder->call(
Expand Down
26 changes: 26 additions & 0 deletions ext/standard/StreamIncludeOpenJitHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\standard;

/**
* Script/include stream open gate for compiled JIT/AOT modules (#32104).
*
* VM SSOT: {@see VmStreamIncludeOpenPolicy}. php-src: main/streams/streams.c STREAM_OPEN_FOR_INCLUDE.
*/
final class StreamIncludeOpenJitHelper
{
/**
* When blocked, emits Zend-shaped warnings and returns true.
*/
public static function warnIfBlocked(string $path, string $function, bool $forHighlight): bool
{
if (!VmStreamIncludeOpenPolicy::blockedForScriptOpen($path, null)) {
return false;
}
VmStreamIncludeOpenPolicy::warnScriptOpenBlockedStandalone($function, $path, $forHighlight);

return true;
}
}
Loading
Loading