From a7051c42e6f16a759b4ddeeec10dd8c9ccbb5fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Napur=C3=AD?= Date: Sat, 23 Nov 2024 15:26:59 -0500 Subject: [PATCH] fix: sync oop docs --- language/constants.xml | 382 ++++++++++++++++++++++ language/oop5.xml | 1 + language/oop5/abstract.xml | 68 +++- language/oop5/property-hooks.xml | 535 +++++++++++++++++++++++++++++++ language/oop5/visibility.xml | 166 ++++++++-- 5 files changed, 1107 insertions(+), 45 deletions(-) create mode 100644 language/constants.xml create mode 100644 language/oop5/property-hooks.xml diff --git a/language/constants.xml b/language/constants.xml new file mode 100644 index 000000000..40c9f0ff1 --- /dev/null +++ b/language/constants.xml @@ -0,0 +1,382 @@ + + + + Constants + + + A constant is an identifier (name) for a simple value. As the name + suggests, that value cannot change during the execution of the + script (except for + magic constants, which aren't actually constants). + Constants are case-sensitive. By convention, constant + identifiers are always uppercase. + + + + + Prior to PHP 8.0.0, constants defined using the define + function may be case-insensitive. + + + + + The name of a constant follows the same rules as any label in PHP. A + valid constant name starts with a letter or underscore, followed + by any number of letters, numbers, or underscores. As a regular + expression, it would be expressed thusly: + ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$ + + + It is possible to define constants with reserved or even + invalid names, whose value can only be retrieved with the + constant function. However, doing so is not recommended. + + &tip.userlandnaming; + + + + Valid and invalid constant names + + +]]> + + + + + + For our purposes here, a letter is a-z, A-Z, and the ASCII + characters from 128 through 255 (0x80-0xff). + + + + + Like &link.superglobals;, the scope of a constant is global. + Constants can be accessed from anywhere in a script without regard to scope. + For more information on scope, read the manual section on + variable scope. + + + + + As of PHP 7.1.0, class constant may declare a visibility of protected + or private, making them only available in the hierarchical scope of the + class in which it is defined. + + + + + Syntax + + Constants can be defined using the const keyword, + or by using the define-function. + While define allows a constant to be + defined to an arbitrary expression, the const keyword has + restrictions as outlined in the next paragraph. + Once a constant is defined, it can never be + changed or undefined. + + + When using the const keyword, + only scalar (bool, int, + float and string) expressions and constant + arrays containing only scalar expressions are accepted. + It is possible to define constants as a resource, + but it should be avoided, as it can cause unexpected results. + + + The value of a constant is accessed simply by specifying its name. + Unlike variables, a constant is not prepended + with a $. + It is also possible to use the constant function to + read a constant's value if the constant's name is obtained dynamically. + Use get_defined_constants to get a list of + all defined constants. + + + + + Constants and (global) variables are in a different namespace. + This implies that for example &true; and + $TRUE are generally different. + + + + + If an undefined constant is used an Error is thrown. + Prior to PHP 8.0.0, undefined constants would be interpreted as a bare + word string, i.e. (CONSTANT vs "CONSTANT"). + This fallback is deprecated as of PHP 7.2.0, and an error of level + E_WARNING is issued when it happens. + Prior to PHP 7.2.0, an error of level + E_NOTICE has been issued instead. + See also the manual entry on why + $foo[bar] is + wrong (unless bar is a constant). + This does not apply to (fully) qualified constants, + which will always raise a Error if undefined. + + + + + To check if a constant is set, use the defined function. + + + + + These are the differences between constants and variables: + + + + Constants do not have a dollar sign ($) + before them; + + + + + Constants may be defined and accessed anywhere without regard + to variable scoping rules; + + + + + Constants may not be redefined or undefined once they have been + set; and + + + + + Constants may only evaluate to scalar values or arrays. + + + + + + + + Defining Constants + + +]]> + + + + + + + Defining Constants using the <literal>const</literal> keyword + + +]]> + + + + + + + As opposed to defining constants using define, + constants defined using the const keyword must be + declared at the top-level scope because they are defined at compile-time. + This means that they cannot be declared inside functions, loops, + if statements or + try/catch blocks. + + + + + &reftitle.seealso; + + + Class Constants + + + + + + + Predefined constants + + + PHP provides a large number of predefined constants to any script + which it runs. Many of these constants, however, are created by + various extensions, and will only be present when those extensions + are available, either via dynamic loading or because they have + been compiled in. + + + + + Magic constants + + There are nine magical constants that change depending on + where they are used. For example, the value of + __LINE__ depends on the line that it's + used on in your script. All these "magical" constants are resolved + at compile time, unlike regular constants, which are resolved at runtime. + These special constants are case-insensitive and are as follows: + + + + PHP's magic constants + + + + &Name; + &Description; + + + + + __LINE__ + + The current line number of the file. + + + + __FILE__ + + The full path and filename of the file with symlinks resolved. If used inside an include, + the name of the included file is returned. + + + + __DIR__ + + The directory of the file. If used inside an include, + the directory of the included file is returned. This is equivalent + to dirname(__FILE__). This directory name + does not have a trailing slash unless it is the root directory. + + + + __FUNCTION__ + + The function name, or {closure} for anonymous functions. + + + + __CLASS__ + + The class name. The class name includes the namespace + it was declared in (e.g. Foo\Bar). + When used + in a trait method, __CLASS__ is the name of the class the trait + is used in. + + + + __TRAIT__ + + The trait name. The trait name includes the namespace + it was declared in (e.g. Foo\Bar). + + + + __METHOD__ + + The class method name. + + + + __PROPERTY__ + + Only valid inside a property hook. It is equal to the name of the property. + + + + __NAMESPACE__ + + The name of the current namespace. + + + + ClassName::class + + The fully qualified class name. + + + + +
+
+ + + &reftitle.seealso; + + + ::class + get_class + get_object_vars + file_exists + function_exists + + + + +
+
+ + diff --git a/language/oop5.xml b/language/oop5.xml index ac27f968f..52c870ca9 100644 --- a/language/oop5.xml +++ b/language/oop5.xml @@ -26,6 +26,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 39ffbcd5b..9cb3b3fea 100644 --- a/language/oop5/abstract.xml +++ b/language/oop5/abstract.xml @@ -1,18 +1,18 @@ - + Abstracción de clases - + - PHP 5 introduce clases y métodos abstractos. Las clases definidas como - abstractas no se pueden instanciar y cualquier clase que - contiene al menos un método abstracto debe ser definida como tal. Los métodos - definidos como abstractos simplemente declaran la firma del método, pero no pueden - definir la implementación. + PHP tiene clases, métodos y propiedades abstractas. + Las clases definidas como abstractas no se pueden instanciar y cualquier clase + que contiene al menos un método abstracto debe ser definida como tal. + Los métodos definidos como abstractos simplemente declaran la firma del método, y si es público o protegido; no pueden definir la implementación. + pero no pueden definir la implementación. Las propiedades definidas como abstractas pueden declarar un requisito para get o set, y pueden proporcionar una implementación para una, pero no ambas, operaciones. - + Cuando se hereda de una clase abstracta, todos los métodos definidos como abstractos en la declaración de la clase madre deben ser definidos en la clase hija; además, @@ -20,13 +20,29 @@ visibilidad. Por ejemplo, si el método abstracto está definido como protegido, la implementación de la función debe ser definida como protegida o pública, pero nunca como privada. Por otra parte, - las firmas de los métodos tienen que coincidir, es decir, la declaración de tipos y el número + las firmas de los métodos tienen que coincidir, es decir, la declaración de tipos y el número de argumentos requeridos deben ser los mismos. Por ejemplo, si la clase derivada define un argumento opcional y la firma del método abstracto no lo hace, no habría conflicto con la firma. Esto también se aplica a los constructores a partir de PHP 5.4. Antes de PHP 5.4, las firmas del constructor podían ser diferentes. - + + + Al heredar de una clase abstracta, todos los métodos marcados como abstractos en la declaración de la clase padre deben ser definidos por la clase hija y seguir las reglas habituales de + herencia y + compatibilidad de firmas. + + + + A partir de PHP 8.4, una clase abstracta puede declarar una propiedad abstracta, ya sea pública o protegida. + Una propiedad abstracta protegida puede ser satisfecha por una propiedad que sea legible/escribible tanto desde el ámbito protegido como público. + + + Una propiedad abstracta puede ser satisfecha ya sea por una propiedad estándar o por una propiedad con + hooks definidos, correspondientes a la operación requerida. + + + Ejemplo de clase abstracta @@ -86,7 +102,7 @@ FOO_ClaseConcreta2 ]]> - + Ejemplo de clase abstracta @@ -129,12 +145,30 @@ Mrs. Pacwoman ]]> - - - El código antiguo que no tenga clases o funciones definidas por el usuario - llamadas 'abstract' deberían ejecutarse sin modificaciones. - - + + Una propiedad abstracta en una clase abstracta puede proporcionar implementaciones para cualquier hook, + pero debe tener declarado get o set, pero no definido (como en el ejemplo anterior). + + + Ejemplo de propiedad abstracta + + +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: + + + + + 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 all properties: get and set. + They allow overriding the read and write behavior of a property, respectively. + + + 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. + + + + 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 wil 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/visibility.xml b/language/oop5/visibility.xml index daa69b03c..a6799128c 100644 --- a/language/oop5/visibility.xml +++ b/language/oop5/visibility.xml @@ -1,28 +1,27 @@ - + Visibilidad La visibilidad de una propiedad, un método o (a partir de PHP 7.1.0) una constante se puede definir anteponiendo - a su declaración una de las palabras reservadas public, - protected o - private. A los miembros de clase declarados como 'public' se puede + a su declaración una de las palabras reservadas public, + protected o + private. A los miembros de clase declarados como 'public' se puede acceder desde donde sea; a los miembros declarados como 'protected', solo desde la misma clase, mediante clases heredadas o desde la clase padre. A los miembros declarados como 'private' únicamente se puede acceder desde la clase que los definió. - + Visibilidad de propiedades - - Las propiedades de clases deben ser definidas como 'public', 'private' - o 'protected'. Si se declaran usando var, - serán definidas como 'public'. - - + + Las propiedades de clases deben ser definidas como 'public', 'private' + o 'protected'. Las propiedades declaradas sin especificar explícitamente + una visibilidad se consideran públicas. + Declaración de propiedades @@ -75,21 +74,132 @@ echo $obj2->protected; // Error Fatal echo $obj2->private; // Undefined $obj2->printHello(); // Muestra Public2, Protected2, Undefined -?> +?> ]]> - - - - La forma de declaración de una variable de PHP 4 con la - palabra clave var todavía es soportado - (como un sinónimo de public) por razones de compatibilidad. En PHP 5 - antes de 5.1.3, su uso genera un Warning E_STRICT. - - - - + + Visibilidad asimétrica de propiedades + + A partir de PHP 8.4, las propiedades también pueden tener su + visibilidad configurada de manera asimétrica, con un alcance diferente para + lectura (get) y escritura (set). + Específicamente, la visibilidad de set puede + especificarse por separado, siempre que no sea más permisiva que la + visibilidad predeterminada. + + + Visibilidad asimétrica de propiedades + +author = $author; // OK + $this->pubYear = $year; // Fatal Error + } +} + +$b = new Book('How to PHP', 'Peter H. Peterson', 2024); + +echo $b->title; // Works +echo $b->author; // Works +echo $b->pubYear; // Fatal Error + +$b->title = 'How not to PHP'; // Fatal Error +$b->author = 'Pedro H. Peterson'; // Fatal Error +$b->pubYear = 2023; // Fatal Error +?> +]]> + + + There are a few caveats regarding asymmetric visibility: + + + + Only typed properties may have a separate set visibility. + + + + + The set visibility must be the same + as get or more restrictive. That is, + public protected(set) and protected protected(set) + are allowed, but protected public(set) will cause a syntax error. + + + + + If a property is public, then the main visibility may be + omitted. That is, public private(set) and private(set) + will have the same result. + + + + + A property with private(set) visibility + is automatically final, and may not be redeclared in a child class. + + + + + Obtaining a reference to a property follows set visibility, not get. + That is because a reference may be used to modify the property value. + + + + + Similarly, trying to write to an array property involves both a get and + set operation internally, and therefore will follow the set + visibility, as that is always the more restrictive. + + + + + When a class extends another, the child class may redefine + any property that is not final. When doing so, + it may widen either the main visibility or the set + visibility, provided that the new visibility is the same or wider + than the parent class. However, be aware that if a private + property is overridden, it does not actually change the parent's property + but creates a new property with a different internal name. + + + Asymmetric Property inheritance + + +]]> + + + + + Visibilidad de Métodos @@ -164,7 +274,7 @@ class Bar public function testPublic() { echo "Bar::testPublic\n"; } - + private function testPrivate() { echo "Bar::testPrivate\n"; } @@ -175,14 +285,14 @@ class Foo extends Bar public function testPublic() { echo "Foo::testPublic\n"; } - + private function testPrivate() { echo "Foo::testPrivate\n"; } } $myFoo = new Foo(); -$myFoo->test(); // Bar::testPrivate +$myFoo->test(); // Bar::testPrivate // Foo::testPublic ?> ]]> @@ -190,7 +300,7 @@ $myFoo->test(); // Bar::testPrivate - + Visibilidad de las constantes @@ -256,7 +366,7 @@ $myclass2->foo2(); // Funcionan Public y Protected, pero no Private - + Visibilidad desde otros objetos