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 const 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