Skip to content
Closed
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
7 changes: 7 additions & 0 deletions builtin-functions/_functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ function kphp_backtrace($pretty ::: bool = true) ::: string[];
function ini_get ($s ::: string) ::: string | false;
function ini_set ($s ::: string, $v ::: string) ::: bool;

define('INI_SCANNER_NORMAL', 0);
define('INI_SCANNER_RAW', 1);
define('INI_SCANNER_TYPED', 2);

function parse_ini_string($ini_string ::: string, $process_sections ::: bool = false, $scanner_mode ::: int = INI_SCANNER_NORMAL) ::: mixed[];
function parse_ini_file($filename ::: string, $process_sections ::: bool = false, $scanner_mode ::: int = INI_SCANNER_NORMAL) ::: mixed[];

function memory_get_usage ($real_usage ::: bool = false) ::: int;
function memory_get_peak_usage ($real_usage ::: bool = false) ::: int;
function memory_get_total_usage() ::: int;
Expand Down
240 changes: 240 additions & 0 deletions runtime/ini.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#include "ini.h"

/*
* INI parsing functions
*/

const int64_t SINGLE_QOUTES = 0;
const int64_t DOUBLE_QUOTES = 1;
const int64_t ALL_QUOTES = 2;
Comment on lines +7 to +9

Choose a reason for hiding this comment

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

In this case, it's better to use enum.

enum qoutes_type {
    SINGLE_QOUTES,
    DOUBLE_QUOTES,
    ALL_QUOTES
} qoutes_type_t;


string clear_quotes(const string &str, int64_t flag) {

Choose a reason for hiding this comment

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

The variable named flag contains little information about what exactly is there, I think it's better to call this variable quotes_type.

Then the function declaration will look like this:

string clear_quotes(const string &str, qoutes_type_t quotes_type);

if (str.empty()) {
return {};
}

Optional<string> clear_str;

switch (flag) {
case SINGLE_QOUTES:
clear_str = f$preg_replace(string("/'+/"), string(""), str);
break;
case DOUBLE_QUOTES:
clear_str = f$preg_replace(string("/\"+/"), string(""), str);
break;
case ALL_QUOTES:
clear_str = f$preg_replace(string("/['|\"]+/"), string(""), str);
break;
}

if (clear_str.is_null()) {
return string("");
}

return clear_str.ref();
}

string clear_extra_spaces(const string &str) {
if (str.empty()) {
return {};
}

Optional<string> clear_str;

clear_str = f$preg_replace(string("/ +/"), string(" "), str);
Comment on lines +42 to +44

Choose a reason for hiding this comment

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

In this case, it's better to take advantage of the opportunities that C++11 standard gives us and use auto.

auto clear_str = f$preg_replace(string("/ +/"), string(" "), str);


if (clear_str.is_null()) {
return string("");
}

return clear_str.ref();
}

/*
* INI parsing functions
*/

bool is_ini_section(const string &ini_section) {
return f$preg_match(string("/^\\[.+\\]$/"), ini_section).val();
}

bool is_ini_var(const string &ini_var) {
return f$preg_match(string("/^.+/"), ini_var).val();
}

bool is_ini_val(const string &ini_val) {
return f$preg_match(string("/(.*\n(?=[A-Z])|.*$)/"), ini_val).val();
}

bool is_ini_bool_val(const string &ini_val) {
return f$preg_match(string("/^(false|true|0|1|off|on)$/i"), ini_val).val();
}

bool is_ini_float_val(const string &ini_val) {
return f$preg_match(string(R"(/^\d+\.\d*$/)"), ini_val).val();
}

bool is_ini_str_val(const string &ini_val) {
return f$preg_match(string("/^.*$/i"), ini_val).val();
}

string get_ini_section(const string &ini_entry) {
if (ini_entry.empty()) {
return {};
}

return ini_entry.substr(1, ini_entry.size()-2);
}

array<string> split_ini_entry(const string &ini_entry) {
if (ini_entry.empty()) {
return {};
}

array<string> res = f$explode(string("="), ini_entry, 2);

if (res.size().int_size != 2) {
php_warning("Error");
return {};
}

return res;
}

bool cast_str_to_bool(const string &ini_var) {
if (ini_var.empty()) {
return false;
}

string ini_bool_var = f$strtolower(ini_var);

if (ini_bool_var == string("on") || ini_bool_var == string("1") || ini_bool_var == string("true")) {
return true;
}

if (ini_bool_var == string("off") || ini_bool_var == string("0") || ini_bool_var == string("false")) {
return false;
}

return false;
Comment on lines +115 to +119

Choose a reason for hiding this comment

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

Redundant condition.
You can notice that in this code piece, regardless of the result, it will be false. Therefore, you should simply return false.

}

array<mixed> f$parse_ini_string(const string &ini_string, bool process_sections, int scanner_mode) {
if (ini_string.empty()) {
return {};
}

string ini_string_copy = f$trim(ini_string);
ini_string_copy = clear_extra_spaces(ini_string_copy);

array<mixed> res(array_size(0, 0, true));
array<mixed> section(array_size(0, 0, true));
string ini_entry;
string ini_section;

for (string::size_type i = 0; i <= ini_string_copy.size(); ++i) {
Copy link

@Tsygankov-Slava Tsygankov-Slava Dec 3, 2022

Choose a reason for hiding this comment

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

Firstly, also it's better to use auto here.
Secondly, it looks very strange that your for goes to ini_string_copy.size(), suddenly there will be a situation that you turn to ini_string_copy[ini_string_copy.size()]) (for example, if a string of the form [text somehow gets to your function). It's not very good.
It's better to fix on

for (string::size_type i = 0; i < ini_string_copy.size(); ++i) {
...
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Type of i is deduced as int, it is not an intention.

Choose a reason for hiding this comment

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

Type of i is deduced as int, it is not an intention.

Thanks, you're right.
Fixed

if (ini_string_copy[i] == '[') {
while (ini_string_copy[i] != ']') {
ini_entry.push_back(ini_string_copy[i]);
++i;
}
}

if (ini_string_copy[i] == '\"' || ini_string_copy[i] == '\'') {
ini_entry.push_back(ini_string_copy[i]);

++i;
while (ini_string_copy[i] != '\"' && ini_string_copy[i] != '\'') {
ini_entry.push_back(ini_string_copy[i]);
++i;
}
}

if (ini_string_copy[i] == ' ' || ini_string_copy[i] == '\n' || ini_string_copy[i] == '\0') {
if (is_ini_section(ini_entry)) {
if (process_sections && !ini_section.empty()) {
res.set_value(ini_section, section);
section.clear();
}

ini_section = get_ini_section(ini_entry);
} else if (!ini_entry.empty()){
array<string> ini = split_ini_entry(ini_entry);

if (ini.size().int_size != 2) {
php_warning("Invalid ini string format %s", ini_entry.c_str());
return {};
}

string ini_var = ini[0];
string ini_val = ini[1];

if (!is_ini_var(ini_var) && !is_ini_val(ini_val)) {
php_warning("Invalid ini string format %s", ini_entry.c_str());
return {};
}

if (!ini_var.empty()) {
switch (scanner_mode) {
case INI_SCANNER_NORMAL:
if (is_ini_bool_val(ini_val)) {
section.set_value(ini_var, cast_str_to_bool(ini_val) ? string("1") : string(""));
} else if (is_ini_str_val(ini_val)) {
section.set_value(ini_var, clear_quotes(ini_val, ALL_QUOTES));
}
break;
case INI_SCANNER_RAW:
if (is_ini_str_val(ini_val)) {
section.set_value(ini_var, clear_quotes(ini_val, DOUBLE_QUOTES));
} else {
section.set_value(ini_var, ini_val);
}
break;
case INI_SCANNER_TYPED:
if (ini_val.is_int()) {
section.set_value(ini_var, ini_val.to_int());
} else if (is_ini_float_val(ini_val)) {
section.set_value(ini_var, ini_val.to_float());
} else if (is_ini_bool_val(ini_val)) {
section.set_value(ini_var, cast_str_to_bool(ini_val));
} else if (is_ini_str_val(ini_val)) {
section.set_value(ini_var, clear_quotes(ini_val, ALL_QUOTES));
}
break;
}
}
}

ini_entry = {};
} else {
ini_entry.push_back(ini_string_copy[i]);
}
}

if (process_sections) {
if (!ini_section.empty()) {
res.set_value(ini_section, section);
}
} else {
return section;
}

return res;
}



array<mixed> f$parse_ini_file(const string &filename, bool process_sections, int scanner_mode) {
if (filename.empty()) {
php_warning("Filename cannot be empty");
return {};
}

Optional<string> ini_string = f$file_get_contents(filename);

if (ini_string.is_null()) {
return {};
}

return f$parse_ini_string(ini_string.ref(), process_sections, scanner_mode);
}
15 changes: 15 additions & 0 deletions runtime/ini.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include "runtime/kphp_core.h"
#include "runtime/string_functions.h"
#include "runtime/array_functions.h"
#include "runtime/streams.h"
#include "runtime/regexp.h"

const int INI_SCANNER_NORMAL = 0;
const int INI_SCANNER_RAW = 1;
const int INI_SCANNER_TYPED = 2;

array<mixed> f$parse_ini_string(const string &ini_string, bool process_sections = false, int scanner_mode = 0);

array<mixed> f$parse_ini_file(const string &filename, bool process_sections = false, int scanner_mode = 0);
1 change: 1 addition & 0 deletions runtime/runtime.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ prepend(KPHP_RUNTIME_SOURCES ${BASE_DIR}/runtime/
net_events.cpp
on_kphp_warning_callback.cpp
openssl.cpp
ini.cpp
php_assert.cpp
profiler.cpp
regexp.cpp
Expand Down
Loading