Подтвердить что ты не робот

Ли (строка) "печатает" строку?

PHP использует систему копирования на модификацию.

Есть ли $a = (string) $a; ($ a - уже строка), чтобы изменить и скопировать что-нибудь?


В частности, это моя проблема:

Параметр 1 - mixed/Я хочу разрешить передавать не строки и преобразовывать их в строки.
Но иногда эти строки очень большие. Поэтому я хочу опустить копирование параметра, который уже является строкой.

Можно ли использовать версию Foo или мне нужно использовать версию Bar?

class Foo {
    private $_foo;
    public function __construct($foo) {
        $this->_foo = (string) $foo;
    }
}

class Bar {
    private $_bar;
    public function __construct($bar) {
        if (is_string($bar)) {
            $this->_bar = $bar;
        } else {
            $this->_bar = (string) $bar;
        }
    }
}
4b9b3361

Ответ 1

Ответ: да, он копирует строку. Сортировка... Не совсем. Ну, это зависит от вашего определения "copy"...

>= 5.4

Чтобы узнать, что происходит, взгляните на источник. Исполнитель обрабатывает переменную cast в 5.5 здесь.

    zend_make_printable_zval(expr, &var_copy, &use_copy);
    if (use_copy) {
        ZVAL_COPY_VALUE(result, &var_copy);
        // if optimized out
    } else {
        ZVAL_COPY_VALUE(result, expr);
        // if optimized out
        zendi_zval_copy_ctor(*result);
    }

Как вы можете видеть, вызов использует zend_make_printable_zval(), который просто замыкается на замыкание, если zval уже является строкой.

Итак, код, который выполняется для копирования, - это (ветка else):

ZVAL_COPY_VALUE(result, expr);

Теперь рассмотрим определение ZVAL_COPY_VALUE:

#define ZVAL_COPY_VALUE(z, v)                   \
    do {                                        \
        (z)->value = (v)->value;                \
        Z_TYPE_P(z) = Z_TYPE_P(v);              \
    } while (0)

Заметьте, что это делает. Сама строка НЕ ​​скопирована (которая хранится в блоке ->value zval). Он просто ссылается (указатель остается тем же, поэтому строковое значение одно и то же, без копирования). Но он создает новую переменную (часть zval, которая обертывает значение).

Теперь мы переходим к вызову zendi_zval_copy_ctor. Что внутренне делает некоторые интересные вещи самостоятельно. Примечание:

case IS_STRING:
    CHECK_ZVAL_STRING_REL(zvalue);
    if (!IS_INTERNED(zvalue->value.str.val)) {
        zvalue->value.str.val = (char *) estrndup_rel(zvalue->value.str.val, zvalue->value.str.len);
    }
    break;

В основном это означает, что если это интернированная строка, она не будет скопирована. но если это не так, то будет скопировано... Итак, что такое интернированная строка и что это значит?

<= 5.3

В 5.3 встроенные строки не существовали. Поэтому строка всегда копируется. Это действительно единственная разница...

Время тестирования:

Ну, в таком случае:

$a = "foo";
$b = (string) $a;

Никакая копия строки не будет выполняться в 5.4, но в 5.3 произойдет копия.

Но в таком случае:

$a = str_repeat("a", 10);
$b = (string) $a;

Копия будет для всех версий. Это потому, что в PHP не все строки интернированы...

Попробуй попробовать в тесте: http://3v4l.org/HEelW

$a = "foobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisout";
$b = str_repeat("a", 300);

echo "Static Var\n";
testCopy($a);
echo "Dynamic Var\n";
testCopy($b);

function testCopy($var) {
    echo memory_get_usage() . "\n";
    $var = (string) $var;
    echo memory_get_usage() . "\n";
}

Результаты:

  • 5.4 - 5.5 alpha 1 (не включая другие альфы, поскольку различия незначительны, чтобы не иметь принципиального значения)

    Static Var
    220152
    220200
    Dynamic Var
    220152
    220520
    

    Таким образом, статический var увеличился на 48 байт, а динамический var увеличился на 368 байт.

  • 5.3.11 - 5.3.22:

    Static Var
    624472
    625408
    Dynamic Var
    624472
    624840
    

    Статический var увеличился на 936 байт, а динамический var увеличился на 368 байт.

Поэтому обратите внимание, что в 5.3 скопированы как статические, так и динамические переменные. Таким образом, строка всегда дублируется.

Но в 5.4 со статическими строками была скопирована только структура zval. Это означает, что сама строка, которая была интернирована, остается неизменной и не копируется...

Одна другая вещь

Еще одно замечание: все вышеперечисленное является спорным. Вы передаете переменную в качестве параметра функции. Затем вы выполняете функцию. Таким образом, копирование на запись будет вызвано вашей линией. Так что запуск, который всегда (ну, в 99,9% случаев) вызывает переменную копию. Так что в лучшем случае (интернированные строки) вы говорите о дублировании zval и связанных с этим накладных расходах. В худшем случае вы говорите о дублировании строк...

Ответ 2

Ваш код на самом деле не работает:

$a = (string)$a;

Это более похоже на это, поскольку семантика copy-on-write применяется, когда строка передается как аргумент функции:

$b = (string)$a;

Там есть довольно большая разница между этими двумя утверждениями. Первый не будет иметь никакого влияния на память, тогда как второй делает... обычно.

Следующий код делает примерно то, что будет делать ваш код; какая-то строка передается и вы передаете ее другой переменной. Он отслеживает увеличение в памяти.

<?php

$x = 0;
$y = 0;

$x = memory_get_usage();

$s = str_repeat('c', 1200);

$y = memory_get_usage();

echo $y - $x, PHP_EOL;

$s1 = (string)$s;

$x = memory_get_usage();

echo $x - $y, PHP_EOL;

Результаты (5.4.9):

1360
1360

Результаты (5.3.19):

1368
1368

Назначение в основном копирует все строковое значение.

Использование строковых литералов

При использовании строкового литерала поведение зависит от версии:

<?php

$x = 0;
$y = 0;

$x = memory_get_usage();

$s = 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc';

$y = memory_get_usage();

echo $y - $x, PHP_EOL;

$s1 = (string)$s;

$x = memory_get_usage();

echo $x - $y, PHP_EOL;

Результаты (5.4.9):

152
136

Результаты (5.3.19):

1328
1328

Причина в том, что строковые литералы обрабатываются по-разному движком, как вы можете прочитать из ircmaxell answer.

Ответ 3

Удивительно, но создает копию:

$string = "TestMe";
debug_zval_dump($string);

$string2 = $string;
debug_zval_dump($string);

$string3 = $string;
debug_zval_dump($string);

$string4 = (string) $string;
debug_zval_dump($string);

$string5 = (string) $string;
debug_zval_dump($string);

Вывод:

string(6) "TestMe" refcount(2)
string(6) "TestMe" refcount(3)
string(6) "TestMe" refcount(4)
string(6) "TestMe" refcount(4)
string(6) "TestMe" refcount(4)

Еще одно доказательство:

echo memory_get_usage(), PHP_EOL;

$s = str_repeat('c', 100000);
echo memory_get_usage(), PHP_EOL;

$s1 = $s;
echo memory_get_usage(), PHP_EOL;

$s2 = (string) $s;
echo memory_get_usage(), PHP_EOL;

Вывод:

627496
727664
727760  # small increase, new allocated object, but no string copy
827928  # oops, we copied the string...