aboutsummaryrefslogtreecommitdiffhomepage
path: root/note.md
diff options
context:
space:
mode:
Diffstat (limited to 'note.md')
-rw-r--r--note.md465
1 files changed, 465 insertions, 0 deletions
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
+<?php
+
+$a =& $b;
+```
+
+> $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
+<?php
+
+function foo(&$var) {}
+
+foo($a);
+$b = [];
+foo($b['b']);
+// array_key_exists('b', $b) // => true
+
+$c = new stdClass();
+foo($c->d);
+// property_exists($c, 'd') // => true
+```
+
+```php
+<?php
+
+function& foo() {}
+
+$a =& foo();
+
+$b =& new stdClass();
+// => 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
+<?php
+
+$var1 = "Example variable";
+$var2 = "";
+
+function global_references($use_globals) {
+ global $var1, $var2;
+ if ($use_globals) {
+ $GLOBALS["var2"] =& $var1; // visible also in global context
+ } else {
+ $var2 =& $var1; // visible only inside the function
+ }
+}
+
+global_references(false);
+echo "var2 is set to '$var2'\n"; // var2 is set to ''
+global_references(true);
+echo "var2 is set to '$var2'\n"; // var2 is set to 'Example variable'
+```
+
+> 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
+<?php
+
+$ref = 0;
+$row =& $ref;
+
+foreach ([1, 2, 3] as $row) {
+ // do something
+}
+echo $ref; // 3: last element of the iterated array
+```
+
+
+!!!
+
+> 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
+<?php
+
+$a = 1;
+$b = [2, 3];
+$arr = [&$a, &$b[0], &$b[1]];
+$arr[0]++; $arr[1]++; $arr[2]++;
+var_dump($a);
+// => 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
+<?php
+
+/* Assignment of scalar variables */
+$a = 1;
+$b =& $a;
+$c = $b;
+$c = 7; //$c is not a reference; no change to $a or $b
+
+/* Assignment of array variables */
+$arr = [1, 2];
+$a =& $arr[0]; //$a and $arr[0] are in the same reference set
+$arr2 = $arr; //not an assignment-by-reference!
+$arr2[0]++;
+$arr2[1]++;
+/* $a == 2, $arr == [2, 2] */
+/* The contents of $arr are changed even though it's not a reference! */
+```
+
+> 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
+<?php
+
+function foo(&$var) {
+ $var =& $GLOBALS["baz"];
+}
+foo($bar);
+```
+
+
+https://www.php.net/manual/en/language.references.pass.php
+
+
+リファレンス渡しできるもの
+
+* 変数
+* リファレンス返しする関数の返り値
+
+```php
+<?php
+
+$a = 1;
+$b =& $a;
+unset($a);
+```
+
+https://www.php.net/manual/en/language.references.spot.php
+
+Reference っぽくない reference
+
+```php
+<?php
+
+// global $var;
+$var =& $GLOBALS["var"];
+```
+
+
+
+
+# PHP Internals Book
+
+https://www.phpinternalsbook.com/
+
+## https://www.phpinternalsbook.com/php7/zvals/basic_structure.html
+
+> 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
+<?php
+
+class Test {
+ public int $prop = 42;
+}
+$test = new Test();
+$ref =& $test->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 を少しだけ探索できるようになること
+> 話さないこと: 参照のメリット・デメリットや使うべき場面