From b0ad59f3c3abf2fe25d7163c6d29711ab652deb0 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Tue, 27 Dec 2022 20:52:31 +0900 Subject: スライドを作成 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- note.md | 465 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 note.md (limited to 'note.md') diff --git a/note.md b/note.md new file mode 100644 index 0000000..00846c9 --- /dev/null +++ b/note.md @@ -0,0 +1,465 @@ +https://www.php.net/manual/ja/language.references.whatdo.php +https://www.phpinternalsbook.com/ + + + +```c +ZEND_VM_HANDLER(30, ZEND_ASSIGN_REF, VAR|CV, VAR|CV, SRC) +{ + USE_OPLINE + zval *variable_ptr; + zval *value_ptr; + + SAVE_OPLINE(); + value_ptr = GET_OP2_ZVAL_PTR_PTR(BP_VAR_W); + variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W); + + if (OP1_TYPE == IS_VAR && + UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT)) { + + zend_throw_error(NULL, "Cannot assign by reference to an array dimension of an object"); + variable_ptr = &EG(uninitialized_zval); + } else if (OP2_TYPE == IS_VAR && + opline->extended_value == ZEND_RETURNS_FUNCTION && + UNEXPECTED(!Z_ISREF_P(value_ptr))) { + + variable_ptr = zend_wrong_assign_to_variable_reference( + variable_ptr, value_ptr OPLINE_CC EXECUTE_DATA_CC); + } else { + // !!! + zend_assign_to_variable_reference(variable_ptr, value_ptr); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr); + } + + FREE_OP2(); + FREE_OP1(); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + +// https://github.com/php/php-src/blob/php-8.2.3/Zend/zend_execute.c#L533-L557 +static inline void zend_assign_to_variable_reference(zval *variable_ptr, zval *value_ptr) +{ + zend_reference *ref; + + if (EXPECTED(!Z_ISREF_P(value_ptr))) { + ZVAL_NEW_REF(value_ptr, value_ptr); + } else if (UNEXPECTED(variable_ptr == value_ptr)) { + return; + } + + ref = Z_REF_P(value_ptr); + GC_ADDREF(ref); + if (Z_REFCOUNTED_P(variable_ptr)) { + zend_refcounted *garbage = Z_COUNTED_P(variable_ptr); + + if (GC_DELREF(garbage) == 0) { + ZVAL_REF(variable_ptr, ref); + rc_dtor_func(garbage); + return; + } else { + gc_check_possible_root(garbage); + } + } + ZVAL_REF(variable_ptr, ref); +} + +// https://github.com/php/php-src/blob/php-8.2.3/Zend/zend_types.h#L1077-L1086 +#define ZVAL_NEW_REF(z, r) do { \ + zend_reference *_ref = \ + (zend_reference *) emalloc(sizeof(zend_reference)); \ + GC_SET_REFCOUNT(_ref, 1); \ + GC_TYPE_INFO(_ref) = GC_REFERENCE; \ + ZVAL_COPY_VALUE(&_ref->val, r); \ + _ref->sources.ptr = NULL; \ + Z_REF_P(z) = _ref; \ + Z_TYPE_INFO_P(z) = IS_REFERENCE_EX; \ + } while (0) + + zend_reference *_ref = (zend_reference *)malloc(/* 略 */); + _ref->refcount = 1; + ZVAL_COPY_VALUE(&_ref->val, value_ptr); + value_ptr->value.ref = _ref; + value_ptr->type_info = IS_REFERENCE; + +#define GC_SET_REFCOUNT(p, rc) zend_gc_set_refcount(&(p)->gc, rc) +static zend_always_inline uint32_t zend_gc_set_refcount(zend_refcounted_h *p, uint32_t rc) { + p->refcount = rc; + return p->refcount; +} +#define GC_TYPE_INFO(p) (p)->gc.u.type_info + + do { \ + zval *_z1 = (z); \ + const zval *_z2 = (v); \ + zend_refcounted *_gc = Z_COUNTED_P(_z2); \ + uint32_t _t = Z_TYPE_INFO_P(_z2); \ + ZVAL_COPY_VALUE_EX(_z1, _z2, _gc, _t); \ + } while (0) + +#define Z_TYPE_INFO(zval) (zval).u1.type_info +#define Z_TYPE_INFO_P(zval_p) Z_TYPE_INFO(*(zval_p)) + +#define Z_REF_P(zval_p) Z_REF(*(zval_p)) +#define Z_REF(zval) (zval).value.ref + +#define ZVAL_REF(z, r) do { \ + zval *__z = (z); \ + Z_REF_P(__z) = (r); \ + Z_TYPE_INFO_P(__z) = IS_REFERENCE_EX; \ + } while (0) + +#define GC_ADDREF(p) zend_gc_addref(&(p)->gc) + +static zend_always_inline uint32_t zend_gc_addref(zend_refcounted_h *p) { + ZEND_RC_MOD_CHECK(p); + return ++(p->refcount); +} +``` + + + + + + + +# php-src + +```c +typedef struct _zval_struct zval; + +// https://github.com/php/php-src/blob/php-8.2.3/Zend/zend_types.h#L315-L340 +struct _zval_struct { + zend_value value; /* value */ + union { + uint32_t type_info; + struct { + ZEND_ENDIAN_LOHI_3( + zend_uchar type, /* active type */ + zend_uchar type_flags, + union { + uint16_t extra; /* not further specified */ + } u) + } v; + } u1; + union { + uint32_t next; /* hash collision chain */ + uint32_t cache_slot; /* cache slot (for RECV_INIT) */ + uint32_t opline_num; /* opline number (for FAST_CALL) */ + uint32_t lineno; /* line number (for ast nodes) */ + uint32_t num_args; /* arguments number for EX(This) */ + uint32_t fe_pos; /* foreach position */ + uint32_t fe_iter_idx; /* foreach iterator index */ + uint32_t property_guard; /* single property guard */ + uint32_t constant_flags; /* constant flags */ + uint32_t extra; /* not further specified */ + } u2; +}; + +// https://github.com/php/php-src/blob/php-8.2.3/Zend/zend_types.h#L295-L313 +typedef union _zend_value { + zend_long lval; /* long value */ + double dval; /* double value */ + zend_refcounted *counted; + zend_string *str; + zend_array *arr; + zend_object *obj; + zend_resource *res; + zend_reference *ref; + zend_ast_ref *ast; + zval *zv; + void *ptr; + zend_class_entry *ce; + zend_function *func; + struct { + uint32_t w1; + uint32_t w2; + } ww; +} zend_value; + +// https://github.com/php/php-src/blob/php-8.2.3/Zend/zend_types.h#L548-L560 +/* Regular data types: Must be in sync with zend_variables.c. */ +#define IS_UNDEF 0 +#define IS_NULL 1 +#define IS_FALSE 2 +#define IS_TRUE 3 +#define IS_LONG 4 +#define IS_DOUBLE 5 +#define IS_STRING 6 +#define IS_ARRAY 7 +#define IS_OBJECT 8 +#define IS_RESOURCE 9 +#define IS_REFERENCE 10 +#define IS_CONSTANT_AST 11 /* Constant expressions */ + +// https://github.com/php/php-src/blob/php-8.2.3/Zend/zend_types.h#L537-L541 +struct _zend_reference { + zend_refcounted_h gc; + zval val; + zend_property_info_source_list sources; +}; + +typedef struct _zend_refcounted_h { + uint32_t refcount; /* reference counter 32-bit */ + union { + uint32_t type_info; + } u; +} zend_refcounted_h; +``` + + + + +# PHP Manual + + +https://www.php.net/manual/en/language.references.whatdo.php + + +```php + $a and $b are completely equal here. $a is not pointing to $b or vice versa. $a and $b are pointing to the same place. + +> If you assign, pass, or return an undefined variable by reference, it will get created. + +```php + true + +$c = new stdClass(); +foo($c->d); +// property_exists($c, 'd') // => true +``` + +```php + Error! +``` + +> If you assign a reference to a variable declared global inside a function, +> the reference will be visible only inside the function. You can avoid this by +> using the `$GLOBALS` array. + + + +!!! + +```php + Think about `global $var;` as a shortcut to `$var =& $GLOBALS['var'];`. Thus +> assigning another reference to `$var` only changes the local variable's +> reference. + + + + +> If you assign a value to a variable with references in a foreach statement, +> the references are modified too. + +```php + While not being strictly an assignment by reference, expressions created with +> the language construct `array()` can also behave as such by prefixing `&` to the +> array element to add. Example: + +```php + 2 +var_dump($b); +// => [3, 4] +``` + + +!!! + +> Note, however, that references inside arrays are potentially dangerous. Doing +> a normal (not by reference) assignment with a reference on the right side +> does not turn the left side into a reference, but references inside arrays +> are preserved in these normal assignments. This also applies to function +> calls where the array is passed by value. Example: + +```php + In other words, the reference behavior of arrays is defined in an +> element-by-element basis; the reference behavior of individual elements is +> dissociated from the reference status of the array container. + + + +!!! + +https://www.php.net/manual/en/language.references.arent.php + +```php + The IS_REFERENCE type in conjunction with the zend_reference *ref member is +> used to represent a PHP reference. While from a userland perspective +> references are not a separate type, internally references are represented as +> a wrapper around another zval, that can be shared by multiple places. + + +## https://www.phpinternalsbook.com/php7/zvals/references.html + +> People will commonly say that “$b is a reference to $a”. However, this is not +> quite correct, in that references in PHP have no concept of directionality. +> After $b =& $a, both $a and $b reference a common value, and neither of the +> variables is privileged in any way. + +----- + +> Normally, PHP does not track who or what makes use of a given reference. The +> only knowledge that is stored is how many users there are (through the +> refcount), so that the reference may be destroyed in time. + +> However, due to the introduction of typed properties in PHP 7.4, we do need +> to track of which typed properties make use of a certain reference, in order +> to enforce property types for indirect modifications through references: + +```php +prop; +$ref = "string"; // TypeError +``` + +> The sources member of zend_reference stores a list of zend_property_info +> pointers to track typed properties that use the reference. Macros like +> ZEND_REF_HAS_TYPE_SOURCES(), ZEND_REF_ADD_TYPE_SOURCE(), and +> ZEND_REF_DEL_TYPE_SOURCE() + + + + +# プロポーザル + +https://fortee.jp/phperkaigi-2023/proposal/95e4dd94-5fc7-40fe-9e1a-230e36404cbe + +## 詳説「参照」:PHP 処理系の実装から参照を理解する + +> PHP における参照に似た機能は、他の言語にも存在しています。C のポインタ、C++ の参照、Java の参照型、C# の参照渡し……。しかしこれらは、それぞれ細かな点で PHP のそれとは異なっています。 +> PHP における参照を完全に理解すべく、1) PHP レベルでの挙動を観察し、2) PHP 処理系 (https://github.com/php/php-src) のソースコードを追いかけます。 +> +> 対象: 重箱の隅をつつきたい PHPer、または PHP の language lawyer になりたい人。PHP 処理系は C で書かれていますが、C の知識は (あまり) 要求しないようにするつもりです +> 目標: PHP の参照を、実装レベルで完全に理解すること、また、php-src を少しだけ探索できるようになること +> 話さないこと: 参照のメリット・デメリットや使うべき場面 -- cgit v1.2.3-70-g09d2