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
21 changes: 19 additions & 2 deletions ext/standard/array_search.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\ExceptionBridge;
use PHPCompiler\JIT\JitBoolArg;
use PHPCompiler\JIT\JitNativeString;
use PHPCompiler\JIT\JitValueBox;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPLLVM\Value;
Expand All @@ -40,9 +41,10 @@ public function execute(Frame $frame): void
2,
'haystack'
);
// Z_PARAM_BOOL $strict — strict_types TypeError; else null→false + E_DEPRECATED (#29866).
$strict = false;
if (3 === $argc) {
$strict = $frame->calledArgs[2]->resolveIndirect()->toBool();
$strict = VmMath::parseBoolBuiltinArgForFrame($frame, 2, 'array_search', 3, 'strict');
}
$vm = null !== $frame->vmContext ? $frame->vmContext->runtime->vm() : null;
if (null === $frame->returnVar) {
Expand Down Expand Up @@ -70,7 +72,17 @@ public function call(Context $context, JITVariable ...$args): Value
}
$strict = $context->constantFromBool(false);
if (3 === $argc) {
$strict = JitBoolArg::lower($context, $args[2], 'array_search() strict');
// Compile-time null under strict: catchable TypeError then stop IR (peer substr_compare #29756).
if ($context->callerStrictTypes && self::isCompileTimeNull($args[2])) {
JitNativeString::ensureInsertBlock($context);
ExceptionBridge::emitTypeErrorAndAbort(
$context,
'array_search(): Argument #3 ($strict) must be of type bool, null given'
);

return $context->constantFromBool(false);
}
$strict = JitBoolArg::lowerCoerceZParamBool($context, $args[2], 'array_search', 'strict', 3);
}
if (JITVariable::TYPE_STRING === $args[0]->type || JITVariable::TYPE_VALUE === $args[0]->type) {
$this->jitString($context, $args[0], 'array_search() needle');
Expand All @@ -79,4 +91,9 @@ public function call(Context $context, JITVariable ...$args): Value

return ArraySearchRuntime::search($context, $args[0], $args[1], $strict);
}

private static function isCompileTimeNull(JITVariable $arg): bool
{
return JITVariable::TYPE_NULL === $arg->type || ($arg->isNullConstant ?? false);
}
}
28 changes: 24 additions & 4 deletions ext/standard/in_array.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Builtin\InArrayRuntime;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\ExceptionBridge;
use PHPCompiler\JIT\JitBoolArg;
use PHPCompiler\JIT\JitNativeString;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;

/**
* in_array() for arrays of scalar values (subset of PHP; JIT via InArrayRuntime).
*
* php-src: ext/standard/array.stub.php / array.c — PHP_FUNCTION(in_array)
*/
final class in_array extends Internal
{
Expand All @@ -48,9 +52,10 @@ public function execute(Frame $frame): void
2,
'haystack'
);
// Z_PARAM_BOOL $strict — strict_types TypeError; else null→false + E_DEPRECATED (#29866).
$strict = false;
if (3 === \count($frame->calledArgs)) {
$strict = $frame->calledArgs[2]->resolveIndirect()->toBool();
if (3 === $argc) {
$strict = VmMath::parseBoolBuiltinArgForFrame($frame, 2, 'in_array', 3, 'strict');
}
if (null === $frame->returnVar) {
return;
Expand Down Expand Up @@ -81,8 +86,18 @@ public function call(Context $context, JITVariable ...$args): Value
));
}
$strict = $context->constantFromBool(false);
if (3 === \count($args)) {
$strict = JitBoolArg::lower($context, $args[2], 'in_array() strict');
if (3 === $argc) {
// Compile-time null under strict: catchable TypeError then stop IR (peer substr_compare #29756).
if ($context->callerStrictTypes && self::isCompileTimeNull($args[2])) {
JitNativeString::ensureInsertBlock($context);
ExceptionBridge::emitTypeErrorAndAbort(
$context,
'in_array(): Argument #3 ($strict) must be of type bool, null given'
);

return $context->constantFromBool(false);
}
$strict = JitBoolArg::lowerCoerceZParamBool($context, $args[2], 'in_array', 'strict', 3);
}
if (JITVariable::TYPE_STRING === $args[0]->type || JITVariable::TYPE_VALUE === $args[0]->type) {
$this->jitString($context, $args[0], 'in_array() needle');
Expand All @@ -97,4 +112,9 @@ public function call(Context $context, JITVariable ...$args): Value

return InArrayRuntime::inArray($context, $args[0], $args[1], $strict);
}

private static function isCompileTimeNull(JITVariable $arg): bool
{
return JITVariable::TYPE_NULL === $arg->type || ($arg->isNullConstant ?? false);
}
}
2 changes: 2 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@
<file>./test/compliance/SubstrCompareNullCiCoerceJITTest.php</file>
<file>./test/compliance/Base64DecodeStrictNullVMTest.php</file>
<file>./test/compliance/Base64DecodeStrictNullJITTest.php</file>
<file>./test/compliance/InArrayArraySearchStrictNullVMTest.php</file>
<file>./test/compliance/InArrayArraySearchStrictNullJITTest.php</file>
<file>./test/compliance/SubstrReplaceNullReplaceStrictVMTest.php</file>
<file>./test/compliance/SubstrReplaceNullReplaceStrictJITTest.php</file>
<file>./test/compliance/MetaphoneMaxPhonemesValueErrorVMTest.php</file>
Expand Down
29 changes: 29 additions & 0 deletions test/compliance/InArrayArraySearchStrictNullJITTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

require_once __DIR__.'/../BaseTest.php';

/**
* @group llvm
*/
/** JIT: in_array/array_search(null $strict) under strict_types TypeError (#29866). */
final class InArrayArraySearchStrictNullJITTest extends BaseTest
{
protected static string $DIR = __DIR__;

public static function providePHPTests(): \Generator
{
yield 'in_array_array_search_strict_null_jit.phpt' => self::parsePHPT(
__DIR__.'/cases/stdlib/in_array_array_search_strict_null_jit.phpt',
'in_array_array_search_strict_null_jit.phpt'
);
}

public function setUp(): void
{
$this->BIN = realpath(__DIR__.'/../../bin/jit.php');
}
}
30 changes: 30 additions & 0 deletions test/compliance/InArrayArraySearchStrictNullVMTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

require_once __DIR__.'/../BaseTest.php';

/** VM: in_array/array_search(null $strict) under strict_types TypeError (#29866). */
final class InArrayArraySearchStrictNullVMTest extends BaseTest
{
protected static string $DIR = __DIR__;

public static function providePHPTests(): \Generator
{
yield 'in_array_array_search_strict_null.phpt' => self::parsePHPT(
__DIR__.'/cases/stdlib/in_array_array_search_strict_null.phpt',
'in_array_array_search_strict_null.phpt'
);
yield 'in_array_array_search_strict_null_weak.phpt' => self::parsePHPT(
__DIR__.'/cases/stdlib/in_array_array_search_strict_null_weak.phpt',
'in_array_array_search_strict_null_weak.phpt'
);
}

public function setUp(): void
{
$this->BIN = realpath(__DIR__.'/../../bin/vm.php');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
stdlib in_array/array_search(null $strict) under strict_types TypeError (#29866, ext/standard/array.c Z_PARAM_BOOL)
--FILE--
<?php
declare(strict_types=1);

try {
var_export(in_array(1, [1], null));
echo " uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
try {
var_export(array_search(1, [1], null));
echo " uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
$s = null;
try {
var_export(in_array(1, [1], $s));
echo " uncaught-var\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
in_array(): Argument #3 ($strict) must be of type bool, null given
array_search(): Argument #3 ($strict) must be of type bool, null given
in_array(): Argument #3 ($strict) must be of type bool, null given
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
JIT: in_array/array_search(null $strict) under strict_types TypeError (#29866, ext/standard/array.c Z_PARAM_BOOL)
--FILE--
<?php
declare(strict_types=1);

try {
var_export(in_array(1, [1], null));
echo " uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
try {
var_export(array_search(1, [1], null));
echo " uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
in_array(): Argument #3 ($strict) must be of type bool, null given
array_search(): Argument #3 ($strict) must be of type bool, null given
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
stdlib in_array/array_search(null $strict) without strict_types — Deprecated + coerce (#29866, ext/standard/array.c)
--FILE--
<?php
$prev = set_error_handler(static function (int $errno, string $errstr): bool {
if (E_DEPRECATED === $errno) {
echo 'DEP:', $errstr, "\n";

return true;
}

return false;
});
var_export(in_array(1, [1], null));
echo "\n";
var_export(array_search(1, [1], null));
echo "\n";
restore_error_handler();
--EXPECT--
DEP:in_array(): Passing null to parameter #3 ($strict) of type bool is deprecated
true
DEP:array_search(): Passing null to parameter #3 ($strict) of type bool is deprecated
0
22 changes: 22 additions & 0 deletions test/fixtures/aot/cases/in_array_array_search_strict_null.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
AOT: in_array/array_search(null $strict) under strict_types TypeError (#29866, ext/standard/array.c Z_PARAM_BOOL)
--FILE--
<?php
declare(strict_types=1);

try {
var_export(in_array(1, [1], null));
echo "\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
try {
var_export(array_search(1, [1], null));
echo "\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
in_array(): Argument #3 ($strict) must be of type bool, null given
array_search(): Argument #3 ($strict) must be of type bool, null given
15 changes: 15 additions & 0 deletions test/repro/issue_29866_in_array_strict_null.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);

try {
var_export(in_array(1, [1], null));
echo "\n";
} catch (Throwable $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
try {
var_export(array_search(1, [1], null));
echo "\n";
} catch (Throwable $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
Loading