PHPソースのexplode使用説明
6587 ワード
配列をある文字や文字列に基づいて配列に分割する必要がある時、explodeはとても良いppyを使っていますが、explodeはどのように働いているかご存知ですか?~まず、explodeも空間を割り当てていることは間違いないです。
// 1:ext/standard/string.c
// explode
PHP_FUNCTION(explode)
{
char *str, *delim;
int str_len = 0, delim_len = 0;
long limit = LONG_MAX; /* No limit */
zval zdelim, zstr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &delim, &delim_len, &str, &str_len, &limit) == FAILURE) {
return;
}
if (delim_len == 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
RETURN_FALSE;
}
// ,
array_init(return_value);
// , explode('|', '');
if (str_len == 0) {
if (limit >= 0) {
add_next_index_stringl(return_value, "", sizeof("") - 1, 1);
}
return;
}
// _zval_struct ,
//ZVAL_STRINGL ~~
ZVAL_STRINGL(&zstr, str, str_len, 0);
ZVAL_STRINGL(&zdelim, delim, delim_len, 0);
//limit explode explode ,
if (limit > 1) {
php_explode(&zdelim, &zstr, return_value, limit);
} else if (limit < 0) {
php_explode_negative_limit(&zdelim, &zstr, return_value, limit);
} else {
add_index_stringl(return_value, 0, str, str_len, 1);
}
}
//ZVAL_STRINGL :
// 2:zend/zend_API.c
#define ZVAL_STRINGL(z, s, l, duplicate) { \
const char *__s=(s); int __l=l; \
Z_STRLEN_P(z) = __l; \
Z_STRVAL_P(z) = (duplicate?estrndup(__s, __l):(char*)__s);\
Z_TYPE_P(z) = IS_STRING; \
}
....
//estrndup :
// 3:zend/zend_alloc.h
#define estrndup(s, length) _estrndup((s), (length) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
....
//_estrndup : zend/zend_alloc.c
ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
char *p;
p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
if (UNEXPECTED(p == NULL)) {
return p;
}
memcpy(p, s, length); //
p[length] = 0;
return p;
}
// substr strrchr strstr ZVAL_STRING
以下、explodeの3番目のパラメータlimitに基づいて呼出を分析します。条件に対応するのはexplodeの最後の3行です。limit条件に対する違いがあります。MAX、つまり分岐1の場合1、limit>1:PHp_を呼び出します。explodeメソッドは、ext/standard/streg.cにおいても発見でき、explodeに続いて実現される上に現れるので(本関数の検索で本ファイルからのメソッドを呼び出すときに便利で、ほとんどの列の外はこの関数の直後の上にある^u^)、
PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, long limit)
{
char *p1, *p2, *endp;
//
endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);
//
p1 = Z_STRVAL_P(str);
// str , strrpos strpos
p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
if (p2 == NULL) {
// , explode('|', 'abc'); , array(0 => 'abc')
add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1);
} else {
// ,
do {
// ( ,
add_next_index_stringl(return_value, p1, p2 - p1, 1);
// p2+
// , ='|', = 'ab|c', p2 = 2, p1=2+1=3
p1 = p2 + Z_STRLEN_P(delim);
} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&
--limit > 1);
//
//explode('|', 'avc|sdf'); => array(0 => 'avc', 1= > 'sdf')
if (p1 <= endp)
add_next_index_stringl(return_value, p1, endp-p1, 1);
}
}
、limit<0:php_を呼び出します。explode_ネガティヴlimitメソッド
PHPAPI void php_explode_negative_limit(zval *delim, zval *str, zval *return_value, long limit)
{
#define EXPLODE_ALLOC_STEP 64
char *p1, *p2, *endp;
endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);
p1 = Z_STRVAL_P(str);
p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
if (p2 == NULL) {
// , explode('|', 'abc', -1) ,
/*
do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
by doing nothing we return empty array
*/
} else {
int allocated = EXPLODE_ALLOC_STEP, found = 0;
long i, to_return;
char **positions = emalloc(allocated * sizeof(char *));
// positions ,
positions[found++] = p1; //
// , ,
do {
if (found >= allocated) {
allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
positions = erealloc(positions, allocated*sizeof(char *));
}
positions[found++] = p1 = p2 + Z_STRLEN_P(delim);
} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL);
//
to_return = limit + found;
/* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
for (i = 0;i < to_return;i++) { /* this checks also for to_return > 0 */
add_next_index_stringl(return_value, positions[i],
(positions[i+1] - Z_STRLEN_P(delim)) - positions[i],
1
);
}
efree(positions);// ,
}
#undef EXPLODE_ALLOC_STEP
}
、limit=1 or limit=0:すべての第一条件と第二条件が満たされていないときに入るこの分岐は、単純にソース文字列を出力配列に入れて、explode(''avc𞓜sd',1)or explode(',avc',avars')に返します。
//add_index_stringl
// 4:zend/zend_API.c
ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int duplicate) /* {{{ */
{
zval *tmp;
MAKE_STD_ZVAL(tmp);
ZVAL_STRINGL(tmp, str, length, duplicate);
return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp, sizeof(zval *), NULL);
}
//zend_hash_next_index_insert
//zend/zend_hash.h
#define zend_hash_next_index_insert(ht, pData, nDataSize, pDest) \
_zend_hash_index_update_or_next_insert(ht, 0, pData, nDataSize, pDest, HASH_NEXT_INSERT ZEND_FILE_LINE_CC)
//zend/zend_hash.c
/// ~~~~
で見られます(分配空間などは含まれていません)。limit>1の場合、効率はO(N)、Nはlimit値です。limit<0の場合、効率はO(N+M)、Nはlimit値、Mは分割出現回数です。limit=1 or limit=0の場合、効率はO(1)です。