[php-src] Issue #21901: `php_getopt()` does not clear `*optarg` for long options with optional argument when value is absent — `--ini` regression in 8.5

[email protected] (mellonella)
Newsgroups php.bugs
Message-ID <[email protected]>
Issue: https://github.com/php/php-src/issues/21901
Author: mellonella

### Description

In PHP 8.5 the `--ini` CLI option was changed from `no_argument` to `optional_argument` to support the new `--ini=diff` subcommand. As a side effect, `--ini` (without a value) now fails with `Unknown argument for --ini` whenever it is preceded on the command line by another option that takes a required argument.

**Reproducer (PHP 8.5):**

```
$ php --ini                       # OK
$ php -n --ini                    # OK   (-n takes no argument)
$ php -c /etc/php.ini --ini       # FAIL: Unknown argument for --ini
$ php -d foo=bar --ini            # FAIL: Unknown argument for --ini
$ php --ini -c /etc/php.ini       # OK   (workaround: reorder)
```

The same commands work correctly in PHP 8.4 and earlier.

**Expected behavior:**

`--ini` without a value should be treated identically regardless of its position on the command line, equivalent to `--ini=`.

**Root cause:**

In `main/getopt.c`, `php_getopt()` does not reset `*optarg` between calls. When a long option is declared with `need_param == 2` (optional) and is supplied without `=value`, the function falls through without assigning `*optarg`, leaving it pointing at the value consumed by the previous option (e.g. the path passed to `-c`, or the assignment passed to `-d`). The CLI SAPI then interprets that stale value as the `--ini` subcommand, fails to match it against `""` or `"diff"`, and emits the error.

The bug existed before 8.5 but was never observable because no long option used `optional_argument`. The introduction of `--ini=diff` (and the corresponding flip of `--ini` to `optional_argument`) exposed it.

**Affected versions:** 8.5.0 and later (any build with `--ini=diff` support).

---

# Proposed Patch

The simplest and most defensive fix is to clear `*optarg` at the top of `php_getopt()` so that callers never observe a stale pointer from a previous invocation, regardless of which code path is taken later.

```diff
--- main/getopt.c.orig	2026-03-10 19:15:23.000000000 -0400
+++ main/getopt.c	2026-04-24 06:11:15.038033405 -0400
@@ -59,6 +59,7 @@
 	static char **prev_optarg = NULL;
 
 	php_optidx = -1;
+	*optarg = NULL;
 
 	if(prev_optarg && prev_optarg != optarg) {
 		/* reset the state */

```

### PHP Version

```plain
PHP 8.5.4 (cli) (built: Apr 24 2026 07:24:54) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.5.4, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.4, Copyright (c), by Zend Technologies
```

### Operating System

Debian 12.13
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.