Skip to content
Open
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
1 change: 1 addition & 0 deletions sapi/cli/cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ typedef enum php_cli_mode {
PHP_CLI_MODE_REFLECTION_ZEND_EXTENSION = 12,
PHP_CLI_MODE_SHOW_INI_CONFIG = 13,
PHP_CLI_MODE_SHOW_INI_DIFF = 14,
PHP_CLI_MODE_SHOW_INI_JSON = 15,
} php_cli_mode;

typedef struct php_cli_server_context {
Expand Down
54 changes: 54 additions & 0 deletions sapi/cli/php_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
#include "zend_hash.h"
#include "zend_modules.h"
#include "zend_interfaces.h"
#include "zend_smart_str.h"

#include "ext/reflection/php_reflection.h"
#include "ext/json/php_json.h"

#include "SAPI.h"

Expand Down Expand Up @@ -826,6 +828,8 @@ static int do_cli(int argc, char **argv) /* {{{ */
if (php_optarg) {
if (strcmp(php_optarg, "diff") == 0) {
context.mode = PHP_CLI_MODE_SHOW_INI_DIFF;
} else if(strcmp(php_optarg, "json") == 0) {
context.mode = PHP_CLI_MODE_SHOW_INI_JSON;
} else {
param_error = "Unknown argument for --ini\n";
}
Expand Down Expand Up @@ -1148,6 +1152,56 @@ static int do_cli(int argc, char **argv) /* {{{ */
zend_array_destroy(sorted);
break;
}
case PHP_CLI_MODE_SHOW_INI_JSON:
{
zval out;
array_init_size(&out, 4);

add_assoc_string(&out, "path", PHP_CONFIG_FILE_PATH);
if (php_ini_opened_path) {
add_assoc_string(&out, "file", php_ini_opened_path);
}
if (php_ini_scanned_path) {
add_assoc_string(&out, "scan", php_ini_scanned_path);
}
if (php_ini_scanned_files) {
char* scanning = estrdup(php_ini_scanned_files);
char* token;
char* next = php_strtok_r(scanning, ",\n", &token);
zval scanned;
array_init(&scanned);

while (next) {
while (*next == ' ' || *next == '\t') {
next++;
}
if (*next != '\0') {
char *end = next + strlen(next) - 1;
while (end > next && (*end == ' ' || *end == '\t')) {
*end = '\0';
end--;
}
add_next_index_string(&scanned, next);
}
next = php_strtok_r(NULL, ",\n", &token);
}
add_assoc_zval(&out, "scanned", &scanned);
efree(scanning);
}
smart_str buf = {0};
if (php_json_encode(&buf, &out,
PHP_JSON_PRETTY_PRINT) == SUCCESS) {
fwrite(ZSTR_VAL(buf.s), 1, ZSTR_LEN(buf.s), stdout);
fwrite("\n", 1, sizeof("\n")-1, stdout);
} else {
fprintf(stderr,
Copy link
Member Author

@krakjoe krakjoe Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain this can actually happen.

If anyone can show convincingly that this is pointless we should drop it ...

Maybe it's better to use throw on error, but I prefer to print something specifically to stderr because of how this interface will be used, still I can't imagine how an error could occur in the first place ... but this is probably a failure of my imagination rather than a fact.

"An error occured while trying to encode configuration\n");
EG(exit_status) = 1;
}
smart_str_free(&buf);
zval_dtor(&out);
break;
}
}
} zend_end_try();

Expand Down
54 changes: 54 additions & 0 deletions sapi/cli/tests/026.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--TEST--
CLI php --ini=json
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
$php = \getenv('TEST_PHP_EXECUTABLE');

$interface = \shell_exec(\sprintf(
"%s --ini=json",
$php));
$script = \shell_exec(\sprintf(
"%s -r 'echo json_encode([
\"file\" => php_ini_loaded_file(),
\"scanned\" => php_ini_scanned_files() ?: []]);'",
$php));

$response["interface"] = \json_decode($interface,
JSON_THROW_ON_ERROR|JSON_OBJECT_AS_ARRAY);
$response["script"] = \json_decode($script,
JSON_THROW_ON_ERROR);
if (isset($response["script"]["scanned"]) &&
$response["script"]["scanned"]) {
$response["script"]["scanned"] =
\array_map('trim',\explode(
",\n", $response["script"]["scanned"]));
}

if (!isset($response["interface"]["file"]) &&
isset($response["script"]["file"]) &&
$response["script"]["file"]) {
echo "interface is missing file\n";
var_dump($response);
}

if (!isset($response["interface"]["scanned"]) &&
isset($response["script"]["scanned"]) &&
$response["script"]["scanned"]) {
echo "interface is missing scanned\n";
var_dump($response);
}

if (isset($response["interface"]["scanned"]) &&
$response["interface"]["scanned"] != $response["script"]["scanned"]) {
echo "interface and script scanned do not match\n";
var_dump($response);
}

echo "OK\n";
?>
--EXPECT--
OK