From ebb71fa3c0210738eb970833cc1a315ea9c03dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Tue, 24 Dec 2024 17:43:38 +0900 Subject: [PATCH 1/6] =?UTF-8?q?`EN-Revision`=E6=9B=B4=E6=96=B0=E3=81=A8?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=82=B9=E3=82=BF=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E5=8E=9F=E6=96=87=E3=81=B8=E3=81=AE=E6=BA=96=E6=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- language/constants.xml | 4 +- language/oop5.xml | 2 +- language/oop5/abstract.xml | 39 +- language/oop5/interfaces.xml | 2 +- language/oop5/property-hooks.xml | 603 +++++++++++++++++++++++++++++++ language/oop5/variance.xml | 2 +- 6 files changed, 631 insertions(+), 21 deletions(-) create mode 100644 language/oop5/property-hooks.xml diff --git a/language/constants.xml b/language/constants.xml index f1c72d632b9..01b4693daf0 100644 --- a/language/constants.xml +++ b/language/constants.xml @@ -1,6 +1,6 @@ - + @@ -35,7 +35,6 @@ この場合の定数の値は constant 関数によってのみ取得できますが、 このような使い方は推奨できません。 - &tip.userlandnaming; @@ -69,6 +68,7 @@ define("__FOO__", "something"); (0x80-0xff)のASCII文字を指します。 + &link.superglobals;と同様に定数のスコープはグローバルです。 つまり、スコープによらずスクリプトの中ではどこでも定数に diff --git a/language/oop5.xml b/language/oop5.xml index a135ea2b3ed..990e6c62b6e 100644 --- a/language/oop5.xml +++ b/language/oop5.xml @@ -1,5 +1,5 @@ - + diff --git a/language/oop5/abstract.xml b/language/oop5/abstract.xml index 85a031eabaf..9dce95acb65 100644 --- a/language/oop5/abstract.xml +++ b/language/oop5/abstract.xml @@ -1,6 +1,6 @@ - + @@ -26,6 +26,7 @@ getValue() . "\n"; } } class ConcreteClass1 extends AbstractClass { - protected function getValue() { + protected function getValue() + { return "ConcreteClass1"; } - public function prefixValue($prefix) { + public function prefixValue($prefix) + { return "{$prefix}ConcreteClass1"; } } class ConcreteClass2 extends AbstractClass { - public function getValue() { + public function getValue() + { return "ConcreteClass2"; } - public function prefixValue($prefix) { + public function prefixValue($prefix) + { return "{$prefix}ConcreteClass2"; } } -$class1 = new ConcreteClass1; +$class1 = new ConcreteClass1(); $class1->printOut(); -echo $class1->prefixValue('FOO_') ."\n"; +echo $class1->prefixValue('FOO_'), "\n"; -$class2 = new ConcreteClass2; +$class2 = new ConcreteClass2(); $class2->printOut(); -echo $class2->prefixValue('FOO_') ."\n"; +echo $class2->prefixValue('FOO_'), "\n"; + ?> ]]> @@ -86,18 +93,18 @@ FOO_ConcreteClass2 prefixName("Pacman"), "\n"; echo $class->prefixName("Pacwoman"), "\n"; + ?> ]]> @@ -123,9 +132,7 @@ Mrs. Pacwoman ]]> - - - + diff --git a/language/oop5/property-hooks.xml b/language/oop5/property-hooks.xml new file mode 100644 index 00000000000..2a90b18000f --- /dev/null +++ b/language/oop5/property-hooks.xml @@ -0,0 +1,603 @@ + + + + + Property Hooks + + + Property hooks, also known as "property accessors" in some other languages, + are a way to intercept and override the read and write behavior of a property. + This functionality serves two purposes: + + + + + It allows for properties to be used directly, without get- and set- methods, + while leaving the option open to add additional behavior in the future. + That renders most boilerplate get/set methods unnecessary, + even without using hooks. + + + + + It allows for properties that describe an object without needing to store + a value directly. + + + + + There are two hooks available on non-static properties: get and set. + They allow overriding the read and write behavior of a property, respectively. + Hooks are available for both typed and untyped properties. + + + A property may be "backed" or "virtual". + A backed property is one that actually stores a value. + Any property that has no hooks is backed. + A virtual property is one that has hooks and those hooks do not interact with the property itself. + In this case, the hooks are effectively the same as methods, + and the object does not use any space to store a value for that property. + + + Property hooks are incompatible with readonly properties. + If there is a need to restrict access to a get or set + operation in addition to altering its behavior, use + asymmetric property visibility. + + + Basic Hook Syntax + + The general syntax for declaring a hook is as follows. + + + Property hooks (full version) + +modified) { + return $this->foo . ' (modified)'; + } + return $this->foo; + } + set(string $value) { + $this->foo = strtolower($value); + $this->modified = true; + } + } +} + +$example = new Example(); +$example->foo = 'changed'; +print $example->foo; +?> +]]> + + + + The $foo property ends in {}, rather than a semicolon. + That indicates the presence of hooks. + Both a get and set hook are defined, + although it is allowed to define only one or the other. + Both hooks have a body, denoted by {}, that may contain arbitrary code. + + + The set hook additionally allows specifying the type and name of an incoming value, + using the same syntax as a method. + The type must be either the same as the type of the property, + or contravariant (wider) to it. + For instance, a property of type string could have a + set hook that accepts stringStringable, + but not one that only accepts array. + + + At least one of the hooks references $this->foo, the property itself. + That means the property wll be "backed." + When calling $example->foo = 'changed', + the provided string will be first cast to lowercase, then saved to the backing value. + When reading from the property, the previously saved value may conditionally be appended + with additional text. + + + There are a number of shorthand syntax variants as well to handle common cases. + + + If the get hook is a single expression, + then the {} may be omitted and replaced with an arrow expression. + + + Property get expression + + This example is equivalent to the previous. + + + $this->foo . ($this->modified ? ' (modified)' : ''); + + set(string $value) { + $this->foo = strtolower($value); + $this->modified = true; + } + } +} +?> +]]> + + + + If the set hook's parameter type is the same as the property type (which is typical), + it may be omitted. In that case, the value to set is automatically given the name $value. + + + Property set defaults + + This example is equivalent to the previous. + + + $this->foo . ($this->modified ? ' (modified)' : ''); + + set { + $this->foo = strtolower($value); + $this->modified = true; + } + } +} +?> +]]> + + + + If the set hook is only setting a modified version of the passed in value, + then it may also be simplified to an arrow expression. + The value the expression evaluates to will be set on the backing value. + + + Property set expression + + $this->foo . ($this->modified ? ' (modified)' : ''); + set => strtolower($value); + } +} +?> +]]> + + + + This example is not quite equivalent to the previous, + as it does not also modify $this->modified. + If multiple statements are needed in the set hook body, use the braces version. + + + A property may implement zero, one, or both hooks as the situation requires. + All shorthand versions are mutually-independent. + That is, using a short-get with a long-set, + or a short-set with an explicit type, or so on is all valid. + + + On a backed property, omitting a get orset + hook means the default read or write behavior will be used. + + + + Hooks can be defined when using + constructor property promotion. + However, when doing so, values provided + to the constructor must match the type associated with the property, + regardless of what the set hook might allow. + + + Consider the following: + + +created = $value; + } + }, + ) { + } +} +]]> + + + Internally, the engine decomposes this to the following: + + +created = $value; + } + } + + public function __construct( + DateTimeInterface $created, + ) { + $this->created = $created; + } +} +]]> + + + Any attempts to set the property outside the constructor will + allow either string or DateTimeInterface + values, but the constructor will only allow DateTimeInterface. + This is because the defined type for the property (DateTimeInterface) + is used as the parameter type within the constructor signature, regardless of what + the set hook allows. + + + If this kind of behavior is needed from the constructor, constructor + property promotion cannot be used. + + + + + Virtual properties + + Virtual properties are properties that have no backing value. + A property is virtual if neither its get + nor set hook references the property itself using exact syntax. + That is, a property named $foo whose hook contains $this->foo will be backed. + But the following is not a backed property, and will error: + + + Invalid virtual property + +$temp; // Doesn't refer to $this->foo, so it doesn't count. + } + } +} +?> +]]> + + + + For virtual properties, if a hook is omitted then that operation does + not exist and trying to use it will produce an error. + Virtual properties take up no memory space in an object. + Virtual properties are suited for "derived" properties, + such as those that are the combination of two other properties. + + + Virtual property + + $this->h * $this->w; + } + + public function __construct(public int $h, public int $w) {} +} + +$s = new Rectangle(4, 5); +print $s->area; // prints 20 +$s->area = 30; // Error, as there is no set operation defined. +?> +]]> + + + + Defining both a get and set hook on a virtual property is also allowed. + + + + Scoping + + All hooks operate in the scope of the object being modified. + That means they have access to all public, private, or protected methods of the object, + as well as any public, private, or protected properties, + including properties that may have their own property hooks. + Accessing another property from within a hook does not bypass the hooks defined on that property. + + + The most notable implication of this is that non-trivial hooks may sub-call + to an arbitrarily complex method if they wish. + + + Calling a method from a hook + + $this->sanitizePhone($value); + } + + private function sanitizePhone(string $value): string { + $value = ltrim($value, '+'); + $value = ltrim($value, '1'); + + if (!preg_match('/\d\d\d\-\d\d\d\-\d\d\d\d/', $value)) { + throw new \InvalidArgumentException(); + } + return $value; + } +} +?> +]]> + + + + + References + + Because the presence of hooks intercept the read and write process for properties, + they cause issues when acquiring a reference to a property or with indirect + modification, such as $this->arrayProp['key'] = 'value';. + That is because any attempted modification of the value by reference would bypass a set hook, + if one is defined. + + + In the rare case that getting a reference to a property that has hooks defined is necessary, + the get hook may be prefixed with & + to cause it to return by reference. + Defining both get and &get on the + same property is a syntax error. + + + Defining both &get and set hooks on a backed property is not allowed. + As noted above, writing to the value returned by reference would bypass the set hook. + On virtual properties, there is no necessary common value shared between the two hooks, so defining both is allowed. + + + Writing to an index of an array property also involves an implicit reference. + For that reason, writing to a backed array property with hooks defined is + allowed if and only if it defines only a &get hook. + On a virtual property, writing to the array returned from either + get or &get is legal, + but whether that has any impact on the object depends on the hook implementation. + + + Overwriting the entire array property is fine, and behaves the same as any other property. + Only working with elements of the array require special care. + + + + Inheritance + + Final hooks + + Hooks may also be declared final, + in which case they may not be overridden. + + + Final hooks + + strtolower($value); + } +} + +class Manager extends User +{ + public string $username { + // This is allowed + get => strtoupper($this->username); + + // But this is NOT allowed, because set is final in the parent. + set => strtoupper($value); + } +} +?> +]]> + + + + A property may also be declared final. + A final property may not be redeclared by a child class in any way, + which precludes altering hooks or widening its access. + + + Declaring hooks final on a property that is declared final is redundant, + and will be silently ignored. + This is the same behavior as final methods. + + + A child class may define or redefine individual hooks on a property + by redefining the property and just the hooks it wishes to override. + A child class may also add hooks to a property that had none. + This is essentially the same as if the hooks were methods. + + + Hook inheritance + +x = $value; + } + } +} +?> +]]> + + + + Each hook overrides parent implementations independently of each other. + If a child class adds hooks, any default value set on the property is removed, and must be redeclared. + That is the same consistent with how inheritance works on hook-less properties. + + + + Accessing parent hooks + + A hook in a child class may access the parent class's property using the + parent::$prop keyword, followed by the desired hook. + For example, parent::$propName::get(). + It may be read as "access the prop defined on the parent class, + and then run its get operation" (or set operation, as appropriate). + + + If not accessed this way, the parent class's hook is ignored. + This behavior is consistent with how all methods work. + This also offers a way to access the parent class's storage, if any. + If there is no hook on the parent property, + its default get/set behavior will be used. + Hooks may not access any other hook except their own parent on their own property. + + + The example above could be rewritten more efficiently as follows. + + + Parent hook access (set) + +x = $value; + } + } +} +?> +]]> + + + + An example of overriding only a get hook could be: + + + Parent hook access (get) + + $this->uppercase + ? strtoupper(parent::$val::get()) + : strtolower(parent::$val::get()); + } +} +?> +]]> + + + + + + Serialization + + PHP has a number of different ways in which an object may be serialized, + either for public consumption or for debugging purposes. + The behavior of hooks varies depending on the use case. + In some cases, the raw backing value of a property will be used, + bypassing any hooks. + In others, the property will be read or written "through" the hook, + just like any other normal read/write action. + + + var_dump: Use raw value + serialize: Use raw value + unserialize: Use raw value + __serialize()/__unserialize(): Custom logic, uses get/set hook + Array casting: Use raw value + var_export: Use get hook + json_encode: Use get hook + JsonSerializable: Custom logic, uses get hook + get_object_vars: Use get hook + get_mangled_object_vars: Use raw value + + + + diff --git a/language/oop5/variance.xml b/language/oop5/variance.xml index 56660deeb85..86f9712e1d9 100644 --- a/language/oop5/variance.xml +++ b/language/oop5/variance.xml @@ -1,6 +1,6 @@ - + 共変性と反変性 From b0d3c4e20e894e751eb543571cd5fab226de5ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Wed, 25 Dec 2024 14:05:43 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=E3=80=8C=E3=83=97=E3=83=AD=E3=83=91?= =?UTF-8?q?=E3=83=86=E3=82=A3=E3=83=95=E3=83=83=E3=82=AF=E3=80=8D=E3=81=AE?= =?UTF-8?q?=E7=BF=BB=E8=A8=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- language/constants.xml | 12 +- language/oop5.xml | 1 + language/oop5/abstract.xml | 100 ++++++++- language/oop5/interfaces.xml | 93 +++++++- language/oop5/property-hooks.xml | 372 +++++++++++++++---------------- language/oop5/variance.xml | 57 ++++- 6 files changed, 434 insertions(+), 201 deletions(-) diff --git a/language/constants.xml b/language/constants.xml index 01b4693daf0..74d03030456 100644 --- a/language/constants.xml +++ b/language/constants.xml @@ -262,7 +262,7 @@ echo ANIMALS[1]; // 出力は "cat" マジック定数 - 使われる場所によって値が変化する定数(マジック定数)が 9 つあります。 + 使われる場所によって値が変化する定数(マジック定数)がいくつかあります。 例えば、__LINE__はスクリプト上において 使われる行によって値が変化します。 これらの @@ -315,7 +315,7 @@ echo ANIMALS[1]; // 出力は "cat" クラス名。 クラス名には、そのクラスが宣言されている名前空間も含みます (例 Foo\Bar)。 - トレイトのメソッド内で __CLASS__ を使うと、 + トレイトのメソッド内で __CLASS__ を使うと、 そのトレイトを use しているクラスの名前を返します。 @@ -333,6 +333,14 @@ echo ANIMALS[1]; // 出力は "cat" クラスのメソッド名。 + + __PROPERTY__ + + プロパティ名。 + プロパティフック + の内部でのみ使用可能です。 + + __NAMESPACE__ diff --git a/language/oop5.xml b/language/oop5.xml index 990e6c62b6e..e7c8987919d 100644 --- a/language/oop5.xml +++ b/language/oop5.xml @@ -29,6 +29,7 @@ &language.oop5.basic; &language.oop5.properties; + &language.oop5.property-hooks; &language.oop5.constants; &language.oop5.autoload; &language.oop5.decon; diff --git a/language/oop5/abstract.xml b/language/oop5/abstract.xml index 9dce95acb65..844693c2a2c 100644 --- a/language/oop5/abstract.xml +++ b/language/oop5/abstract.xml @@ -7,11 +7,13 @@ クラスの抽象化 - PHP には、抽象クラスと抽象メソッドの機能があります。 - abstract として定義されたクラスのインスタンスを生成することはできません。 - 1つ以上の抽象メソッドを含む全てのクラスもまた抽象クラスとなります。 - abstract として定義されたメソッドは、そのメソッドのシグネチャを宣言するのみで、 - 実装を定義することはできません。 + PHP には、抽象クラス、抽象メソッド、抽象プロパティがあります。 + abstract として定義された抽象クラスのインスタンスを生成することはできず、 + 1つ以上の抽象メソッドや抽象プロパティを含むクラスは抽象クラスでなければいけません。 + abstract として定義されたメソッドは、そのメソッドのシグネチャと public または protected のアクセス権を宣言するのみで、 + 実装を定義することはできません。抽象プロパティは、 + getset の要件を宣言することができ、 + 実装はどちらか一方に対してのみ行えます。両方同時に実装することはできません。 @@ -21,8 +23,18 @@ シグネチャの互換性に関するルール に従わなければいけません。 + + PHP 8.4 から、抽象クラスは public または protected の抽象プロパティを宣言できるようになりました。 + protected な抽象プロパティは、protected または public のスコープから読み書き可能なプロパティにより + 要件が満たされます。 + + + 抽象プロパティは、通常のプロパティによって、または + 必要な操作に対応した フック を定義したプロパティにより要件が満たされます。 + + - 抽象クラスの例 + 抽象メソッドの例 - 抽象クラスの例 + 抽象メソッドの例 + + 抽象プロパティの例 + + $value; + } + + // protected から public にアクセス権を拡張しており、問題ありません + public string $both; +} + +?> +]]> + + + + 抽象プロパティにはフックを実装できます。 + 前の例のように、get または set のどちらかを、定義せず宣言のみ行います。 + + + 抽象プロパティの例 + +foo = $value; + } + } +} + +?> +]]> + + - Property Hooks + プロパティフック - Property hooks, also known as "property accessors" in some other languages, - are a way to intercept and override the read and write behavior of a property. - This functionality serves two purposes: + プロパティフックは、他の言語では「プロパティアクセサー」とも呼ばれる仕組みで、 + プロパティの読み書き動作へ干渉し、それを上書きする機能を提供します。 + この機能には、次の2つの目的があります: - It allows for properties to be used directly, without get- and set- methods, - while leaving the option open to add additional behavior in the future. - That renders most boilerplate get/set methods unnecessary, - even without using hooks. + get- メソッドや set- メソッドを使わずプロパティを参照しつつ、 + 将来的に追加の機能を追加する余地を残す。 + フックを使わない場合の定型の get/set メソッドのほとんどは + 不要になる。 - It allows for properties that describe an object without needing to store - a value directly. + 実際に値を保持することなく、 + オブジェクトを説明するプロパティを実装する。 - There are two hooks available on non-static properties: get and set. - They allow overriding the read and write behavior of a property, respectively. - Hooks are available for both typed and untyped properties. + 非静的なプロパティには getset の2種類のフックがあります。 + それぞれ、プロパティの読み取りと書き込みの動作を上書きします。 + フックは、型付きプロパティ、型のないプロパティ、何れにも利用可能です。 - A property may be "backed" or "virtual". - A backed property is one that actually stores a value. - Any property that has no hooks is backed. - A virtual property is one that has hooks and those hooks do not interact with the property itself. - In this case, the hooks are effectively the same as methods, - and the object does not use any space to store a value for that property. + プロパティはバックドプロパティか仮想プロパティのどちらかになります。 + バックドプロパティは実体の値を持つプロパティです。 + フックを持たないプロパティは、全てバックドプロパティになります。 + 仮想プロパティは、実体の値を必要としないフックだけを持つプロパティです。 + この場合、フックは実質的にメソッドと等価であり、 + オブジェクトはこのプロパティ用のメモリを必要としません。 - Property hooks are incompatible with readonly properties. - If there is a need to restrict access to a get or set - operation in addition to altering its behavior, use - asymmetric property visibility. + プロパティフックは readonly プロパティと同時に使えません。 + getset の動作の変更だけでなく + アクセス自体の制限も行いたい場合、 + 非対称可視性プロパティ を使ってください。 - - Basic Hook Syntax + + 基本的なフック構文 - The general syntax for declaring a hook is as follows. + フックを宣言する一般的な構文は次のとおりです。 - Property hooks (full version) + プロパティフック (フルバージョン) foo; - The $foo property ends in {}, rather than a semicolon. - That indicates the presence of hooks. - Both a get and set hook are defined, - although it is allowed to define only one or the other. - Both hooks have a body, denoted by {}, that may contain arbitrary code. + $foo プロパティの末尾はセミコロンではなく {} です。 + これがフックの存在を表します。 + ここでは getset 両方のフックを定義していますが、 + 一方だけ定義することも可能です。 + どちらのフックも {} の中に任意のコードを書くことができます。 - The set hook additionally allows specifying the type and name of an incoming value, - using the same syntax as a method. - The type must be either the same as the type of the property, - or contravariant (wider) to it. - For instance, a property of type string could have a - set hook that accepts stringStringable, - but not one that only accepts array. + set フックは、渡される値の型と名前を + メソッドと同じ書式で指定できます。 + この型はプロパティの型と同じか、 + 反変 (より広い型) でなければなりません。 + たとえば string 型のプロパティに対しては、 + stringStringable を受け取る set フックを定義できますが、 + array のみを受け取るものは定義できません。 - At least one of the hooks references $this->foo, the property itself. - That means the property wll be "backed." - When calling $example->foo = 'changed', - the provided string will be first cast to lowercase, then saved to the backing value. - When reading from the property, the previously saved value may conditionally be appended - with additional text. + 少なくとも一方のフックが $this->foo(プロパティ自体)を参照しているため、 + このプロパティはバックドプロパティになります。 + $example->foo = 'changed' が呼び出されると、 + 渡された文字列は小文字に変換され、それが値として保存されます。 + プロパティを読み取る際には、保存された値に対し、条件に応じて追加の文字列が + 付与されます。 - There are a number of shorthand syntax variants as well to handle common cases. + 一般的なケースを簡単に書くための、いくつかの短縮構文があります。 - If the get hook is a single expression, - then the {} may be omitted and replaced with an arrow expression. + get フックが単独の式である場合、 + {} を省略してアロー式で置き換えることができます。 - Property get expression + プロパティの get 式 - This example is equivalent to the previous. + この例は前の例と等価です。 - If the set hook's parameter type is the same as the property type (which is typical), - it may be omitted. In that case, the value to set is automatically given the name $value. + set フックのパラメータの型がプロパティの型と同じ場合 (典型的にはそうなります)、 + その型は省略可能です。この場合、渡される値には自動的に $value という名前が付きます。 - Property set defaults + プロパティ set のデフォルト - This example is equivalent to the previous. + この例は前の例と等価です。 - If the set hook is only setting a modified version of the passed in value, - then it may also be simplified to an arrow expression. - The value the expression evaluates to will be set on the backing value. + set フックが、渡された値を変形して保存するだけの場合には、 + アロー式でより簡略にできます。 + 式の評価結果が値として保存されます。 - Property set expression + プロパティの set 式 - This example is not quite equivalent to the previous, - as it does not also modify $this->modified. - If multiple statements are needed in the set hook body, use the braces version. + この例は $this->modified の値を更新していないため + 前の例と等価ではありません。 + set フック内で複数の文が必要な場合、中括弧付きの構文を使用してください。 - A property may implement zero, one, or both hooks as the situation requires. - All shorthand versions are mutually-independent. - That is, using a short-get with a long-set, - or a short-set with an explicit type, or so on is all valid. + プロパティは状況に応じて、一方または双方のフックを実装するか、どちらも実装しないことができます。 + それぞれの短縮構文は独立しており、 + たとえば「短縮構文の get と 通常の set」 + 「型を明示した短縮構文の set」など、いずれも有効です。 - On a backed property, omitting a get orset - hook means the default read or write behavior will be used. + バックドプロパティで getset のフックを省略した場合、 + それはデフォルトの読み書き動作になります。 - Hooks can be defined when using - constructor property promotion. - However, when doing so, values provided - to the constructor must match the type associated with the property, - regardless of what the set hook might allow. + フックは + コンストラクタのプロモーション + にも定義できます。ただし、コンストラクタで受け取る値は + set フックが受け入れる型ではなく、 + プロパティの本来の型に一致しなければならない点に注意してください。 - Consider the following: + 次のような例を考えます: - Internally, the engine decomposes this to the following: + 内部では次のような形で処理されます: - Any attempts to set the property outside the constructor will - allow either string or DateTimeInterface - values, but the constructor will only allow DateTimeInterface. - This is because the defined type for the property (DateTimeInterface) - is used as the parameter type within the constructor signature, regardless of what - the set hook allows. + コンストラクタ以外からプロパティに値を書き込む時は string か + DateTimeInterface を受け入れますが、 + コンストラクタからは DateTimeInterface のみ受け入れます。 + プロパティの型 (DateTimeInterface) が + コンストラクタのシグネチャのパラメータ型として使われ、 + set フックが受け入れる型は参照されないためです。 - If this kind of behavior is needed from the constructor, constructor - property promotion cannot be used. + このような振る舞いがコンストラクタでも必要な場合、 + コンストラクタのプロモーションは使用できません。 - Virtual properties + 仮想プロパティ - Virtual properties are properties that have no backing value. - A property is virtual if neither its get - nor set hook references the property itself using exact syntax. - That is, a property named $foo whose hook contains $this->foo will be backed. - But the following is not a backed property, and will error: + 仮想プロパティは、値を保持しないプロパティです。 + getset 何れのフックも + プロパティ自体を正確に参照していない場合、それは仮想プロパティになります。 + 例えば、$foo という名前のプロパティのフックに $this->foo というコード含まれれば、それはバックドプロパティです。 + 次のプロパティはバックドプロパティではなく、エラーが発生します: - Invalid virtual property + 無効な仮想プロパティ $temp; // Doesn't refer to $this->foo, so it doesn't count. + return $this->$temp; // $this->foo を正確に参照していません } } } @@ -295,20 +295,20 @@ class Example - For virtual properties, if a hook is omitted then that operation does - not exist and trying to use it will produce an error. - Virtual properties take up no memory space in an object. - Virtual properties are suited for "derived" properties, - such as those that are the combination of two other properties. + 仮想プロパティでは、フックを省略するとその操作は存在しないとみなされ、 + 使おうとするとエラーが発生します。 + 仮想プロパティはオブジェクト内でメモリを消費しません。 + 仮想プロパティは、例えば他の2つのプロパティを組み合わせて値を作るような + 「派生」プロパティに向いています。 - Virtual property + 仮想プロパティ $this->h * $this->w; } @@ -317,31 +317,31 @@ readonly class Rectangle } $s = new Rectangle(4, 5); -print $s->area; // prints 20 -$s->area = 30; // Error, as there is no set operation defined. +print $s->area; // 20 と表示 +$s->area = 30; // set が定義されていないためエラー ?> ]]> - Defining both a get and set hook on a virtual property is also allowed. + 仮想プロパティに対して getset 両方のフックを定義することもできます。 - Scoping + スコープ - All hooks operate in the scope of the object being modified. - That means they have access to all public, private, or protected methods of the object, - as well as any public, private, or protected properties, - including properties that may have their own property hooks. - Accessing another property from within a hook does not bypass the hooks defined on that property. + 全てのフックは、オブジェクトスコープで動作します。 + すなわち、全ての public、private、protected な + メソッドやプロパティに対してアクセスできます。 + フックを持つプロパティへアクセスする場合も同様です。 + フック内で別のプロパティにアクセスしても、そのフックがバイパスされることはありません。 - The most notable implication of this is that non-trivial hooks may sub-call - to an arbitrarily complex method if they wish. + 重要なのは、フック内で必要に応じて、 + 任意の複雑なメソッドを呼び出すこともできるという点です。 - Calling a method from a hook + フックからメソッド呼び出し - References + リファレンス - Because the presence of hooks intercept the read and write process for properties, - they cause issues when acquiring a reference to a property or with indirect - modification, such as $this->arrayProp['key'] = 'value';. - That is because any attempted modification of the value by reference would bypass a set hook, - if one is defined. + フックはプロパティの読み書きの操作へ干渉するため、 + プロパティへのリファレンスを取得したり、 + $this->arrayProp['key'] = 'value'; のような + 間接的な変更操作を行う場合に問題が発生します。 + リファレンスによる値の変更が set フックをバイパスしてしまうからです。 - In the rare case that getting a reference to a property that has hooks defined is necessary, - the get hook may be prefixed with & - to cause it to return by reference. - Defining both get and &get on the - same property is a syntax error. + プロパティからフックを経由してリファレンスを取得する必要がある場合、 + get フックの先頭に & + を付与します。 + 同じプロパティで get&get を両方定義すると + 構文エラーになります。 - Defining both &get and set hooks on a backed property is not allowed. - As noted above, writing to the value returned by reference would bypass the set hook. - On virtual properties, there is no necessary common value shared between the two hooks, so defining both is allowed. + バックドプロパティでは &get フックと set フックは同時に定義できません。 + 前述のとおり、リファレンスへの書き込みは set フックをバイパスしてしまうためです。 + 仮想プロパティでは、両フックから共有される実体の値がないため、同時に定義しても問題ありません。 - Writing to an index of an array property also involves an implicit reference. - For that reason, writing to a backed array property with hooks defined is - allowed if and only if it defines only a &get hook. - On a virtual property, writing to the array returned from either - get or &get is legal, - but whether that has any impact on the object depends on the hook implementation. + 配列プロパティのインデックスへ書き込みを行う場合も、暗黙的にリファレンスが使われます。 + 従って、フックを伴う配列のバックドプロパティの要素の書き換えができるのは、 + &get フックだけが定義されている場合に限ります。 + 仮想プロパティの場合、 + get&get から返された配列の書き換え自体は可能ですが、 + それが実際にオブジェクトに反映されるかはフックの実装次第です。 - Overwriting the entire array property is fine, and behaves the same as any other property. - Only working with elements of the array require special care. + 配列全体を上書きする場合は問題ありません。他のプロパティと同様に扱われます。 + 配列の個々の要素を操作する場合のみ注意が必要です。 - Inheritance + 継承 - Final hooks + final フック - Hooks may also be declared final, - in which case they may not be overridden. + フックは final としても宣言することもできます。 + その場合はオーバーライドできなくなります。 - Final hooks + final フックの例 strtoupper($this->username); - // But this is NOT allowed, because set is final in the parent. + // parentのsetがfinalのため許可されない。 set => strtoupper($value); } } @@ -434,23 +434,23 @@ class Manager extends User - A property may also be declared final. - A final property may not be redeclared by a child class in any way, - which precludes altering hooks or widening its access. + プロパティ自体を final と宣言することもできます。 + final で宣言されたプロパティは子クラスで再宣言できず、 + フックを変更したりアクセス権を広げることもできません。 - Declaring hooks final on a property that is declared final is redundant, - and will be silently ignored. - This is the same behavior as final methods. + final と宣言したプロパティに対しフックも final とするのは + 単に冗長であり無視されます。 + これは final メソッドと同じ動作です。 - A child class may define or redefine individual hooks on a property - by redefining the property and just the hooks it wishes to override. - A child class may also add hooks to a property that had none. - This is essentially the same as if the hooks were methods. + 子クラスでは、オーバーライドしたいフックを再定義することで、 + フックを個別に上書きできます。 + フックを持たないプロパティにフックを追加することもできます。 + フックがメソッドのように振る舞うという点で、一貫した動作です。 - Hook inheritance + フックの継承 - Each hook overrides parent implementations independently of each other. - If a child class adds hooks, any default value set on the property is removed, and must be redeclared. - That is the same consistent with how inheritance works on hook-less properties. + それぞれのフックは親の実装を個別にオーバーライドします。 + 子クラスがフックを追加する場合、親クラスのプロパティで設定されたデフォルト値は削除され、再宣言が必要です。 + これはフックのないプロパティを継承する場合と同じ動作です。 - Accessing parent hooks + 親フックへのアクセス - A hook in a child class may access the parent class's property using the - parent::$prop keyword, followed by the desired hook. - For example, parent::$propName::get(). - It may be read as "access the prop defined on the parent class, - and then run its get operation" (or set operation, as appropriate). + 子クラスのフックから parent::$prop に続き目的のフックを指定することで、 + 親クラスのプロパティにアクセスできます。 + 例えば parent::$propName::get() は、 + 「親クラスに定義された prop の get 操作を実行する」 + という意味になります。同様に set 操作も実行できます。 - If not accessed this way, the parent class's hook is ignored. - This behavior is consistent with how all methods work. - This also offers a way to access the parent class's storage, if any. - If there is no hook on the parent property, - its default get/set behavior will be used. - Hooks may not access any other hook except their own parent on their own property. + これらの方法でアクセスしない限り、親クラスのフックは無視されます。 + これはメソッドの動作と同じです。 + この方法で親クラスの記憶領域にアクセスできます。 + 親クラスのプロパティにフックが存在しない場合、 + デフォルトの get/set 動作が使われます。 + フックは、自分自身のプロパティにおける親フック以外は呼び出せません。 - The example above could be rewritten more efficiently as follows. + 上記の例をより効率的に書くと、以下のようになります。 - Parent hook access (set) + 親フックへのアクセス (set) - An example of overriding only a get hook could be: + get フックだけをオーバーライドする場合、次の例のようになります: - Parent hook access (get) + 親フックへのアクセス (get) - Serialization + シリアライズ - PHP has a number of different ways in which an object may be serialized, - either for public consumption or for debugging purposes. - The behavior of hooks varies depending on the use case. - In some cases, the raw backing value of a property will be used, - bypassing any hooks. - In others, the property will be read or written "through" the hook, - just like any other normal read/write action. + PHP には、オブジェクトを外部で利用したりしたり + デバッグしたりするための、いくつかのシリアライズ手段があります。 + その際のフックの挙動は、用途によって異なります。 + あるケースでは、プロパティに保存された生の値が使われ、 + フックはバイパスされます。 + 別のケースでは、通常の読み書きと同じように + フックを通して処理されます。 - var_dump: Use raw value - serialize: Use raw value - unserialize: Use raw value - __serialize()/__unserialize(): Custom logic, uses get/set hook - Array casting: Use raw value - var_export: Use get hook - json_encode: Use get hook - JsonSerializable: Custom logic, uses get hook - get_object_vars: Use get hook - get_mangled_object_vars: Use raw value + var_dump: 生の値を使用 + serialize: 生の値を使用 + unserialize: 生の値を使用 + __serialize()/__unserialize(): カスタムロジックと get/set フック + 配列キャスト: 生の値を使用 + var_export: get フックを使用 + json_encode: get フックを使用 + JsonSerializable: カスタムロジックと get フック + get_object_vars: get フックを使用 + get_mangled_object_vars: 生の値を使用 diff --git a/language/oop5/variance.xml b/language/oop5/variance.xml index 86f9712e1d9..a181e64de48 100644 --- a/language/oop5/variance.xml +++ b/language/oop5/variance.xml @@ -12,7 +12,7 @@ 共変性とは、子クラスのメソッドが、親クラスの戻り値よりも、より特定の、狭い型を返すことを許すことです。 - 一方で、反変性とは、親クラスのものよりも、より抽象的な、広い型を引数に指定することを許すものです。 + 反変性とは、親クラスのものよりも、より抽象的な、広い型を引数に指定することを許すものです。 @@ -247,6 +247,61 @@ Fatal error: Uncaught TypeError: Argument 1 passed to Animal::eat() must be an i + + プロパティの共変性と反変性(変性) + + デフォルトでは、プロパティは共変でも反変でもなく不変です。 + つまり、子クラスでは型は変更できません。 + 「get」操作は共変でなければならず、 + 「set」操作は反変でなければならないことが理由です。 + 双方を同時に満たすには、プロパティは不変である必要があります。 + + + PHP 8.4.0 から、インターフェイスや抽象クラスでの抽象プロパティや、 + 仮想プロパティ が追加されたことにより、 + プロパティが「get」または「set」だけを持つことを宣言できるようになりました。 + つまり、「get」操作だけが必要な抽象プロパティや仮想プロパティは共変性を持ちます。 + 同様に、「set」操作だけが必要な抽象プロパティや仮想プロパティは反変性を持ちます。 + + + ただし、いったんプロパティが「get」と「set」操作の両方を持つようになると、 + それ以上の拡張において共変あるいは反変にはなりません。 + その時点で不変となります。 + + + プロパティの型の変性 + + +]]> + + +