0fd0fa814c
Post-hphpc, declaring builtins as inline is useless, which obviates the need for this layer. This doesn't fully delete the file, because I wanted to keep mindless parts and use-your-brain parts separate. This is the mindless part. The remaining functions in noinline.cpp are no-inline wrappers around (a) a function that isn't defined in an extension, call_user_func_array, and (b) calling polymorphic is_* functions, which can probably mostly go away now. But I wanted to exercise more care around those, so they'll come in a followup diff.
1162 linhas
34 KiB
C++
1162 linhas
34 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
| Copyright (c) 1997-2010 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <runtime/ext/ext_string.h>
|
|
#include <runtime/base/util/string_buffer.h>
|
|
#include <runtime/base/zend/zend_string.h>
|
|
#include <runtime/base/zend/zend_url.h>
|
|
#include <runtime/base/zend/zend_printf.h>
|
|
#include <runtime/base/zend/zend_scanf.h>
|
|
#include <runtime/base/bstring.h>
|
|
#include <runtime/base/util/request_local.h>
|
|
#include <util/lock.h>
|
|
#include <locale.h>
|
|
#include <runtime/base/server/http_request_handler.h>
|
|
#include <runtime/base/server/http_protocol.h>
|
|
#include <runtime/base/taint/taint_data.h>
|
|
#include <runtime/base/taint/taint_observer.h>
|
|
|
|
namespace HPHP {
|
|
|
|
static Mutex s_mutex;
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
String f_addcslashes(CStrRef str, CStrRef charlist) {
|
|
return StringUtil::CEncode(str, charlist);
|
|
}
|
|
String f_stripcslashes(CStrRef str) {
|
|
return StringUtil::CDecode(str);
|
|
}
|
|
String f_addslashes(CStrRef str) {
|
|
return StringUtil::SqlEncode(str);
|
|
}
|
|
String f_stripslashes(CStrRef str) {
|
|
return StringUtil::SqlDecode(str);
|
|
}
|
|
String f_bin2hex(CStrRef str) {
|
|
return StringUtil::HexEncode(str);
|
|
}
|
|
Variant f_hex2bin(CStrRef str) {
|
|
try {
|
|
return StringUtil::HexDecode(str);
|
|
} catch (...) {
|
|
raise_warning("hex2bin: malformed input");
|
|
return false;
|
|
}
|
|
}
|
|
String f_nl2br(CStrRef str) {
|
|
return str.replace("\n", "<br />\n");
|
|
}
|
|
String f_quotemeta(CStrRef str) {
|
|
return StringUtil::RegExEncode(str);
|
|
}
|
|
String f_str_shuffle(CStrRef str) {
|
|
return StringUtil::Shuffle(str);
|
|
}
|
|
String f_strrev(CStrRef str) {
|
|
return StringUtil::Reverse(str);
|
|
}
|
|
String f_strtolower(CStrRef str) {
|
|
return StringUtil::ToLower(str);
|
|
}
|
|
String f_strtoupper(CStrRef str) {
|
|
return StringUtil::ToUpper(str, StringUtil::ToUpperAll);
|
|
}
|
|
String f_ucfirst(CStrRef str) {
|
|
return StringUtil::ToUpper(str, StringUtil::ToUpperFirst);
|
|
}
|
|
String f_lcfirst(CStrRef str) {
|
|
return StringUtil::ToLower(str, StringUtil::ToLowerFirst);
|
|
}
|
|
String f_ucwords(CStrRef str) {
|
|
return StringUtil::ToUpper(str, StringUtil::ToUpperWords);
|
|
}
|
|
String f_strip_tags(CStrRef str, CStrRef allowable_tags /* = "" */) {
|
|
return StringUtil::StripHTMLTags(str, allowable_tags);
|
|
}
|
|
String f_trim(CStrRef str, CStrRef charlist /* = k_HPHP_TRIM_CHARLIST */) {
|
|
return StringUtil::Trim(str, StringUtil::TrimBoth, charlist);
|
|
}
|
|
String f_ltrim(CStrRef str, CStrRef charlist /* = k_HPHP_TRIM_CHARLIST */) {
|
|
return StringUtil::Trim(str, StringUtil::TrimLeft, charlist);
|
|
}
|
|
String f_rtrim(CStrRef str, CStrRef charlist /* = k_HPHP_TRIM_CHARLIST */) {
|
|
return StringUtil::Trim(str, StringUtil::TrimRight, charlist);
|
|
}
|
|
String f_chop(CStrRef str, CStrRef charlist /* = k_HPHP_TRIM_CHARLIST */) {
|
|
return StringUtil::Trim(str, StringUtil::TrimRight, charlist);
|
|
}
|
|
Variant f_explode(CStrRef delimiter, CStrRef str, int limit /* = 0x7FFFFFFF */) {
|
|
return StringUtil::Explode(str, delimiter, limit);
|
|
}
|
|
|
|
String f_implode(CVarRef arg1, CVarRef arg2 /* = null_variant */) {
|
|
Array items;
|
|
String delim;
|
|
if (arg1.is(KindOfArray)) {
|
|
items = arg1.toArray();
|
|
delim = arg2.toString();
|
|
} else if (arg2.is(KindOfArray)) {
|
|
items = arg2.toArray();
|
|
delim = arg1.toString();
|
|
} else {
|
|
throw_bad_type_exception("arguments need at least one array");
|
|
return String();
|
|
}
|
|
return StringUtil::Implode(items, delim);
|
|
}
|
|
|
|
String f_join(CVarRef glue, CVarRef pieces /* = null_variant */) {
|
|
return f_implode(glue, pieces);
|
|
}
|
|
Variant f_str_split(CStrRef str, int split_length /* = 1 */) {
|
|
return StringUtil::Split(str, split_length);
|
|
}
|
|
Variant f_chunk_split(CStrRef body, int chunklen /* = 76 */,
|
|
CStrRef end /* = "\r\n" */) {
|
|
return StringUtil::ChunkSplit(body, chunklen, end);
|
|
}
|
|
|
|
class TokenizerData : public RequestEventHandler {
|
|
public:
|
|
String str;
|
|
int pos;
|
|
int mask[256];
|
|
|
|
virtual void requestInit() {
|
|
str.reset();
|
|
pos = 0;
|
|
memset(&mask, 0, sizeof(mask));
|
|
}
|
|
virtual void requestShutdown() {
|
|
requestInit();
|
|
}
|
|
};
|
|
IMPLEMENT_STATIC_REQUEST_LOCAL(TokenizerData, s_tokenizer_data);
|
|
|
|
Variant f_strtok(CStrRef str, CVarRef token /* = null_variant */) {
|
|
String stoken;
|
|
if (!token.isNull()) {
|
|
s_tokenizer_data->str = str;
|
|
s_tokenizer_data->pos = 0;
|
|
stoken = token.toString();
|
|
} else {
|
|
stoken = str;
|
|
}
|
|
|
|
String sstr = s_tokenizer_data->str;
|
|
int pos = s_tokenizer_data->pos;
|
|
if (pos >= sstr.size()) {
|
|
return false;
|
|
}
|
|
|
|
// set up mask
|
|
int *mask = s_tokenizer_data->mask;
|
|
for (int i = 0; i < stoken.size(); i++) {
|
|
mask[(unsigned char)stoken.data()[i]] = 1;
|
|
}
|
|
|
|
// skip leading delimiters
|
|
const char *s0 = sstr.data();
|
|
int i = pos;
|
|
for (; i < sstr.size(); i++) {
|
|
if (!mask[(unsigned char)s0[i]]) {
|
|
break;
|
|
}
|
|
}
|
|
int pos0 = i;
|
|
for (; i < sstr.size(); i++) {
|
|
if (mask[(unsigned char)s0[i]]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
String ret(s0 + pos0, i - pos0, CopyString);
|
|
s_tokenizer_data->pos = i + 1;
|
|
|
|
// reset mask
|
|
for (int i = 0; i < stoken.size(); i++) {
|
|
mask[(unsigned char)stoken.data()[i]] = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static Variant str_replace(CVarRef search, CVarRef replace, CStrRef subject,
|
|
int &count, bool caseSensitive) {
|
|
if (search.is(KindOfArray)) {
|
|
String ret = subject;
|
|
|
|
Array searchArr = search.toArray();
|
|
if (replace.is(KindOfArray)) {
|
|
Array replArr = replace.toArray();
|
|
ArrayIter replIter(replArr);
|
|
for (ArrayIter iter(searchArr); iter; ++iter) {
|
|
TAINT_OBSERVER(TAINT_BIT_MUTATED, TAINT_BIT_NONE);
|
|
if (replIter) {
|
|
ret = ret.replace(iter.second().toString(),
|
|
replIter.second().toString(),
|
|
count, caseSensitive);
|
|
++replIter;
|
|
} else {
|
|
ret = ret.replace(iter.second().toString(),
|
|
"", count, caseSensitive);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
TAINT_OBSERVER(TAINT_BIT_MUTATED, TAINT_BIT_NONE);
|
|
String repl = replace.toString();
|
|
for (ArrayIter iter(searchArr); iter; ++iter) {
|
|
ret = ret.replace(iter.second().toString(), repl, count, caseSensitive);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
if (replace.is(KindOfArray)) {
|
|
raise_notice("Array to string conversion");
|
|
}
|
|
TAINT_OBSERVER(TAINT_BIT_MUTATED, TAINT_BIT_NONE);
|
|
return subject.replace(search.toString(), replace.toString(), count,
|
|
caseSensitive);
|
|
}
|
|
|
|
static Variant str_replace(CVarRef search, CVarRef replace, CVarRef subject,
|
|
int &count, bool caseSensitive) {
|
|
if (subject.is(KindOfArray)) {
|
|
Array arr = subject.toArray();
|
|
Array ret;
|
|
for (ArrayIter iter(arr); iter; ++iter) {
|
|
String replaced = str_replace(search, replace, iter.second().toString(),
|
|
count, caseSensitive);
|
|
ret.set(iter.first(), replaced);
|
|
}
|
|
return ret;
|
|
}
|
|
return str_replace(search, replace, subject.toString(), count,
|
|
caseSensitive);
|
|
}
|
|
|
|
Variant f_str_replace(CVarRef search, CVarRef replace, CVarRef subject,
|
|
VRefParam count /* = null */) {
|
|
int nCount = 0;
|
|
Variant ret = str_replace(search, replace, subject, nCount, true);
|
|
count = nCount;
|
|
return ret;
|
|
}
|
|
|
|
Variant f_str_ireplace(CVarRef search, CVarRef replace, CVarRef subject,
|
|
VRefParam count /* = null */) {
|
|
int nCount = 0;
|
|
Variant ret = str_replace(search, replace, subject, nCount, false);
|
|
count = nCount;
|
|
return ret;
|
|
}
|
|
|
|
Variant f_substr_replace(CVarRef str, CVarRef replacement, CVarRef start,
|
|
CVarRef length /* = 0x7FFFFFFF */) {
|
|
if (!str.is(KindOfArray)) {
|
|
String repl;
|
|
if (replacement.is(KindOfArray)) {
|
|
repl = replacement[0].toString();
|
|
} else {
|
|
repl = replacement.toString();
|
|
}
|
|
TAINT_OBSERVER(TAINT_BIT_MUTATED, TAINT_BIT_NONE);
|
|
if (start.is(KindOfArray)) {
|
|
if (!length.is(KindOfArray)) {
|
|
throw_invalid_argument("start and length should be of same type - "
|
|
"numerical or array");
|
|
return str;
|
|
}
|
|
Array startArr = start.toArray();
|
|
Array lengthArr = length.toArray();
|
|
if (startArr.size() != lengthArr.size()) {
|
|
throw_invalid_argument("start and length: (different item count)");
|
|
return str;
|
|
}
|
|
throw_invalid_argument("start and length as arrays not implemented");
|
|
return str;
|
|
}
|
|
return str.toString().replace(start.toInt32(), length.toInt32(), repl);
|
|
}
|
|
|
|
Array ret;
|
|
Array strArr = str.toArray();
|
|
Array startArr = start.toArray();
|
|
Array lengthArr = length.toArray();
|
|
ArrayIter startIter(startArr);
|
|
ArrayIter lengthIter(lengthArr);
|
|
|
|
if (replacement.is(KindOfArray)) {
|
|
Array replArr = replacement.toArray();
|
|
ArrayIter replIter(replArr);
|
|
for (ArrayIter iter(strArr); iter;
|
|
++iter, ++startIter, ++lengthIter) {
|
|
TAINT_OBSERVER(TAINT_BIT_MUTATED, TAINT_BIT_NONE);
|
|
int nStart = startIter.second().toInt32();
|
|
int nLength = lengthIter.second().toInt32();
|
|
String repl("");
|
|
if (replIter) {
|
|
repl = replIter.second().toString();
|
|
++replIter;
|
|
}
|
|
ret.append(iter.second().toString().replace(nStart, nLength, repl));
|
|
}
|
|
} else {
|
|
String repl = replacement.toString();
|
|
for (ArrayIter iter(strArr); iter;
|
|
++iter, ++startIter, ++lengthIter) {
|
|
TAINT_OBSERVER(TAINT_BIT_MUTATED, TAINT_BIT_NONE);
|
|
int nStart = startIter.second().toInt32();
|
|
int nLength = lengthIter.second().toInt32();
|
|
ret.append(iter.second().toString().replace(nStart, nLength, repl));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant f_substr(CStrRef str, int start, int length /* = 0x7FFFFFFF */) {
|
|
String ret = str.substr(start, length, true);
|
|
if (ret.isNull()) return false;
|
|
return ret;
|
|
}
|
|
String f_str_pad(CStrRef input, int pad_length, CStrRef pad_string /* = " " */,
|
|
int pad_type /* = k_STR_PAD_RIGHT */) {
|
|
return StringUtil::Pad(input, pad_length, pad_string,
|
|
(StringUtil::PadType)pad_type);
|
|
}
|
|
String f_str_repeat(CStrRef input, int multiplier) {
|
|
return StringUtil::Repeat(input, multiplier);
|
|
}
|
|
Variant f_wordwrap(CStrRef str, int width /* = 75 */, CStrRef wordbreak /* = "\n" */,
|
|
bool cut /* = false */) {
|
|
String ret = StringUtil::WordWrap(str, width, wordbreak, cut);
|
|
if (ret.isNull()) return false;
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
Variant f_printf(int _argc, CStrRef format, CArrRef _argv /* = null_array */) {
|
|
int len = 0; char *output = string_printf(format.data(), format.size(),
|
|
_argv, &len);
|
|
if (output == NULL) return false;
|
|
echo(output); free(output);
|
|
return len;
|
|
}
|
|
Variant f_vprintf(CStrRef format, CArrRef args) {
|
|
int len = 0; char *output = string_printf(format.data(), format.size(),
|
|
args, &len);
|
|
if (output == NULL) return false;
|
|
echo(output); free(output);
|
|
return len;
|
|
}
|
|
Variant f_sprintf(int _argc, CStrRef format, CArrRef _argv /* = null_array */) {
|
|
char *output = string_printf(format.data(), format.size(), _argv, NULL);
|
|
if (output == NULL) return false;
|
|
return String(output, AttachString);
|
|
}
|
|
Variant f_vsprintf(CStrRef format, CArrRef args) {
|
|
char *output = string_printf(format.data(), format.size(), args, NULL);
|
|
if (output == NULL) return false;
|
|
return String(output, AttachString);
|
|
}
|
|
|
|
Variant f_sscanf(int _argc, CStrRef str, CStrRef format, CArrRef _argv /* = null_array */) {
|
|
Variant ret;
|
|
int result;
|
|
result = string_sscanf(str, format, _argv.size(), ret);
|
|
if (SCAN_ERROR_WRONG_PARAM_COUNT == result) return uninit_null();
|
|
if (_argv.empty()) return ret;
|
|
|
|
if (ret.isArray()) {
|
|
Array retArray = ret.toArray();
|
|
for (int i = 0; i < retArray.size(); i++) {
|
|
((Array&)_argv).lvalAt(i) = retArray[i];
|
|
}
|
|
return retArray.size();
|
|
}
|
|
if (ret.isNull()) return 0;
|
|
return ret;
|
|
}
|
|
|
|
String f_chr(int64_t ascii) {
|
|
char buf[2]; buf[0] = ascii; buf[1] = 0;
|
|
return String(buf, 1, CopyString);
|
|
}
|
|
int64_t f_ord(CStrRef str) {
|
|
return (int64_t)(unsigned char)(*((const char *)str));
|
|
}
|
|
Variant f_money_format(CStrRef format, double number) {
|
|
String s = StringUtil::MoneyFormat(format, number);
|
|
if (s.isNull()) return false;
|
|
return s;
|
|
}
|
|
|
|
String f_number_format(double number, int decimals /* = 0 */,
|
|
CStrRef dec_point /* = "." */,
|
|
CStrRef thousands_sep /* = "," */) {
|
|
char ch_dec_point = '.';
|
|
if (!dec_point.isNull()) {
|
|
if (dec_point.size() >= 1) {
|
|
ch_dec_point = ((const char *)dec_point)[0];
|
|
} else {
|
|
ch_dec_point = 0;
|
|
}
|
|
}
|
|
char ch_thousands_sep = ',';
|
|
if (!thousands_sep.isNull()) {
|
|
if (thousands_sep.size() >= 1) {
|
|
ch_thousands_sep = ((const char *)thousands_sep)[0];
|
|
} else {
|
|
ch_thousands_sep = 0;
|
|
}
|
|
}
|
|
char *ret = string_number_format(number, decimals, ch_dec_point,
|
|
ch_thousands_sep);
|
|
return String(ret, AttachString);
|
|
}
|
|
|
|
int64_t f_strcmp(CStrRef str1, CStrRef str2) {
|
|
return string_strcmp(str1.data(), str1.size(), str2.data(), str2.size());
|
|
}
|
|
int64_t f_strncmp(CStrRef str1, CStrRef str2, int len) {
|
|
return string_strncmp(str1.data(), str1.size(), str2.data(), str2.size(),
|
|
len);
|
|
}
|
|
int64_t f_strnatcmp(CStrRef str1, CStrRef str2) {
|
|
return string_natural_cmp(str1.data(), str1.size(), str2.data(), str2.size(),
|
|
false);
|
|
}
|
|
int64_t f_strcasecmp(CStrRef str1, CStrRef str2) {
|
|
return bstrcasecmp(str1.data(), str1.size(), str2.data(), str2.size());
|
|
}
|
|
int64_t f_strncasecmp(CStrRef str1, CStrRef str2, int len) {
|
|
return string_strncasecmp(str1.data(), str1.size(), str2.data(), str2.size(),
|
|
len);
|
|
}
|
|
int64_t f_strnatcasecmp(CStrRef str1, CStrRef str2) {
|
|
return string_natural_cmp(str1.data(), str1.size(), str2.data(), str2.size(),
|
|
true);
|
|
}
|
|
int64_t f_strcoll(CStrRef str1, CStrRef str2) {
|
|
return strcoll(str1, str2);
|
|
}
|
|
|
|
Variant f_substr_compare(CStrRef main_str, CStrRef str, int offset,
|
|
int length /* = INT_MAX */,
|
|
bool case_insensitivity /* = false */) {
|
|
int s1_len = main_str.size();
|
|
int s2_len = str.size();
|
|
|
|
if (offset < 0) {
|
|
offset = s1_len + offset;
|
|
if (offset < 0) offset = 0;
|
|
}
|
|
if (offset >= s1_len || length <= 0) {
|
|
return false;
|
|
}
|
|
|
|
int cmp_len = s1_len - offset;
|
|
if (cmp_len < s2_len) cmp_len = s2_len;
|
|
if (cmp_len > length) cmp_len = length;
|
|
|
|
const char *s1 = main_str.data();
|
|
if (case_insensitivity) {
|
|
return bstrcasecmp(s1 + offset, cmp_len, str, cmp_len);
|
|
}
|
|
return string_ncmp(s1 + offset, str, cmp_len);
|
|
}
|
|
|
|
Variant f_strrchr(CStrRef haystack, CVarRef needle) {
|
|
Variant ret = f_strrpos(haystack, needle);
|
|
if (same(ret, false)) {
|
|
return false;
|
|
}
|
|
return haystack.substr(ret.toInt32());
|
|
}
|
|
|
|
Variant f_strstr(CStrRef haystack, CVarRef needle,
|
|
bool before_needle /* = false */) {
|
|
Variant ret = f_strpos(haystack, needle);
|
|
if (same(ret, false)) {
|
|
return false;
|
|
}
|
|
if (before_needle) {
|
|
return haystack.substr(0, ret.toInt32());
|
|
} else {
|
|
return haystack.substr(ret.toInt32());
|
|
}
|
|
}
|
|
|
|
Variant f_stristr(CStrRef haystack, CVarRef needle) {
|
|
Variant ret = f_stripos(haystack, needle);
|
|
if (same(ret, false)) {
|
|
return false;
|
|
}
|
|
return haystack.substr(ret.toInt32());
|
|
}
|
|
|
|
Variant f_strpbrk(CStrRef haystack, CStrRef char_list) {
|
|
if (char_list.empty()) {
|
|
throw_invalid_argument("char_list: (empty)");
|
|
return false;
|
|
}
|
|
const char *p = strpbrk(haystack, char_list);
|
|
if (p) {
|
|
return String(p, CopyString);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Variant f_strchr(CStrRef haystack, CVarRef needle) {
|
|
return f_strstr(haystack, needle);
|
|
}
|
|
|
|
Variant f_strpos(CStrRef haystack, CVarRef needle, int offset /* = 0 */) {
|
|
if (offset < 0 || offset > haystack.size()) {
|
|
raise_warning("Offset not contained in string");
|
|
return false;
|
|
}
|
|
int pos;
|
|
if (needle.isString()) {
|
|
String n(needle.toString());
|
|
if (n.length() == 0) {
|
|
raise_warning("Empty delimiter");
|
|
return false;
|
|
}
|
|
pos = haystack.find(n, offset);
|
|
} else {
|
|
pos = haystack.find(needle.toByte(), offset);
|
|
}
|
|
if (pos >= 0) return pos;
|
|
return false;
|
|
}
|
|
|
|
Variant f_stripos(CStrRef haystack, CVarRef needle, int offset /* = 0 */) {
|
|
if (offset < 0 || offset > haystack.size()) {
|
|
raise_warning("Offset not contained in string");
|
|
return false;
|
|
}
|
|
int pos;
|
|
if (needle.isString()) {
|
|
pos = haystack.find(needle.toString(), offset, false);
|
|
} else {
|
|
pos = haystack.find(needle.toByte(), offset, false);
|
|
}
|
|
if (pos >= 0) return pos;
|
|
return false;
|
|
}
|
|
|
|
Variant f_strrpos(CStrRef haystack, CVarRef needle, int offset /* = 0 */) {
|
|
if (offset < -haystack.size() || offset > haystack.size()) {
|
|
raise_warning("Offset is greater than the length of haystack string");
|
|
return false;
|
|
}
|
|
int pos;
|
|
if (needle.isString()) {
|
|
pos = haystack.rfind(needle.toString(), offset);
|
|
} else {
|
|
pos = haystack.rfind(needle.toByte(), offset);
|
|
}
|
|
if (pos >= 0) return pos;
|
|
return false;
|
|
}
|
|
|
|
Variant f_strripos(CStrRef haystack, CVarRef needle, int offset /* = 0 */) {
|
|
if (offset < -haystack.size() || offset > haystack.size()) {
|
|
raise_warning("Offset is greater than the length of haystack string");
|
|
return false;
|
|
}
|
|
int pos;
|
|
if (needle.isString()) {
|
|
pos = haystack.rfind(needle.toString(), offset, false);
|
|
} else {
|
|
pos = haystack.rfind(needle.toByte(), offset, false);
|
|
}
|
|
if (pos >= 0) return pos;
|
|
return false;
|
|
}
|
|
|
|
Variant f_substr_count(CStrRef haystack, CStrRef needle, int offset /* = 0 */,
|
|
int length /* = 0x7FFFFFFF */) {
|
|
int lenNeedle = needle.size();
|
|
if (lenNeedle == 0) {
|
|
throw_invalid_argument("needle: (empty)");
|
|
return false;
|
|
}
|
|
|
|
if (offset < 0 || offset > haystack.size()) {
|
|
throw_invalid_argument("offset: (out of range)");
|
|
return false;
|
|
}
|
|
if (length == 0x7FFFFFFF) {
|
|
length = haystack.size() - offset;
|
|
} else if (length <= 0 || length > haystack.size() - offset) {
|
|
throw_invalid_argument("length: (out of range)");
|
|
return false;
|
|
}
|
|
|
|
int count = 0;
|
|
int posMax = offset + length - lenNeedle;
|
|
for (int pos = haystack.find(needle, offset);
|
|
pos != -1 && pos <= posMax;
|
|
pos = haystack.find(needle, pos + lenNeedle)) {
|
|
++count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
Variant f_strspn(CStrRef str1, CStrRef str2, int start /* = 0 */,
|
|
int length /* = 0x7FFFFFFF */) {
|
|
const char *s1 = str1;
|
|
const char *s2 = str2;
|
|
int s1_len = str1.size();
|
|
int s2_len = str2.size();
|
|
|
|
if (!string_substr_check(s1_len, start, length)) {
|
|
return false;
|
|
}
|
|
|
|
s1 += start;
|
|
for (int pos = 0; pos < length; ++pos) {
|
|
if (memchr(s2, *(s1++), s2_len) == NULL) return pos;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
Variant f_strcspn(CStrRef str1, CStrRef str2, int start /* = 0 */,
|
|
int length /* = 0x7FFFFFFF */) {
|
|
const char *s1 = str1;
|
|
const char *s2 = str2;
|
|
int s1_len = str1.size();
|
|
int s2_len = str2.size();
|
|
|
|
if (!string_substr_check(s1_len, start, length)) {
|
|
return false;
|
|
}
|
|
|
|
s1 += start;
|
|
for (int pos = 0; pos < length; ++pos) {
|
|
if (memchr(s2, *(s1++), s2_len) != NULL) return pos;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
Variant f_strlen(CVarRef vstr) {
|
|
Variant::TypedValueAccessor tva = vstr.getTypedAccessor();
|
|
switch (Variant::GetAccessorType(tva)) {
|
|
case KindOfString:
|
|
case KindOfStaticString:
|
|
return Variant(Variant::GetStringData(tva)->size());
|
|
case KindOfArray:
|
|
raise_warning("strlen() expects parameter 1 to be string, array given");
|
|
return uninit_null();
|
|
case KindOfObject:
|
|
if (!f_method_exists(vstr, "__toString")) {
|
|
raise_warning("strlen() expects parameter 1 to be string, object given");
|
|
return uninit_null();
|
|
} //else fallback to default
|
|
default:
|
|
CStrRef str = vstr.toString();
|
|
return Variant(str.size());
|
|
}
|
|
}
|
|
|
|
Variant f_count_chars(CStrRef str, int64_t mode /* = 0 */) {
|
|
int chars[256];
|
|
memset((void*)chars, 0, sizeof(chars));
|
|
const unsigned char *buf = (const unsigned char *)(const char *)str;
|
|
for (int len = str.size(); len > 0; len--) {
|
|
chars[*buf++]++;
|
|
}
|
|
|
|
Array retarr;
|
|
char retstr[256];
|
|
int retlen = 0;
|
|
switch (mode) {
|
|
case 0:
|
|
for (int inx = 0; inx < 256; inx++) {
|
|
retarr.set(inx, chars[inx]);
|
|
}
|
|
return retarr;
|
|
case 1:
|
|
for (int inx = 0; inx < 256; inx++) {
|
|
if (chars[inx] != 0) {
|
|
retarr.set(inx, chars[inx]);
|
|
}
|
|
}
|
|
return retarr;
|
|
case 2:
|
|
for (int inx = 0; inx < 256; inx++) {
|
|
if (chars[inx] == 0) {
|
|
retarr.set(inx, chars[inx]);
|
|
}
|
|
}
|
|
return retarr;
|
|
case 3:
|
|
for (int inx = 0; inx < 256; inx++) {
|
|
if (chars[inx] != 0) {
|
|
retstr[retlen++] = inx;
|
|
}
|
|
}
|
|
return String(retstr, retlen, CopyString);
|
|
case 4:
|
|
for (int inx = 0; inx < 256; inx++) {
|
|
if (chars[inx] == 0) {
|
|
retstr[retlen++] = inx;
|
|
}
|
|
}
|
|
return String(retstr, retlen, CopyString);
|
|
}
|
|
|
|
throw_invalid_argument("mode: %d", mode);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Counts the number of words inside a string. If format of 1 is specified,
|
|
* then the function will return an array containing all the words
|
|
* found inside the string. If format of 2 is specified, then the function
|
|
* will return an associated array where the position of the word is the key
|
|
* and the word itself is the value.
|
|
*
|
|
* For the purpose of this function, 'word' is defined as a locale dependent
|
|
* string containing alphabetic characters, which also may contain, but not
|
|
* start with "'" and "-" characters.
|
|
*/
|
|
Variant f_str_word_count(CStrRef str, int64_t format /* = 0 */,
|
|
CStrRef charlist /* = "" */) {
|
|
int str_len = str.size();
|
|
switch (format) {
|
|
case 1:
|
|
case 2:
|
|
if (!str_len) {
|
|
return Array::Create();
|
|
}
|
|
break;
|
|
case 0:
|
|
if (!str_len) {
|
|
return 0LL;
|
|
}
|
|
break;
|
|
default:
|
|
throw_invalid_argument("format: %d", format);
|
|
return false;
|
|
}
|
|
|
|
char ch[256];
|
|
const char *char_list = charlist;
|
|
if (*char_list) {
|
|
string_charmask(charlist, charlist.size(), ch);
|
|
} else {
|
|
char_list = NULL;
|
|
}
|
|
|
|
int word_count = 0;
|
|
const char *s0 = str;
|
|
const char *p = s0;
|
|
const char *e = p + str_len;
|
|
|
|
// first character cannot be ' or -, unless explicitly allowed by the user
|
|
if ((*p == '\'' && (!char_list || !ch[(int)'\''])) ||
|
|
(*p == '-' && (!char_list || !ch[(int)'-']))) {
|
|
p++;
|
|
}
|
|
|
|
// last character cannot be -, unless explicitly allowed by the user
|
|
if (*(e - 1) == '-' && (!char_list || !ch[(int)'-'])) {
|
|
e--;
|
|
}
|
|
|
|
Array ret;
|
|
while (p < e) {
|
|
const char *s = p;
|
|
while (p < e &&
|
|
(isalpha(*p) || (char_list && ch[(unsigned char)*p]) ||
|
|
*p == '\'' || *p == '-')) {
|
|
p++;
|
|
}
|
|
if (p > s) {
|
|
switch (format) {
|
|
case 1:
|
|
ret.append(String(s, p - s, CopyString));
|
|
break;
|
|
case 2:
|
|
ret.set((int)(s - s0), String(s, p - s, CopyString));
|
|
break;
|
|
default:
|
|
word_count++;
|
|
break;
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
|
|
if (!format) {
|
|
return word_count;
|
|
}
|
|
return ret.isNull() ? Array::Create() : ret;
|
|
}
|
|
|
|
int64_t f_levenshtein(CStrRef str1, CStrRef str2, int cost_ins /* = 1 */,
|
|
int cost_rep /* = 1 */, int cost_del /* = 1 */) {
|
|
return string_levenshtein(str1, str1.size(), str2, str2.size(),
|
|
cost_ins, cost_rep, cost_del);
|
|
}
|
|
int64_t f_similar_text(CStrRef first, CStrRef second,
|
|
VRefParam percent /* = uninit_null() */) {
|
|
float p;
|
|
int ret = string_similar_text(first, first.size(), second, second.size(),
|
|
&p);
|
|
percent = p;
|
|
return ret;
|
|
}
|
|
Variant f_soundex(CStrRef str) {
|
|
if (str.empty()) return false;
|
|
return String(string_soundex(str), AttachString);
|
|
}
|
|
Variant f_metaphone(CStrRef str, int phones /* = 0 */) {
|
|
char *ret = string_metaphone(str, str.size(), 0, 1);
|
|
if (ret) {
|
|
return String(ret, AttachString);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
String f_html_entity_decode(CStrRef str, int quote_style /* = k_ENT_COMPAT */,
|
|
CStrRef charset /* = "ISO-8859-1" */) {
|
|
const char *scharset = charset.data();
|
|
if (!*scharset) scharset = "UTF-8";
|
|
return StringUtil::HtmlDecode(str, (StringUtil::QuoteStyle)quote_style,
|
|
scharset, true);
|
|
}
|
|
String f_htmlentities(CStrRef str, int quote_style /* = k_ENT_COMPAT */,
|
|
CStrRef charset /* = "ISO-8859-1" */,
|
|
bool double_encode /* = true */) {
|
|
// dropping double_encode parameters and see runtime/base/zend_html.h
|
|
const char *scharset = charset.data();
|
|
if (!*scharset) scharset = "UTF-8";
|
|
return StringUtil::HtmlEncode(str, (StringUtil::QuoteStyle)quote_style,
|
|
scharset, true);
|
|
}
|
|
String f_htmlspecialchars_decode(CStrRef str,
|
|
int quote_style /* = k_ENT_COMPAT */) {
|
|
return StringUtil::HtmlDecode(str, (StringUtil::QuoteStyle)quote_style,
|
|
"UTF-8", false);
|
|
}
|
|
String f_htmlspecialchars(CStrRef str, int quote_style /* = k_ENT_COMPAT */,
|
|
CStrRef charset /* = "ISO-8859-1" */,
|
|
bool double_encode /* = true */) {
|
|
// dropping double_encode parameters and see runtime/base/zend_html.h
|
|
const char *scharset = charset.data();
|
|
if (!*scharset) scharset = "UTF-8";
|
|
return StringUtil::HtmlEncode(str, (StringUtil::QuoteStyle)quote_style,
|
|
scharset, false);
|
|
}
|
|
String f_fb_htmlspecialchars(CStrRef str, int quote_style /* = k_ENT_COMPAT */,
|
|
CStrRef charset /* = "ISO-8859-1" */,
|
|
CArrRef extra /* = Array() */) {
|
|
return StringUtil::HtmlEncodeExtra(str, (StringUtil::QuoteStyle)quote_style,
|
|
charset.data(), false, extra);
|
|
}
|
|
String f_quoted_printable_encode(CStrRef str) {
|
|
return StringUtil::QuotedPrintableEncode(str);
|
|
}
|
|
String f_quoted_printable_decode(CStrRef str) {
|
|
return StringUtil::QuotedPrintableDecode(str);
|
|
}
|
|
Variant f_convert_uudecode(CStrRef data) {
|
|
String ret = StringUtil::UUDecode(data);
|
|
if (ret.isNull()) {
|
|
return false; // bad format
|
|
}
|
|
return ret;
|
|
}
|
|
Variant f_convert_uuencode(CStrRef data) {
|
|
if (data.empty()) return false;
|
|
return StringUtil::UUEncode(data);
|
|
}
|
|
String f_str_rot13(CStrRef str) {
|
|
return StringUtil::ROT13(str);
|
|
}
|
|
int64_t f_crc32(CStrRef str) {
|
|
return (uint32_t)StringUtil::CRC32(str);
|
|
}
|
|
String f_crypt(CStrRef str, CStrRef salt /* = "" */) {
|
|
return StringUtil::Crypt(str, salt);
|
|
}
|
|
String f_md5(CStrRef str, bool raw_output /* = false */) {
|
|
return StringUtil::MD5(str, raw_output);
|
|
}
|
|
String f_sha1(CStrRef str, bool raw_output /* = false */) {
|
|
return StringUtil::SHA1(str, raw_output);
|
|
}
|
|
|
|
Variant f_strtr(CStrRef str, CVarRef from, CVarRef to /* = null_variant */) {
|
|
if (str.empty()) {
|
|
return str;
|
|
}
|
|
|
|
if (!to.isNull()) {
|
|
TAINT_OBSERVER(TAINT_BIT_MUTATED, TAINT_BIT_NONE);
|
|
return StringUtil::Translate(str, from.toString(), to.toString());
|
|
}
|
|
|
|
if (!from.is(KindOfArray)) {
|
|
throw_invalid_argument("2nd argument: (not array)");
|
|
return false;
|
|
}
|
|
|
|
int maxlen = 0;
|
|
int minlen = -1;
|
|
Array arr = from.toArray();
|
|
|
|
if (arr.empty()) {
|
|
// Nothing to translate
|
|
return str;
|
|
}
|
|
|
|
for (ArrayIter iter(arr); iter; ++iter) {
|
|
String search = iter.first();
|
|
int len = search.size();
|
|
if (len < 1) return false;
|
|
if (maxlen < len) maxlen = len;
|
|
if (minlen == -1 || minlen > len) minlen = len;
|
|
}
|
|
|
|
TAINT_OBSERVER(TAINT_BIT_MUTATED, TAINT_BIT_NONE);
|
|
const char *s = str.data();
|
|
int slen = str.size();
|
|
char *key = (char *)malloc(maxlen+1);
|
|
|
|
StringBuffer result(slen);
|
|
for (int pos = 0; pos < slen; ) {
|
|
if ((pos + maxlen) > slen) {
|
|
maxlen = slen - pos;
|
|
}
|
|
bool found = false;
|
|
memcpy(key, s + pos, maxlen);
|
|
for (int len = maxlen; len >= minlen; len--) {
|
|
key[len] = 0;
|
|
if (arr.exists(key)) {
|
|
String replace = arr[key].toString();
|
|
if (!replace.empty()) {
|
|
result += replace;
|
|
}
|
|
pos += len;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
result += s[pos++];
|
|
}
|
|
}
|
|
free(key);
|
|
|
|
return result.detach();
|
|
}
|
|
|
|
void f_parse_str(CStrRef str, VRefParam arr /* = null */) {
|
|
arr = Array::Create();
|
|
HttpProtocol::DecodeParameters(arr, str.data(), str.size());
|
|
}
|
|
|
|
Variant f_setlocale(int _argc, int category, CVarRef locale, CArrRef _argv /* = null_array */) {
|
|
Array argv = _argv;
|
|
if (locale.is(KindOfArray)) {
|
|
if (!argv.empty()) throw_invalid_argument("locale: not string)");
|
|
argv = locale; // ignore _argv
|
|
}
|
|
|
|
for (int i = -1; i < argv.size(); i++) {
|
|
String slocale;
|
|
if (i == -1) {
|
|
if (locale.is(KindOfArray)) continue;
|
|
slocale = locale.toString();
|
|
} else {
|
|
slocale = argv[i].toString();
|
|
}
|
|
|
|
const char *loc = slocale;
|
|
if (slocale.size() >= 255) {
|
|
throw_invalid_argument("locale name is too long: %s", loc);
|
|
return false;
|
|
}
|
|
if (strcmp("0", loc) == 0) {
|
|
loc = NULL;
|
|
}
|
|
{
|
|
Lock lock(s_mutex);
|
|
const char *retval = setlocale(category, loc);
|
|
if (retval) {
|
|
return String(retval, CopyString);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Array f_localeconv() {
|
|
struct lconv currlocdata;
|
|
{
|
|
Lock lock(s_mutex);
|
|
struct lconv *res = localeconv();
|
|
currlocdata = *res;
|
|
}
|
|
|
|
Array ret;
|
|
#define SET_LOCALE_STRING(x) ret.set(#x, String(currlocdata.x, CopyString))
|
|
SET_LOCALE_STRING(decimal_point);
|
|
SET_LOCALE_STRING(thousands_sep);
|
|
SET_LOCALE_STRING(int_curr_symbol);
|
|
SET_LOCALE_STRING(currency_symbol);
|
|
SET_LOCALE_STRING(mon_decimal_point);
|
|
SET_LOCALE_STRING(mon_thousands_sep);
|
|
SET_LOCALE_STRING(positive_sign);
|
|
SET_LOCALE_STRING(negative_sign);
|
|
#define SET_LOCALE_INTEGER(x) ret.set(#x, currlocdata.x)
|
|
SET_LOCALE_INTEGER(int_frac_digits);
|
|
SET_LOCALE_INTEGER(frac_digits);
|
|
SET_LOCALE_INTEGER(p_cs_precedes);
|
|
SET_LOCALE_INTEGER(p_sep_by_space);
|
|
SET_LOCALE_INTEGER(n_cs_precedes);
|
|
SET_LOCALE_INTEGER(n_sep_by_space);
|
|
SET_LOCALE_INTEGER(p_sign_posn);
|
|
SET_LOCALE_INTEGER(n_sign_posn);
|
|
|
|
Array grouping, mon_grouping;
|
|
|
|
/* Grab the grouping data out of the array */
|
|
int len = strlen(currlocdata.grouping);
|
|
for (int i = 0; i < len; i++) {
|
|
grouping.set(i, currlocdata.grouping[i]);
|
|
}
|
|
ret.set("grouping", grouping);
|
|
|
|
/* Grab the monetary grouping data out of the array */
|
|
len = strlen(currlocdata.mon_grouping);
|
|
for (int i = 0; i < len; i++) {
|
|
mon_grouping.set(i, currlocdata.mon_grouping[i]);
|
|
}
|
|
ret.set("mon_grouping", mon_grouping);
|
|
|
|
return ret;
|
|
}
|
|
|
|
String f_nl_langinfo(int item) {
|
|
return nl_langinfo(item);
|
|
}
|
|
|
|
String f_convert_cyr_string(CStrRef str, CStrRef from, CStrRef to) {
|
|
char ch_from = ((const char *)from)[0];
|
|
char ch_to = ((const char *)to)[0];
|
|
char *ret = string_convert_cyrillic_string(str.data(), str.size(),
|
|
ch_from, ch_to);
|
|
return String(ret, str.size(), AttachString);
|
|
}
|
|
|
|
#define ENT_HTML_QUOTE_NONE 0
|
|
#define ENT_HTML_QUOTE_SINGLE 1
|
|
#define ENT_HTML_QUOTE_DOUBLE 2
|
|
|
|
#define ENT_COMPAT ENT_HTML_QUOTE_DOUBLE
|
|
#define ENT_QUOTES (ENT_HTML_QUOTE_DOUBLE | ENT_HTML_QUOTE_SINGLE)
|
|
#define ENT_NOQUOTES ENT_HTML_QUOTE_NONE
|
|
|
|
static const HtmlBasicEntity basic_entities[] = {
|
|
{ '"', """, 6, ENT_HTML_QUOTE_DOUBLE },
|
|
{ '\'', "'", 6, ENT_HTML_QUOTE_SINGLE },
|
|
{ '\'', "'", 5, ENT_HTML_QUOTE_SINGLE },
|
|
{ '<', "<", 4, 0 },
|
|
{ '>', ">", 4, 0 },
|
|
{ 0, NULL, 0, 0 }
|
|
};
|
|
|
|
Array f_get_html_translation_table(int table, int quote_style) {
|
|
static entity_charset charset = determine_charset(nullptr); // get default one
|
|
char ind[2]; ind[1] = 0;
|
|
|
|
assert(charset != entity_charset_enum::cs_unknown);
|
|
|
|
const int HTML_SPECIALCHARS = 0;
|
|
const int HTML_ENTITIES = 1;
|
|
|
|
using namespace entity_charset_enum;
|
|
|
|
Array ret;
|
|
switch (table) {
|
|
case HTML_ENTITIES: {
|
|
auto entity_map = html_get_entity_map();
|
|
|
|
for (int j = 0; entity_map[j].charset != cs_terminator; j++) {
|
|
const html_entity_map &em = entity_map[j];
|
|
if (em.charset != charset)
|
|
continue;
|
|
|
|
for (int i = 0; i <= em.endchar - em.basechar; i++) {
|
|
char buffer[16];
|
|
|
|
if (em.table[i] == NULL)
|
|
continue;
|
|
/* what about wide chars here ?? */
|
|
ind[0] = i + em.basechar;
|
|
snprintf(buffer, sizeof(buffer), "&%s;", em.table[i]);
|
|
ret.set(ind, String(buffer, CopyString));
|
|
}
|
|
}
|
|
/* fall thru */
|
|
}
|
|
case HTML_SPECIALCHARS:
|
|
for (int j = 0; basic_entities[j].charcode != 0; j++) {
|
|
if (basic_entities[j].flags &&
|
|
(quote_style & basic_entities[j].flags) == 0)
|
|
continue;
|
|
|
|
ind[0] = (unsigned char)basic_entities[j].charcode;
|
|
ret.set(String(ind, 2, CopyString), basic_entities[j].entity);
|
|
}
|
|
ret.set("&", "&");
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
String f_hebrev(CStrRef hebrew_text, int max_chars_per_line /* = 0 */) {
|
|
if (hebrew_text.empty()) return hebrew_text;
|
|
int len = hebrew_text.size();
|
|
char *ret = string_convert_hebrew_string(hebrew_text.data(), len,
|
|
max_chars_per_line, false);
|
|
return String(ret, len, AttachString);
|
|
}
|
|
|
|
String f_hebrevc(CStrRef hebrew_text, int max_chars_per_line /* = 0 */) {
|
|
if (hebrew_text.empty()) return hebrew_text;
|
|
int len = hebrew_text.size();
|
|
char *ret = string_convert_hebrew_string(hebrew_text.data(), len,
|
|
max_chars_per_line, true);
|
|
return String(ret, len, AttachString);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|