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 builtin-functions/_functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ function substr_compare ($main_str ::: string, $str ::: string, $offset ::: int,

function str_starts_with ($haystack ::: string, $needle ::: string) ::: bool;
function str_ends_with ($haystack ::: string, $needle ::: string) ::: bool;
function str_contains ($haystack ::: string, $needle ::: string) ::: bool;

function trim ($s ::: string, $what ::: string = " \n\r\t\v\0") ::: string;
function ltrim ($s ::: string, $what ::: string = " \n\r\t\v\0") ::: string;
Expand Down
14 changes: 14 additions & 0 deletions runtime/string.inl
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,20 @@ bool string::ends_with(const string &other) const noexcept {
return other.size() > size() ? false : !memcmp(c_str() + (size() - other.size()), other.c_str(), other.size());
}

bool string::contains(const string &other) const noexcept {
if (other.size() > size()) {
return false;
}

for (size_type i = 0; i < (size() - other.size() + 1); i++) {
if (memcmp(c_str() + i, other.c_str(), other.size()) == 0) {
return true;
}
}

return false;
}

const char &string::operator[](size_type pos) const {
return p[pos];
}
Expand Down
1 change: 1 addition & 0 deletions runtime/string_decl.inl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public:
inline bool empty() const;
inline bool starts_with(const string &other) const noexcept;
inline bool ends_with(const string &other) const noexcept;
inline bool contains(const string &other) const noexcept;

inline const char &operator[](size_type pos) const;
inline char &operator[](size_type pos);
Expand Down
4 changes: 4 additions & 0 deletions runtime/string_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2327,6 +2327,10 @@ bool f$str_ends_with(const string &haystack, const string &needle) {
return haystack.ends_with(needle);
}

bool f$str_contains(const string &haystack, const string &needle) {
return haystack.contains(needle);
Copy link
Contributor

@quasilyte quasilyte Jun 3, 2022

Choose a reason for hiding this comment

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

What are the advantages of implementing it as a new functions instead of using already existing strpos?

PHP uses similar approach:
https://github.com/php/php-src/blob/0b6f58d907e7169f7876bbea199967262197fbaa/ext/standard/string.c#L1794

As seen in the code below, str_contains could be implemented in terms of strpos like so:

return '' === $needle || false !== strpos($haystack, $needle);

I would check what php_memnstr from PHP is. Maybe we have compatible function. If not, we could add one and use it in places where PHP uses it.

Maybe php_memnstr is identical to the newly introduced contains method, but I suggest re-checking that.

Also: you can run benchmarks using ktest utility.
https://habr.com/ru/company/vk/blog/572424/

}

string f$trim(const string &s, const string &what) {
const char *mask = get_mask(what);

Expand Down
2 changes: 2 additions & 0 deletions runtime/string_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ bool f$str_starts_with(const string &haystack, const string &needle);

bool f$str_ends_with(const string &haystack, const string &needle);

bool f$str_contains(const string &haystack, const string &needle);

string f$trim(const string &s, const string &what = WHAT);

string f$ucfirst(const string &str);
Expand Down
19 changes: 19 additions & 0 deletions tests/cpp/runtime/string-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ TEST(string_test, test_ends_with) {
ASSERT_FALSE(str.starts_with(string{"hello world!"}));
}

TEST(string_test, test_contains) {
string empty_str{""};
ASSERT_TRUE(empty_str.contains(string{""}));
ASSERT_FALSE(empty_str.contains(string{"a"}));

string str{"hello world"};
ASSERT_TRUE(str.contains(string{"hello"}));
ASSERT_TRUE(str.contains(string{"world"}));
ASSERT_TRUE(str.contains(string{"orld"}));
ASSERT_TRUE(str.contains(string{"o w"}));
ASSERT_TRUE(str.contains(string{"d"}));
ASSERT_TRUE(str.contains(string{""}));

ASSERT_FALSE(str.contains(string{"hEllo"}));
ASSERT_FALSE(str.contains(string{"o W"}));

ASSERT_FALSE(str.contains(string{"hello world!"}));
}

TEST(string_test, test_make_const_string_on_memory) {
char mem[1024];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
global $global_map;
$global_map[__FILE__] = true;

#ifndef KPHP
if (!function_exists('str_contains')) {
function str_contains(string $haystack, string $needle): bool {
return '' === $needle || false !== strpos($haystack, $needle);
}
}
#endif