search
HomeBackend DevelopmentPHP TutorialPHP string management zend_string

PHP string management zend_string

String management: zend_string

Any program needs to manage strings. Here we'll detail a custom solution for your PHP needs: zend_string. Every time PHP needs to work with a string, it uses the zend_string structure. This structure is just a simple thin wrapper of the C language's char * string type.

It adds the function of memory management, so the same string can be shared in multiple places without duplication. Additionally, some strings are "internally" i.e. "persistently" allocated and specially managed by memory management so that they are not destroyed across multiple requests. After that, those get permanent allocation from Zend memory management.

Related learning recommendations: PHP programming from entry to proficiency

Structure and access macros

Here is a simplezend_string Structure:

struct _zend_string {
        zend_refcounted_h gc;
        zend_ulong        h;
        size_t            len;
        char              val[1];
};

As you can see, this structure has a zend_refcounted_h header embedded inside it. This is needed for memory management and references. Since this string is most likely to be used as a key for a hash table check, it embeds its hash value in the h field. This is an unsigned long zend_ulong. It is only used when hashing zend_string is required, especially when used with hash table: zend_array. This is very possible.

As you know, strings know their length as a len field to support "binary strings. Binary strings are embedded with one or more NUL characters (\0) strings. When passed to library functions, those strings will be truncated, otherwise their length cannot be calculated correctly. So in zend_string, the length of the string is always known. Please Note that the length is calculated in ASCII characters (bytes), not the final NUL, but the final middle NUL. For example, the string "foo" is in zend_string is stored as "foo\0", and its length is 3. In addition, the string "foo\0bar" will be stored as "foo\0bar\0", and its length is 7.

Finally, the The character is stored in char[1]. This is not char *, but char[1]. Why? This is a hack called "C struct ” memory optimization (you can use search engines with these terms). Basically, it allows the engine to allocate space for the zend_string structure and the characters to be stored, as a separate C pointer. This optimizes memory, because the memory access will be to one contiguously allocated block rather than two scattered blocks (one to store zend_string * and the other to store char *).

This struct hack must be remembered, since the memory layout looks like the C character is at the end of the C zend_string structure, so it may feel like this when using a C debugger (or debug string) Got/seen it. This hack is completely managed by the API and will be used when you manipulate the zend_string structure.

PHP string management zend_string

Use zend_string API

Simple use case

Like Zvals, you don't need to manually manipulate zend_string internal fields, instead always use macros for this. There are also macros that trigger string operations. This is not Functions, but macros, are stored in the required Zend/zend_string.h header file:

zend_string *str;

str = zend_string_init("foo", strlen("foo"), 0);
php_printf("This is my string: %s\n", ZSTR_VAL(str));
php_printf("It is %zd char long\n", ZSTR_LEN(str));

zend_string_release(str);

The simple example above shows you basic string management. It should be zend_string_init() Function (actually a macro, but let's ignore it for now) gives the complete char * C string and its length. The last argument of type int should be 0 or 1. If passed 0, requires the engine to use request-bound heap allocations through Zend memory management. This allocation is destroyed when the current request ends. If you don't do this, in debug builds, the engine will alert you to memory leaks. If 1 is passed, a so-called "persistent" allocation is requested, and the engine will use traditional C malloc() calls and will not track memory allocations in any way.

NOTE

If you need more information about memory management, you can read the dedicated chapter.

然后,我们来显示字符串。我们使用 ZSTR_VAL() 宏访问字符数组。ZSTR_LEN() 允许访问长度信息。zend_string 相关宏都以 ZSTR_**() 开始,注意和 Z_STR**() 宏不一样。

注意

长度使用 size_t 类型存储,为了显示它,printf() 必须使用 “%zd”。你应该总是使用正确的printf()格式。否则可能会导致应用程序崩溃或创建安全问题否则可能会导致内存泄漏和。有关 printf() 格式的详细信息,请访问此链接

最后,我们使用 zend_string_release()释放字符串。该释放是强制的。这与内存管理有关。“释放”是一个简单的操作:字符串的引用计数递减,如果减到0,API会为你释放字符串。如果忘记释放字符串,则很可能造成内存泄漏。

注意

在 C 语言中,你必须总是考虑内存管理。如果你分配——不管是直接使用 malloc(),或者使用能为你这样做的 API,在某些时候你必须使用 free()。否则可能会导致内存泄漏,并转换为任何人都不能安全使用的糟糕设计程序。 

玩转 hash

如果你需要访问哈希值,可使用 ZSTR_H()。但创建 zend_string 时,不会自动计算其哈希值。而当将该字符串与 HashTable API 一起使用时,它将为你完成。如果你强制立即计算哈希值,可使用 ZSTR_HASH() 或 zend_string_hash_val()。当哈希值被计算出来,它会被保存起来并且不再被计算。无论如何,你必须使用  zend_string_forget_hash_val() 重新计算——因为你改变了字符串的值:

zend_string *str;

str = zend_string_init("foo", strlen("foo"), 0);
php_printf("This is my string: %s\n", ZSTR_VAL(str));
php_printf("It is %zd char long\n", ZSTR_LEN(str));

zend_string_hash_val(str);
php_printf("The string hash is %lu\n", ZSTR_H(str));

zend_string_forget_hash_val(str);
php_printf("The string hash is now cleared back to 0!");

zend_string_release(str);

字符串复制和内存管理

 zend_string API 的一个非常棒的特性是:允许某部分通过简单的声明“拥有”字符串。引擎不会在内存复制字符串,而是递增其引用计数(作为字符串zend_refcounted_h 的一部分)。这允许在代码的多个地方共享一个内存。

由此,当我们讨论“复制”一个 zend_string 时,实际上并没有复制内存中的任何东西。如果需要(这仍是可能的操作),之后我们来讨论“复制”字符串。开始吧:

zend_string *foo, *bar, *bar2, *baz;

foo = zend_string_init("foo", strlen("foo"), 0); /* 创建变量foo,值为“foo” */
bar = zend_string_init("bar", strlen("bar"), 0); /* 创建变量bar,值为"bar" */

/* 创建变量bar2,共享变量bar的值。
  另外递增"bar"字符串的引用计数到2 */
bar2 = zend_string_copy(bar);

php_printf("We just copied two strings\n");
php_printf("See : bar content : %s, bar2 content : %s\n", ZSTR_VAL(bar), ZSTR_VAL(bar2));

/* 在内存中复制"bar"字符串,创建变量 baz,
使 baz 单独拥有新创建的"bar"字符串 */
baz = zend_string_dup(bar, 0);

php_printf("We just duplicated 'bar' in 'baz'\n");
php_printf("Now we are free to change 'baz' without fearing to change 'bar'\n");

/* 更改第二个"bar"字符串的最后一个字符,
变为"baz" */
ZSTR_VAL(baz)[ZSTR_LEN(baz) - 1] = 'z';

/* 当字符串改变时,忘记旧哈希值(如果已计算),
因此其哈希值必须更改并重新计数 */
zend_string_forget_hash_val(baz);

php_printf("'baz' content is now %s\n", ZSTR_VAL(baz));

zend_string_release(foo);  /* 销毁(释放)"foo"字符串 */
zend_string_release(bar);  /* 递减"bar"字符串的引用计数到1 */
zend_string_release(bar2); /* 销毁(释放)bar和bar2变量中的"bar"字符串 */
zend_string_release(baz);  /* 销毁(释放)"baz"字符串 */

我们一开始仅分配 “foo” 和 “bar”。然后,我们创建 bar的副本到bar2字符串。这里,必须记住:在内存中,bar 和 bar2 指向同一 C 字符串,更改一个将更改第二个。这是 zend_string_copy() 行为:它仅递增 C 字符串的引用计数。

如果想要分离字符串,即想在内存中拥有该字符串的两个不同副本,我们必须使用 zend_string_dup()复制。然后我们将 bar2 变量字符串复制到 baz 变量。现在,baz 变量嵌入它的字符串副本,并且可以改变它而不影响 bar2 。这就是我们要做的:我们用‘z’改变了‘bar’最后的‘r’,之后,我们显示它,并释放所有字符串。

注意,我们忘记哈希值(如果它在之前已经计算,则不需要考虑其细节)。这是一个值得记住的好习惯。就像我们曾说过,如果 zend_string 作为 HashTables 的一部分,则使用哈希值。这在开发中是很常见的,并且改变字符串的值必须重新计算哈希值。忘记这一步骤将导致可能需要花一些时间去追踪错误。

字符串操作

zend_string API 允许其他操作,例如扩展或缩小字符串,更改大小写或比较字符串。目前尚未有连接字符串操作,但是很容易执行:

zend_string *FOO, *bar, *foobar, *foo_lc;

FOO = zend_string_init("FOO", strlen("FOO"), 0);
bar = zend_string_init("bar", strlen("bar"), 0);

/* 将 zend_string 与 C 字符串文字进行比较 */
if (!zend_string_equals_literal(FOO, "foobar")) {
    foobar = zend_string_copy(FOO);

    /* realloc() 将 C 字符串分配到更大的缓冲区 */
    foobar = zend_string_extend(foobar, strlen("foobar"), 0);

    /* 在重新分配的足够大的“FOO”之后,连接"bar" */
    memcpy(ZSTR_VAL(foobar) + ZSTR_LEN(FOO), ZSTR_VAL(bar), ZSTR_LEN(bar));
}

php_printf("This is my new string: %s\n", ZSTR_VAL(foobar));

/* 比较两个 zend_string */
if (!zend_string_equals(FOO, foobar)) {
    /*复制字符串并改为小写*/
    foo_lc = zend_string_tolower(foo);
}

php_printf("This is FOO in lower-case: %s\n", ZSTR_VAL(foo_lc));

/* 释放内存 */
zend_string_release(FOO);
zend_string_release(bar);
zend_string_release(foobar);
zend_string_release(foo_lc);

使用 zval 访问 zend_string

现在你知道如何管理和操作 zend_string,让我们看看它们与 zval 容器的互动。

注意

你必须熟悉 zval,如果不熟悉,阅读Zvals专用章节。

宏将允许你将 zend_string 存储到 zval,或从 zval 读取 zend_string

zval myval;
zend_string *hello, *world;

zend_string_init(hello, "hello", strlen("hello"), 0);

/* 存储字符串到 zval */
ZVAL_STR(&myval, hello);

/* 从 zval 的 zend_string 中读取 C 字符串 */
php_printf("The string is %s", Z_STRVAL(myval));

zend_string_init(world, "world", strlen("world"), 0);

/* 将 zend_string 更改为 myval:将其替换为另一个 */
Z_STR(myval) = world;

/* ... */

你必须记住的是,以ZSTR_***(s)开头的每个宏都会作用到 zend_string

  • ZSTR_VAL()
  • ZSTR_LEN()
  • ZSTR_HASH()

每个以 Z_STR**(z) 开头的宏都会作用于嵌入到 zval 中的 zend_string 。

  • Z_STRVAL()
  • Z_STRLEN()
  • Z_STRHASH()

还有一些你可能不需要的东西也存在。

PHP 的历史和经典的 C 字符串

简单介绍一下。在 C 语言中,字符串是字符数组(char foo[])或者指向字符的指针(char *)。它们并不知道其长度,这就是它们为什么末尾是 NUL(知道字符串的开始和结尾,就可以知道它的长度)。

在 PHP 7 之前,zend_string 结构还未出现。在那时,还是使用传统的 char * / int。你可能仍会在 PHP 源代码中找到使用了罕见的 char * / int,而不是 zend_string。你也可能发现 API 功能,可以一边使用 zend_string,另一边使用 char * / int来交互。

在任何可能的地方:使用 zend_string。那些罕见的没有使用 zend_string 的地方,是因为在那里使用它们并没有什么意义,但是你仍会发现在 PHP 源代码中有很多对 zend_string 的引用。

Interned zend_string

在这里简单的介绍一下 interned 字符串。你在扩展开发中应该需要这样的概念。Interned 字符串也和 OPCache 扩展交互。

Interned 字符串是去重复的字符串。当与 OPCache 一起使用时,它还可以在请求之间循环使用。

假设你想要创建字符串“foo”。你更想做的是简单地创建一个新字符串“foo”:

zend_string *foo;
foo = zend_string_init("foo", strlen("foo"), 0);

/* ... */

但是有一个问题:字符串是不是在你需要之前已经创建了?当你需要一个字符串时,你的代码会在PHP生命中的某个时刻执行,这意味着在你需要完全相同的字符串(在我们的示例中为“ foo”)之前发生了一些代码。

Interned 字符串是关于要求引擎去探查 interned 字符串存储,并且如果它能找到你的字符串,会重用已经分配的指针。如果没有找到:创建一个新的字符串并“intern” 它,这使得它可用于 PHP 源代码的其他部分(其他扩展,引擎本身等)。

这里有个例子:

zend_string *foo;
foo = zend_string_init("foo", strlen("foo"), 0);

foo = zend_new_interned_string(foo);

php_printf("This string is interned : %s", ZSTR_VAL(foo));

zend_string_release(foo);

上面的代码创建了一个非常经典的新 zend_string 。然后,我们将创建的 zend_string 传递给 zend_new_interned_string()。该函数在引擎 interned 字符串缓冲区查找相同的字符串(这里是“foo”)。如果找到它(意味着有人已经创建了这样的字符串),那么它将释放你的字符串(可能释放它),并且用 interned 字符串缓冲区中的字符串替代它。如果找不到:它将被添加到 interned 字符串缓冲区,使它在将来可使用或可用于 PHP 的其他部分。

你必须注意内存分配。Interned 字符串总是将 refcount 设为1,因为它们不必被引用,由于它们会和 interned 字符串缓冲区共享,因此不可被销毁。

例:

zend_string *foo, *foo2;

foo  = zend_string_init("foo", strlen("foo"), 0);
foo2 = zend_string_copy(foo); /* 递增 foo 的引用计数 */

 /* 引用计数退回 1,即使现在字符串在三个不同的地方被使用 */
foo = zend_new_interned_string(foo);

/* 这没有任何作用,因为 foo 是 interned */
zend_string_release(foo);

/*  这没有任何作用,因为 foo2 是 interned*/
zend_string_release(foo2);

/* 在流程结束时,PHP 将清除它的 interned 字符串缓冲区,
  因此 free() 我们 "foo" 字符串本身 */

这都是关于垃圾收集的。

当字符串是 interned,更改其 GC 标志以添加  IS_STR_INTERNED 标志,不管使用的是什么内存分配类(基于永久或基于请求)。当你想要复制或释放字符串,都会检查该标志。如果是 interned 字符串,当你复制该字符串时,引擎不会递增它的引用计数。但是如果你释放字符串,它也不会递减或释放它。它不做任何事情。在进程生命周期的最后,它会销毁它的 interned 字符串缓冲区,并且释放你的 interned 字符串。

事实上,此过程比这更为复杂。如果你使用的是请求处理中的 interned 字符串,那么该字符串肯定被 interned。但是,如果你是在 PHP 处理一个请求时使用 interned 字符串,那么该字符串只会在当前请求被 interned,并在之后会清理掉。如果你不使用 OPCache 扩展,那这一切都是有效的,有时你不应该使用它。

当使用 OPCache 扩展,如果你使用请求处理中的 interned 字符串,那么该字符串肯定被 interned ,并且和并行产生的每个 PHP 的进程或线程共享。另外,如果当你处理一个请求时使用 interned 字符串,该字符串也将由 OPCache 本身进行 interned,并且共享给并行产生的每个 PHP 进程或线程。

Then, when the OPCache extension is triggered, the Interned string mechanism is changed. OPCache not only allows interned strings from requests, but also allows them to be shared to every PHP process in the same pool. This is done using shared memory. When saving an interned string, OPCache also adds the IS_STR_PERMANENT flag to its GC information. This flag indicates that the memory allocation for the structure (here zend_string) is permanent, which can be a shared read-only memory segment.

Interned strings save memory because the same string will not be saved again in memory. But when it frequently needs to look up interned string storage, it can waste some CPU time, even though the process is now optimized. As an extension designer, this is a global rule:

  • If you are using OPCache (which you should be), and you need to create read-only strings: use interned strings.
  • If you need a string that you know exactly PHP will have interned (well-known PHP strings, such as "php" or "str_replace"), use interned strings.
  • Do not use interned strings if the string is not read-only and can/should be modified after creation.
  • Do not use interned strings if the string is unlikely to be reused in the future.

Warning

Do not attempt to modify (write) an interned string, otherwise it is likely to crash.

Interned string details, see Zend/zend_string.c.

The above is the detailed content of PHP string management zend_string. For more information, please follow other related articles on the PHP Chinese website!

Statement
This article is reproduced at:learnku. If there is any infringement, please contact admin@php.cn delete
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace(" ","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么判断有没有小数点php怎么判断有没有小数点Apr 20, 2022 pm 08:12 PM

php判断有没有小数点的方法:1、使用“strpos(数字字符串,'.')”语法,如果返回小数点在字符串中第一次出现的位置,则有小数点;2、使用“strrpos(数字字符串,'.')”语句,如果返回小数点在字符串中最后一次出现的位置,则有。

php怎么读取字符串后几个字符php怎么读取字符串后几个字符Apr 22, 2022 pm 08:31 PM

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
Repo: How To Revive Teammates
1 months agoBy尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: How To Get Giant Seeds
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌

Hot Tools

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Integrate Eclipse with SAP NetWeaver application server.

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

VSCode Windows 64-bit Download

VSCode Windows 64-bit Download

A free and powerful IDE editor launched by Microsoft