Merge branch 'PHP-8.0' into PHP-8.1

This commit is contained in:
Jakub Zelenka 2022-10-30 11:43:11 +00:00
commit 29f7c4613e
No known key found for this signature in database
GPG key ID: 1C0779DC5C0A9DE4
6 changed files with 150 additions and 25 deletions

8
NEWS
View file

@ -11,6 +11,14 @@ PHP NEWS
. Fixed bug GH-9801 (Generator crashes when memory limit is exceeded during
initialization). (Arnaud)
- Date:
. Fixed bug GH-9763 (DateTimeZone ctr mishandles input and adds null byte if
the argument is an offset larger than 100*60 minutes). (Derick)
- FPM:
. Fixed bug GH-9754 (SaltStack (using Python subprocess) hangs when running
php-fpm 8.1.11). (Jakub Zelenka)
- mysqli:
. Fixed bug GH-9841 (mysqli_query throws warning despite using
silenced error mode). (Kamil Tekiela)

View file

@ -1290,7 +1290,7 @@ static int fpm_conf_post_process(int force_daemon) /* {{{ */
fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0);
}
if (0 > fpm_stdio_save_original_stderr()) {
if (!fpm_global_config.daemonize && 0 > fpm_stdio_save_original_stderr()) {
return -1;
}

View file

@ -75,7 +75,9 @@ int fpm_stdio_init_final(void)
int fpm_stdio_save_original_stderr(void)
{
/* php-fpm loses STDERR fd after call of the fpm_stdio_init_final(). Check #8555. */
/* STDERR fd gets lost after calling fpm_stdio_init_final() (check GH-8555) so it can be saved.
* It should be used only when PHP-FPM is not daemonized otherwise it might break some
* applications (e.g. GH-9754). */
zlog(ZLOG_DEBUG, "saving original STDERR fd: dup()");
fd_stderr_original = dup(STDERR_FILENO);
if (0 > fd_stderr_original) {
@ -88,7 +90,7 @@ int fpm_stdio_save_original_stderr(void)
int fpm_stdio_restore_original_stderr(int close_after_restore)
{
/* php-fpm loses STDERR fd after call of the fpm_stdio_init_final(). Check #8555. */
/* Restore original STDERR fd if it was previously saved. */
if (-1 != fd_stderr_original) {
zlog(ZLOG_DEBUG, "restoring original STDERR fd: dup2()");
if (0 > dup2(fd_stderr_original, STDERR_FILENO)) {
@ -99,9 +101,6 @@ int fpm_stdio_restore_original_stderr(int close_after_restore)
close(fd_stderr_original);
}
}
} else {
zlog(ZLOG_DEBUG, "original STDERR fd is not restored, maybe function is called from a child: dup2()");
return -1;
}
return 0;

View file

@ -0,0 +1,44 @@
--TEST--
FPM: GH-9754 - stderr is not closed in daemonized run
--SKIPIF--
<?php
include "skipif.inc";
FPM\Tester::skipIfRoot();
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
pid = {{FILE:PID}}
[unconfined]
listen = {{ADDR}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start(daemonize: true);
$tester->expectLogStartNotices();
$tester->switchLogSource('{{MASTER:OUT}}');
$tester->expectLogEmpty();
$tester->switchLogSource('{{FILE:LOG}}');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View file

@ -81,15 +81,23 @@ class LogReader
/**
* Get a single log line.
*
* @param int $timeoutSeconds
* @param int $timeoutMicroseconds
* @param int $timeoutSeconds Read timeout in seconds
* @param int $timeoutMicroseconds Read timeout in microseconds
* @param bool $throwOnTimeout Whether to throw an exception on timeout
*
* @return null|string
* @throws \Exception
*/
public function getLine(int $timeoutSeconds = 3, int $timeoutMicroseconds = 0): ?string
{
$line = $this->getSource()->getLine($timeoutSeconds, $timeoutMicroseconds);
public function getLine(
int $timeoutSeconds = 3,
int $timeoutMicroseconds = 0,
bool $throwOnTimeout = false
): ?string {
$line = $this->getSource()->getLine(
$timeoutSeconds,
$timeoutMicroseconds,
$throwOnTimeout
);
$this->trace(is_null($line) ? "LINE - null" : "LINE: $line");
return $line;
@ -196,17 +204,27 @@ class LogReader
}
}
class LogTimoutException extends \Exception
{
}
abstract class LogSource
{
/**
* Get single line from the source.
*
* @param int $timeoutSeconds Read timeout in seconds
* @param int $timeoutMicroseconds Read timeout in microseconds
* @param int $timeoutSeconds Read timeout in seconds
* @param int $timeoutMicroseconds Read timeout in microseconds
* @param bool $throwOnTimeout Whether to throw an exception on timeout
*
* @return string|null
* @throws LogTimoutException
*/
public abstract function getLine(int $timeoutSeconds, int $timeoutMicroseconds): ?string;
public abstract function getLine(
int $timeoutSeconds,
int $timeoutMicroseconds,
bool $throwOnTimeout = false
): ?string;
/**
* Get all lines that has been returned by getLine() method.
@ -236,13 +254,18 @@ class LogStreamSource extends LogSource
/**
* Get single line from the stream.
*
* @param int $timeoutSeconds Read timeout in seconds
* @param int $timeoutMicroseconds Read timeout in microseconds
* @param int $timeoutSeconds Read timeout in seconds
* @param int $timeoutMicroseconds Read timeout in microseconds
* @param bool $throwOnTimeout Whether to throw an exception on timeout
*
* @return string|null
* @throws LogTimoutException
*/
public function getLine(int $timeoutSeconds, int $timeoutMicroseconds): ?string
{
public function getLine(
int $timeoutSeconds,
int $timeoutMicroseconds,
bool $throwOnTimeout = false
): ?string {
if (feof($this->stream)) {
return null;
}
@ -255,6 +278,10 @@ class LogStreamSource extends LogSource
return $line;
} else {
if ($throwOnTimeout) {
throw new LogTimoutException('Timout exceeded when reading line');
}
return null;
}
}
@ -296,13 +323,18 @@ class LogFileSource extends LogSource
/**
* Get single line from the file.
*
* @param int $timeoutSeconds Read timeout in seconds
* @param int $timeoutMicroseconds Read timeout in microseconds
* @param int $timeoutSeconds Read timeout in seconds
* @param int $timeoutMicroseconds Read timeout in microseconds
* @param bool $throwOnTimeout Whether to throw an exception on timeout
*
* @return string|null
* @throws LogTimoutException
*/
public function getLine(int $timeoutSeconds, int $timeoutMicroseconds): ?string
{
public function getLine(
int $timeoutSeconds,
int $timeoutMicroseconds,
bool $throwOnTimeout = false
): ?string {
$endTime = microtime(true) + $timeoutSeconds + ($timeoutMicroseconds / 1_000_000);
while ($this->position >= count($this->lines)) {
if (is_file($this->filePath)) {
@ -317,6 +349,10 @@ class LogFileSource extends LogSource
}
usleep(50_000);
if (microtime(true) > $endTime) {
if ($throwOnTimeout) {
throw new LogTimoutException('Timout exceeded when reading line');
}
return null;
}
}

View file

@ -104,6 +104,11 @@ class Tester
*/
private $masterProcess;
/**
* @var bool
*/
private bool $daemonized;
/**
* @var resource
*/
@ -373,21 +378,27 @@ class Tester
*
* @param array $extraArgs Command extra arguments.
* @param bool $forceStderr Whether to output to stderr so error log is used.
* @param bool $daemonize Whether to start FPM daemonized
*
* @return bool
* @throws \Exception
*/
public function start(array $extraArgs = [], bool $forceStderr = true)
public function start(array $extraArgs = [], bool $forceStderr = true, bool $daemonize = false)
{
$configFile = $this->createConfig();
$desc = $this->outDesc ? [] : [1 => array('pipe', 'w'), 2 => array('redirect', 1)];
$cmd = [self::findExecutable(), '-F', '-y', $configFile];
$cmd = [self::findExecutable(), '-y', $configFile];
if ($forceStderr) {
$cmd[] = '-O';
}
$this->daemonized = $daemonize;
if ( ! $daemonize) {
$cmd[] = '-F';
}
if (getenv('TEST_FPM_RUN_AS_ROOT')) {
$cmd[] = '--allow-to-run-as-root';
}
@ -410,6 +421,9 @@ class Tester
if ( ! $this->outDesc !== false) {
$this->outDesc = $pipes[1];
$this->logReader->setStreamSource('{{MASTER:OUT}}', $this->outDesc);
if ($daemonize) {
$this->switchLogSource('{{FILE:LOG}}');
}
}
return true;
@ -837,7 +851,11 @@ class Tester
*/
public function terminate()
{
proc_terminate($this->masterProcess);
if ($this->daemonized) {
$this->signal('TERM');
} else {
proc_terminate($this->masterProcess);
}
}
/**
@ -1276,6 +1294,26 @@ class Tester
$this->logTool->checkTruncatedMessage($this->response->getErrorData());
}
/**
* Expect log to be empty.
*
* @throws \Exception
*/
public function expectLogEmpty()
{
try {
$line = $this->logReader->getLine(1, 0, true);
if ($line === '') {
$line = $this->logReader->getLine(1, 0, true);
}
if ($line !== null) {
$this->error('Log is not closed and returned line: ' . $line);
}
} catch (LogTimoutException $exception) {
$this->error('Log is not closed and timed out', $exception);
}
}
/**
* Expect reloading lines to be logged.
*