mirror of
https://github.com/php/doc-es.git
synced 2026-03-26 00:12:06 +01:00
git-svn-id: https://svn.php.net/repository/phpdoc/es/trunk@337561 c90b9560-bf6c-de11-be94-00142212c4b1
1136 lines
33 KiB
XML
1136 lines
33 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- $Revision$ -->
|
|
<!-- EN-Revision: addba5dcce72f6c9e66751d0d12fa830bdef4707 Maintainer: chuso Status: ready -->
|
|
<appendix xml:id="oop4" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
<title>Clases y Objetos (PHP 4)</title>
|
|
|
|
<sect1 xml:id="keyword.class">
|
|
<title><literal>class</literal></title>
|
|
<para>
|
|
Una clase es una colección de variables y funciones que trabajan con
|
|
estas variables. Las variables se definen utilizando <literal>var</literal> y
|
|
las funciones utilizando <literal>function</literal>. Una clase se define empleando la
|
|
siguiente sintáxis:
|
|
</para>
|
|
<para>
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class Cart {
|
|
var $items; // Objetos en nuestro carrito de compras
|
|
|
|
// Agregar $num artículos de $artnr al carrito
|
|
|
|
function add_item($artnr, $num) {
|
|
$this->items[$artnr] += $num;
|
|
}
|
|
|
|
// Sacar $num artículos de $artnr fuera del carrito
|
|
|
|
function remove_item($artnr, $num) {
|
|
if ($this->items[$artnr] > $num) {
|
|
$this->items[$artnr] -= $num;
|
|
return true;
|
|
} elseif ($this->items[$artnr] == $num) {
|
|
unset($this->items[$artnr]);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</para>
|
|
|
|
<para>
|
|
Esto define una clase llamada Cart que consiste de una matriz asociativa
|
|
de artículos en el carrito y dos funciones para agregar y quitar
|
|
elementos de este carrito.
|
|
</para>
|
|
|
|
<warning>
|
|
<simpara>
|
|
<emphasis>NO</emphasis> se puede separar una definición de clase en
|
|
múltiples ficheros. <emphasis>Tampoco</emphasis> es posible separar una
|
|
definición de clase en múltiples bloques PHP, a menos que la división sea dentro
|
|
de una declaración de método. Lo siguiente no funcionará:
|
|
</simpara>
|
|
<para>
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class test {
|
|
?>
|
|
<?php
|
|
function test() {
|
|
print 'OK';
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</para>
|
|
<simpara>
|
|
Sin embargo, lo siguiente es permitido:
|
|
</simpara>
|
|
<para>
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class test {
|
|
function test() {
|
|
?>
|
|
<?php
|
|
print 'OK';
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</para>
|
|
</warning>
|
|
|
|
<simpara>
|
|
Las siguientes notas de precaución son válidas para PHP 4.
|
|
</simpara>
|
|
|
|
<caution>
|
|
<simpara>
|
|
El nombre <literal>stdClass</literal> es usado internamente por
|
|
Zend y está reservado. No es posible tener una clase llamada
|
|
<literal>stdClass</literal> en PHP.
|
|
</simpara>
|
|
</caution>
|
|
|
|
<caution>
|
|
<simpara>
|
|
Los nombres de función <literal>__sleep</literal> y
|
|
<literal>__wakeup</literal> son mágicos en las clases de PHP. No
|
|
se puede tener funciones con estos nombres en ninguna
|
|
clase a menos que se desee la funcionalidad mágica asociada
|
|
con ellas. Ver abajo para más información.
|
|
</simpara>
|
|
</caution>
|
|
|
|
<caution>
|
|
<simpara>
|
|
PHP reserva todos los nombres de función que empiezan con <literal>__</literal> como mágicos.
|
|
Se recomienda que no se utilicen nombres de función con
|
|
<literal>__</literal> en PHP a menos que se quiera alguna funcionalidad mágica que esté documentada.
|
|
</simpara>
|
|
</caution>
|
|
|
|
<simpara>
|
|
En PHP 4, para variables <literal>var</literal> solamente se permiten inicializadores constantes.
|
|
Para inicializar variables con valores no constantes,
|
|
se requiere una función de inicialización que es invocada automáticamente
|
|
cuando un objeto se está construyendo a partir de la clase.
|
|
Tal función es llamada un constructor (ver más abajo).
|
|
</simpara>
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class Cart {
|
|
/* Ninguno de estos funcionará en PHP 4. */
|
|
var $todays_date = date("Y-m-d");
|
|
var $name = $firstname;
|
|
var $owner = 'Fred ' . 'Jones';
|
|
/* Pero matrices que contengan valores constantes, si lo harán. */
|
|
var $items = array("VCR", "TV");
|
|
}
|
|
|
|
/* Esta es la manera en que debería hacerse. */
|
|
class Cart {
|
|
var $todays_date;
|
|
var $name;
|
|
var $owner;
|
|
var $items = array("VCR", "TV");
|
|
|
|
function Cart() {
|
|
$this->todays_date = date("Y-m-d");
|
|
$this->name = $GLOBALS['firstname'];
|
|
/* etc. . . */
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Las clases son tipos, es decir, son planos para las variables
|
|
de verdad. Se tiene que crear una variable del tipo deseado con el
|
|
operador <literal>new</literal>.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$cart = new Cart;
|
|
$cart->add_item("10", 1);
|
|
|
|
$another_cart = new Cart;
|
|
$another_cart->add_item("0815", 3);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Esto crea los objetos <varname>$cart</varname> y
|
|
<varname>$another_cart</varname>, ambos de la clase Cart. La función
|
|
<literal>add_item()</literal> del objeto <varname>$cart</varname> es invocada para agregar 1
|
|
elemento del artículo 10 a <varname>$cart</varname>. 3 elementos del
|
|
artículo número 0815 son agregados a <varname>$another_cart</varname>.
|
|
</para>
|
|
|
|
<para>
|
|
Ambos, <varname>$cart</varname> y <varname>$another_cart</varname>, tienen
|
|
las funciones <literal>add_item()</literal>, <literal>remove_item()</literal>
|
|
y una variable <varname>items</varname>. Estas son funciones y variables distintas. Se puede
|
|
pensar en los objetos como algo semejante a directorios en un sistema de ficheros.
|
|
En un sistema de ficheros se pueden tener dos diferentes ficheros llamados
|
|
<filename>README.TXT</filename>, siempre y cuando éstos, estén en directorios distintos.
|
|
Tal como con los directorios donde se tiene que teclear la trayectoria de nombre completa para
|
|
poder alcanzar cada fichero desde el directorio de nivel máximo, se tiene que especificar
|
|
el nombre completo de la función que se desea invocar: en términos de PHP, el directorio de
|
|
nivel máximo sería el espacio de nombres global, y el separador en las trayectorias de nombre
|
|
sería <literal>-></literal>. Así, los nombres
|
|
<varname>$cart->items</varname> y
|
|
<varname>$another_cart->items</varname> nombran a dos variables distintas.
|
|
Nótese que la variable se llama <varname>$cart->items</varname>, y no
|
|
<varname>$cart->$items</varname>, es decir, un nombre de variable en PHP tiene
|
|
únicamente un sólo signo de dólar (<literal>$</literal>).
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
// correcto, solamente un $
|
|
$cart->items = array("10" => 1);
|
|
|
|
// inválido, porque $cart->$items se vuelve $cart->""
|
|
$cart->$items = array("10" => 1);
|
|
|
|
// correcto, pero podría ser o no lo que se pretendía:
|
|
// $cart->$myvar se vuelve $cart->items
|
|
$myvar = 'items';
|
|
$cart->$myvar = array("10" => 1);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Dentro de una definición de clase, no se conoce bajo que nombre el objeto será
|
|
accesible dentro del programa: al momento en que la clase Cart fue
|
|
escrita, se desconocía si el objeto sería nombrado
|
|
<varname>$cart</varname>, <varname>$another_cart</varname>, o algo
|
|
distinto posteriormente. Siendo así,
|
|
no se puede escribir <varname>$cart->items</varname> dentro de la clase Cart
|
|
propiamente. En cambio, para ser capáz de acceder a sus propias funciones y
|
|
variables desde el interior de una clase, es posible utilizar la pseudovariable
|
|
<varname>$this</varname> que puede ser leída como 'lo mío' u 'objeto
|
|
actual'. Así, '<varname>$this->items[$artnr]</varname> +=
|
|
<varname>$num</varname>' puede leerse como 'agregar <varname>$num</varname> al
|
|
contador <varname>$artnr</varname> de mi propia matriz de artículos' o 'agregar
|
|
<varname>$num</varname> al contador <varname>$artnr</varname> de la matriz de
|
|
artículos dentro del objeto actual'.
|
|
</para>
|
|
|
|
<note>
|
|
<para>
|
|
La pseudovariable <varname>$this</varname> normalmente no está definida si
|
|
el método en el que se encuentra alojada es invocado de manera estática. Pero,
|
|
ésta, no es una regla estricta: <varname>$this</varname> está definida si un método es
|
|
invocado estáticamente desde el interior de otro objeto. En este caso, el valor de
|
|
<varname>$this</varname> será aquel del objeto que realiza la invocación. Esto se
|
|
ilustra en el siguiente ejemplo:
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class A
|
|
{
|
|
function foo()
|
|
{
|
|
if (isset($this)) {
|
|
echo '$this is defined (';
|
|
echo get_class($this);
|
|
echo ")\n";
|
|
} else {
|
|
echo "\$this is not defined.\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
class B
|
|
{
|
|
function bar()
|
|
{
|
|
A::foo();
|
|
}
|
|
}
|
|
|
|
$a = new A();
|
|
$a->foo();
|
|
A::foo();
|
|
$b = new B();
|
|
$b->bar();
|
|
B::bar();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
$this is defined (a)
|
|
$this is not defined.
|
|
$this is defined (b)
|
|
$this is not defined.
|
|
]]>
|
|
</screen>
|
|
</informalexample>
|
|
</para>
|
|
</note>
|
|
|
|
<note>
|
|
<para>
|
|
Existen algunas funciones apropiadas para manejar clases y objetos. Quizas se quiera
|
|
echar un vistazo al las <link linkend="ref.classobj">Funciones
|
|
Clase/Objeto</link>.
|
|
</para>
|
|
</note>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="keyword.extends">
|
|
<title><literal>extends</literal></title>
|
|
|
|
<para>
|
|
Frecuentemente son necesarias clases con variables y funciones semejantes
|
|
a los de otra clase ya existente. De hecho, es una buena práctica
|
|
definir una clase genérica que pueda ser utilizada en todos sus
|
|
proyectos y adaptar esta clase a las necesidades de cada uno de sus
|
|
proyectos específicos. Para facilitar esto, las clases pueden ser
|
|
extensiones de otras clases. La clase extendida o derivada
|
|
tiene todas las variables y funciones de la clase base (esto es
|
|
llamado 'herencia' a pesar del hecho de que nadie ha muerto) y aquello que
|
|
se agregue en la definición extendida. No es posible
|
|
disminuir una clase, es decir, remover la definición de cualquier función o
|
|
variable existente. Una clase extendida siempre es dependiente de
|
|
una sóla clase base, es decir, la herencia múltiple no
|
|
está soportada. Las clases son extendidas utilizando la palabra clave '<literal>extends</literal>'.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class Named_Cart extends Cart {
|
|
var $owner;
|
|
|
|
function set_owner ($name) {
|
|
$this->owner = $name;
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Esto define una clase llamada Named_Cart que contiene todas las variables y funciones de
|
|
Cart más una variable adicional <varname>$owner</varname> y una
|
|
función adicional <literal>set_owner()</literal>. Se crea un carrito con nombre de la manera habitual y
|
|
ahora se puede indicar y obtener el dueño del carrito. Aún se pueden utilizar las funciones de
|
|
carrito normal en los carritos con nombre:
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$ncart = new Named_Cart; // Crear un carrito con nombre
|
|
$ncart->set_owner("kris"); // Nombrar el carrito
|
|
print $ncart->owner; // imprimir el nombre del dueño del carrito
|
|
$ncart->add_item("10", 1); // (funcionalidad heredada de cart)
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Esto también es llamado una relación "padre-hijo". Se crea una clase,
|
|
el padre, y se utiliza <literal>extends</literal> para crear una nueva clase
|
|
<emphasis>basada</emphasis> en la clase padre: la clase hijo. Es posible
|
|
incluso utilizar esta nueva clase hijo y crear otra clase basada en esta clase
|
|
hijo.
|
|
</para>
|
|
<note>
|
|
<para>
|
|
¡Las clases deben de estar definidas antes de ser utilizadas! Si se desea que la clase
|
|
<literal>Named_Cart</literal> extienda a la clase
|
|
<literal>Cart</literal>, se tendrá que definir primero la clase
|
|
<literal>Cart</literal>. Si se quiere crear otra clase llamada
|
|
<literal>Yellow_named_cart</literal> basada en la clase
|
|
<literal>Named_Cart</literal> se tiene que definir primero
|
|
<literal>Named_Cart</literal>. Para resumir: el orden en que las
|
|
clases se definen es importante.
|
|
</para>
|
|
</note>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="oop4.constructor">
|
|
<title>Constructores</title>
|
|
|
|
<para>
|
|
Los constructores son funciones en una clase que son invocadas
|
|
automáticamente cuando se crea una nueva instancia de una clase con
|
|
<literal>new</literal>. Una función se vuelve un constructor, cuando
|
|
tiene el mismo nombre que la clase. Si una clase
|
|
no tiene constructor, el constructor de la clase base será
|
|
invocado, si es que existe.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class Auto_Cart extends Cart {
|
|
function Auto_Cart() {
|
|
$this->add_item("10", 1);
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Esto define una clase Auto_Cart que es una Cart más un constructor
|
|
que inicializa el carrito con un elemento del artículo número "10"
|
|
en cada ocasión que un nuevo Auto_Cart es creado con "<literal>new</literal>".
|
|
Los constructores pueden llevar argumentos y estos argumentos pueden ser opcionales, lo que
|
|
los hace mucho más útiles. Para poder seguir utilizando la clase
|
|
sin parámetros, todos los parámetros para los constructores deben hacerse
|
|
opcionales proporcionando los valores por omisión.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class Constructor_Cart extends Cart {
|
|
function Constructor_Cart($item = "10", $num = 1) {
|
|
$this->add_item ($item, $num);
|
|
}
|
|
}
|
|
|
|
// Comprar las mismas cosas aburridas de siempre.
|
|
$default_cart = new Constructor_Cart;
|
|
|
|
// Comprar en serio...
|
|
$different_cart = new Constructor_Cart("20", 17);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
También se puede utilizar el operador <literal>@</literal> para
|
|
<emphasis>callar</emphasis> los errores que ocurren en el constructor, para ejemplo
|
|
<literal>@new</literal>.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class A
|
|
{
|
|
function A()
|
|
{
|
|
echo "I am the constructor of A.<br />\n";
|
|
}
|
|
|
|
function B()
|
|
{
|
|
echo "I am a regular function named B in class A.<br />\n";
|
|
echo "I am not a constructor in A.<br />\n";
|
|
}
|
|
}
|
|
|
|
class B extends A
|
|
{
|
|
}
|
|
|
|
// Esto invocará a B() como un constructor
|
|
$b = new B;
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
La función B() en la clase A repentinamente se volverá un
|
|
constructor en la clase B, aunque nunca fue la intención que lo fuera.
|
|
A PHP 4 no le importa si la función ha sido definida
|
|
en la clase B, o si ha sido heredada.
|
|
</para>
|
|
|
|
<caution>
|
|
<simpara>
|
|
PHP no llama automáticamente a los constructores de la clase base
|
|
desde un constructor de una clase derivada. Es su responsabilidad
|
|
propagar la llamada a los constructores en la jerarquía superior
|
|
cuando sea apropiado.
|
|
</simpara>
|
|
</caution>
|
|
|
|
<para>
|
|
Los destructores son funciones que son invocadas automáticamente
|
|
cuando un objeto es destruido, ya sea con <function>unset</function>
|
|
o simplemente al salir del alcance. No existen destructores
|
|
en PHP. En cambio, se puede utilizar <function>register_shutdown_function</function>
|
|
para simular la mayoría de los efectos de los destructores.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="keyword.paamayim-nekudotayim"><!-- :-) -->
|
|
<title>Operador de Resolución de Alcance (<literal>::</literal>)</title>
|
|
|
|
<caution>
|
|
<simpara>
|
|
Lo siguiente es válido para PHP 4 y posteriores únicamente.
|
|
</simpara>
|
|
</caution>
|
|
|
|
<para>
|
|
Algunas veces es útil hacer referencia a funciones y variables
|
|
en las clases base o hacer referencia a funciones que se encuentran en clases que
|
|
aún no tienen ninguna instancia. El operador <literal>::</literal>
|
|
es utilizado para realizar esto.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class A {
|
|
function example() {
|
|
echo "I am the original function A::example().<br />\n";
|
|
}
|
|
}
|
|
|
|
class B extends A {
|
|
function example() {
|
|
echo "I am the redefined function B::example().<br />\n";
|
|
A::example();
|
|
}
|
|
}
|
|
|
|
// no hay objetos de la clase A.
|
|
// esto imprimirá
|
|
// I am the original function A::example().<br />
|
|
A::example();
|
|
|
|
// crear un objeto de clase B.
|
|
$b = new B;
|
|
|
|
// esto imprimirá
|
|
// I am the redefined function B::example().<br />
|
|
// I am the original function A::example().<br />
|
|
$b->example();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
El ejemplo de arriba invoca a la función <literal>example()</literal> en
|
|
la clase A, pero no hay un objeto de la clase A, así que
|
|
no es posible escribir <literal>$a->example()</literal> o algo semejante. En vez de ello
|
|
se invoca <literal>example()</literal> como una 'función de clase', es decir, como una
|
|
función de la clase misma, no de un objeto de esa
|
|
clase.
|
|
</para>
|
|
|
|
<para>
|
|
Existen funciones de clase, pero no existen variables de clase.
|
|
De hecho, no existe objeto alguno en el momento de la invocación.
|
|
Siendo así, una función de clase no puede utilizar ninguna variable de objeto (pero
|
|
puede utilizar variables locales y globales), y no puede utilizar
|
|
<varname>$this</varname> para nada.
|
|
</para>
|
|
|
|
<para>
|
|
En el ejemplo anterior, la clase B vuelve a definir la función
|
|
<literal>example()</literal>. La definición original en la clase A queda desplazada
|
|
y ya no se encuentra disponible, a menos que se haga referencia específicamente
|
|
a la implementación de <literal>example()</literal> en la clase A empleando el
|
|
operador ::. Se escribe <literal>A::example()</literal> para realizar esto (de hecho, se
|
|
debe escribir <literal>parent::example()</literal>, tal como se muestra en la siguiente
|
|
sección).
|
|
</para>
|
|
|
|
<para>
|
|
En este contexto, existe un objeto actual y puede tener variables de
|
|
objeto. Así, cuando se usen desde el INTERIOR de una función de objeto, se puede utilizar
|
|
<varname>$this</varname> y las variables de objeto.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="keyword.parent">
|
|
<title><literal>parent</literal></title>
|
|
|
|
<para>
|
|
Se puede encontrar que se escribe código que hace referencia a
|
|
variables y funciones en las clases base. Esto es particularmente
|
|
cierto si la clase derivada es un refinamiento
|
|
o especialización de código en la clase base.
|
|
</para>
|
|
|
|
<para>
|
|
En vez de utilizar el nombre de la clase base en el
|
|
código, debe utilizarse el nombre especial
|
|
<literal>parent</literal>, que hace referencia al nombre de la
|
|
clase base tal como se especifica en la declaración <literal>extends</literal>
|
|
de la clase. Al hacer esto, se evita utilizar el
|
|
nombre de la clase base en más de una ubicación. Si el
|
|
árbol de herencia llegase a cambiar durante la implementación, el
|
|
cambio puede llevarse a cabo con facilidad simplemente cambiando la
|
|
declaración <literal>extends</literal> de la clase.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class A {
|
|
function example() {
|
|
echo "I am A::example() and provide basic functionality.<br />\n";
|
|
}
|
|
}
|
|
|
|
class B extends A {
|
|
function example() {
|
|
echo "I am B::example() and provide additional functionality.<br />\n";
|
|
parent::example();
|
|
}
|
|
}
|
|
|
|
$b = new B;
|
|
|
|
// Este ejemplo invocará a B::example(), que a su vez invocará a A::example().
|
|
$b->example();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="oop4.serialization">
|
|
<title>Serialización de objetos - objetos en las sesiones</title>
|
|
|
|
<para>
|
|
<function>serialize</function> devuelve un string conteniendo una
|
|
representación de flujo de bytes de cualquier valor que pueda ser almacenado en
|
|
PHP. <function>unserialize</function> puede utilizar este string para
|
|
recrear los valores originales de la variable. Al utilizar serialize para
|
|
guardar un objeto, se guardarán todas las variables en el objeto. Las
|
|
las funciones en un objeto no serán guardadas, solamente el nombre de
|
|
la clase.
|
|
</para>
|
|
|
|
<para>
|
|
Para poder utilizar <function>unserialize</function> en un objeto, la
|
|
clase de este objeto debe estar definida. Es decir, si se tiene un objeto
|
|
<varname>$a</varname> de la clase A en <filename>page1.php</filename> y se serializa, se
|
|
obtiene un string que hace referencia a la clase A y que contiene todos los valores de las variables
|
|
contenidas en <varname>$a</varname>. Si se desea ser capáz de revertir la serialización
|
|
de esto en <filename>page2.php</filename>, recreando <varname>$a</varname> de la clase A, la
|
|
definición de la clase A debe estar presente en <filename>page2.php</filename>.
|
|
Esto puede hacerse por ejemplo al colocar la definición de clase de la clase A
|
|
en un fichero de include e incluyendo este fichero en ambas páginas
|
|
<filename>page1.php</filename> y en <filename>page2.php</filename>.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
// classa.inc:
|
|
|
|
class A {
|
|
var $one = 1;
|
|
|
|
function show_one() {
|
|
echo $this->one;
|
|
}
|
|
}
|
|
|
|
// page1.php:
|
|
|
|
include("classa.inc");
|
|
|
|
$a = new A;
|
|
$s = serialize($a);
|
|
// almacenar $s en algún lado donde page2.php pueda encontrarle.
|
|
$fp = fopen("store", "w");
|
|
fwrite($fp, $s);
|
|
fclose($fp);
|
|
|
|
// page2.php:
|
|
|
|
// esto es necesario para que unserialize funcione apropiadamente.
|
|
include("classa.inc");
|
|
|
|
$s = implode("", @file("store"));
|
|
$a = unserialize($s);
|
|
|
|
// ahora utilizar la función show_one() del objeto $a.
|
|
$a->show_one();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Si se están utilizando sesiones y se emplea <function>session_register</function>
|
|
para registrar objetos, estos objetos son serializados automáticamente
|
|
al final de cada página de PHP, y la serialización les es revertida automáticamente en
|
|
en cada una de las páginas siguientes. Esto significa básicamente que estos objetos
|
|
pueden aparecer en cualquiera de las páginas una vez que se vuelven parte de la
|
|
sesión.
|
|
</para>
|
|
|
|
<para>
|
|
Se recomienda enfáticamente que se incluyan las definiciones
|
|
de clase de semejantes objetos en todas las
|
|
páginas, aun si no se usan estas clases en todas las
|
|
páginas. Si no se hace y a un objeto se le revierte la
|
|
serialización sin que su definición de clase esté presente,
|
|
perderá su asociacón de clase y se volverá un objeto de la clase
|
|
<classname>__PHP_Incomplete_Class_Name</classname> sin ninguna función disponible
|
|
por completo, es decir, se volverá bastante inútil.
|
|
</para>
|
|
|
|
<para>
|
|
Así que si en el ejemplo anterior <varname>$a</varname> se volviese parte de una sesión
|
|
al ejecutar <literal>session_register("a")</literal>, se debería incluir el
|
|
fichero <literal>classa.inc</literal> en todas las páginas, no sólo en <filename>page1.php</filename>
|
|
y en <filename>page2.php</filename>.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="oop4.magic-functions">
|
|
<title>Las funciones mágicas <literal>__sleep</literal> y <literal>__wakeup</literal></title>
|
|
|
|
<para>
|
|
<function>serialize</function> revisa si la clase tiene una función con
|
|
el nombre mágico <literal>__sleep</literal>. Si es así, esa función es
|
|
ejecutada antes de cualquier serialización. Puede limpiar el objeto
|
|
y se supone que devuelva una matriz con los nombres de todas las variables
|
|
de ese objeto que deberán ser serializadas.
|
|
Si el método no devuelve nada, entonces &null; es serializado y
|
|
<constant>E_NOTICE</constant> es emitida.
|
|
</para>
|
|
|
|
<para>
|
|
La intención de utilizar <literal>__sleep</literal> es asentar datos
|
|
pendientes o realizar tareas similares de limpieza. También la función es
|
|
útil si se tienen objetos muy grandes que no necesitan ser
|
|
guardados completamente.
|
|
</para>
|
|
|
|
<para>
|
|
De manera correspondiente, <function>unserialize</function> revisa la
|
|
presencia de una función con el nombre mágico de
|
|
<literal>__wakeup</literal>. Si está presente, esta función puede
|
|
reconstruir cualesquiera recursos que el objeto pueda tener.
|
|
</para>
|
|
|
|
<para>
|
|
El propósito de utilizar <literal>__wakeup</literal> es
|
|
reestablecer cualquier conexión a bases de datos que se pudiese haber perdido
|
|
durante la serialización y realizar otras tareas de
|
|
reinicialización.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="oop4.newref">
|
|
<title>Referencias dentro del constructor</title>
|
|
<para>
|
|
Crear referencias dentro del cosntructor puede llevar a resultados
|
|
confusos. Esta sección a manera de guía ayuda a evitar problemas.
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class Foo {
|
|
function Foo($name) {
|
|
// crear una referencia dentro de la matriz global $globalref
|
|
global $globalref;
|
|
$globalref[] = &$this;
|
|
// hacer que el nombre sea el valor que ha sido pasado
|
|
$this->setName($name);
|
|
// y mostrarlo
|
|
$this->echoName();
|
|
}
|
|
|
|
function echoName() {
|
|
echo "<br />", $this->name;
|
|
}
|
|
|
|
function setName($name) {
|
|
$this->name = $name;
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</para>
|
|
|
|
<para>
|
|
Revisar si hay alguna diferencia entre
|
|
<varname>$bar1</varname> que ha sido creada usando el
|
|
operador de copiado <literal>=</literal> y
|
|
<varname>$bar2</varname> que ha sido creada utilzando el
|
|
operador de referencia <literal>=&</literal>...
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$bar1 = new Foo('set in constructor');
|
|
$bar1->echoName();
|
|
$globalref[0]->echoName();
|
|
|
|
/* salida:
|
|
set in constructor
|
|
set in constructor
|
|
set in constructor */
|
|
|
|
$bar2 =& new Foo('set in constructor');
|
|
$bar2->echoName();
|
|
$globalref[1]->echoName();
|
|
|
|
/* salida:
|
|
set in constructor
|
|
set in constructor
|
|
set in constructor */
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</para>
|
|
<para>
|
|
Aparentemente no hay diferencia, pero de hecho existe una
|
|
muy significativa: <varname>$bar1</varname> y
|
|
<varname>$globalref[0]</varname> _NO_ están referenciadas, NO
|
|
son la misma variable. Esto es porque "<literal>new</literal>" no
|
|
devuelve una referencia por omisión, en vez de ello devuelve una copia.
|
|
<note>
|
|
<simpara>
|
|
No hay pérdida de desempeño (ya que PHP 4 y superiores utilizan conteo
|
|
de referencias) al devolver copias en vez de referencias. Al contrario
|
|
la mayor parte del tiempo es mejor simplemente trabajar con copias
|
|
en vez de referencias, porque crear referencias toma algo de
|
|
tiempo mientras que crear copias virtualmente no toma tiempo (a menos que una
|
|
de ellas sea un objeto o matriz grande y una de ellas es cambiada
|
|
y la(las) otra(s) subsecuentemente también, entonces sería sabio
|
|
usar referencias para cambiarlas a todas concurrentemente).
|
|
</simpara>
|
|
</note>
|
|
Para probar lo que esta escrito aquí arriba observar el código siguiente.
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
// ahora se cambiará el nombre. ¿Qué esperabas?
|
|
// se podría esperar que $bar1 y $globalref[0] ambas cambien sus nombres...
|
|
$bar1->setName('set from outside');
|
|
|
|
// como se mencionó antes este no es el caso.
|
|
$bar1->echoName();
|
|
$globalref[0]->echoName();
|
|
|
|
/* salida:
|
|
set from outside
|
|
set in constructor */
|
|
|
|
// veamos que es diferente con $bar2 y $globalref[1]
|
|
$bar2->setName('set from outside');
|
|
|
|
// afortunadamente no sólo son iguales, son la misma variable
|
|
// así $bar2->name y $globalref[1]->name son las mismas también
|
|
$bar2->echoName();
|
|
$globalref[1]->echoName();
|
|
|
|
/* salida:
|
|
set from outside
|
|
set from outside */
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</para>
|
|
<para>
|
|
Otro ejemplo final, intente comprenderlo.
|
|
|
|
<informalexample>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class A {
|
|
function A($i) {
|
|
$this->value = $i;
|
|
// intente entender por qué no es necesaria una referencia aquí
|
|
$this->b = new B($this);
|
|
}
|
|
|
|
function createRef() {
|
|
$this->c = new B($this);
|
|
}
|
|
|
|
function echoValue() {
|
|
echo "<br />","class ",get_class($this),': ',$this->value;
|
|
}
|
|
}
|
|
|
|
|
|
class B {
|
|
function B(&$a) {
|
|
$this->a = &$a;
|
|
}
|
|
|
|
function echoValue() {
|
|
echo "<br />","class ",get_class($this),': ',$this->a->value;
|
|
}
|
|
}
|
|
|
|
// intente entender por qué usar una copia simple aquí terminaría
|
|
// en un resultado no deseado en la línea marcada con *
|
|
$a =& new A(10);
|
|
$a->createRef();
|
|
|
|
$a->echoValue();
|
|
$a->b->echoValue();
|
|
$a->c->echoValue();
|
|
|
|
$a->value = 11;
|
|
|
|
$a->echoValue();
|
|
$a->b->echoValue(); // *
|
|
$a->c->echoValue();
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
class A: 10
|
|
class B: 10
|
|
class B: 10
|
|
class A: 11
|
|
class B: 11
|
|
class B: 11
|
|
]]>
|
|
</screen>
|
|
</informalexample>
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="oop4.object-comparison">
|
|
<title>Comparando objects</title>
|
|
<para>
|
|
En PHP 4, los objetos se comparan de una manera muy sencilla, entiéndase: Dos instancias
|
|
de objeto son iguales si tienen los mismos atributos y valores, y son
|
|
instancias de la misma clase. Reglas semejantes se aplican cuando se comparan dos
|
|
objetos utilizando el operador de identidad (<literal>===</literal>).
|
|
</para>
|
|
<para>
|
|
Si se fuese a ejecutar el código en el ejemplo siguiente:
|
|
<example>
|
|
<title>Ejemplo de comparación de objetos en PHP 4</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
function bool2str($bool) {
|
|
if ($bool === false) {
|
|
return 'FALSE';
|
|
} else {
|
|
return 'TRUE';
|
|
}
|
|
}
|
|
|
|
function compareObjects(&$o1, &$o2) {
|
|
echo 'o1 == o2 : '.bool2str($o1 == $o2)."\n";
|
|
echo 'o1 != o2 : '.bool2str($o1 != $o2)."\n";
|
|
echo 'o1 === o2 : '.bool2str($o1 === $o2)."\n";
|
|
echo 'o1 !== o2 : '.bool2str($o1 !== $o2)."\n";
|
|
}
|
|
|
|
class Flag {
|
|
var $flag;
|
|
|
|
function Flag($flag=true) {
|
|
$this->flag = $flag;
|
|
}
|
|
}
|
|
|
|
class SwitchableFlag extends Flag {
|
|
|
|
function turnOn() {
|
|
$this->flag = true;
|
|
}
|
|
|
|
function turnOff() {
|
|
$this->flag = false;
|
|
}
|
|
}
|
|
|
|
$o = new Flag();
|
|
$p = new Flag(false);
|
|
$q = new Flag();
|
|
|
|
$r = new SwitchableFlag();
|
|
|
|
echo "Compare instances created with the same parameters\n";
|
|
compareObjects($o, $q);
|
|
|
|
echo "\nCompare instances created with different parameters\n";
|
|
compareObjects($o, $p);
|
|
|
|
echo "\nCompare an instance of a parent class with one from a subclass\n";
|
|
compareObjects($o, $r);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
Compare instances created with the same parameters
|
|
o1 == o2 : TRUE
|
|
o1 != o2 : FALSE
|
|
o1 === o2 : TRUE
|
|
o1 !== o2 : FALSE
|
|
|
|
Compare instances created with different parameters
|
|
o1 == o2 : FALSE
|
|
o1 != o2 : TRUE
|
|
o1 === o2 : FALSE
|
|
o1 !== o2 : TRUE
|
|
|
|
Compare an instance of a parent class with one from a subclass
|
|
o1 == o2 : FALSE
|
|
o1 != o2 : TRUE
|
|
o1 === o2 : FALSE
|
|
o1 !== o2 : TRUE
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
El cual es el resultado que se esperaría obtener dadas las reglas de comparación
|
|
anteriores. Unicamente instancias con los mismos valores para sus atributos
|
|
y de la misma clase son consideradas como iguales e idénticas.
|
|
</para>
|
|
<para>
|
|
Aun en casos en donde se tiene composición de objetos, se aplican las mismas reglas
|
|
de comparación. En el ejemplo siguiente se crea una clase contenedora que almacena
|
|
una matriz asociativa de objetos <classname>Flag</classname>.
|
|
<example>
|
|
<title>Comparaciones de objetos compuestos en PHP 4</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class FlagSet {
|
|
var $set;
|
|
|
|
function FlagSet($flagArr = array()) {
|
|
$this->set = $flagArr;
|
|
}
|
|
|
|
function addFlag($name, $flag) {
|
|
$this->set[$name] = $flag;
|
|
}
|
|
|
|
function removeFlag($name) {
|
|
if (array_key_exists($name, $this->set)) {
|
|
unset($this->set[$name]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
$u = new FlagSet();
|
|
$u->addFlag('flag1', $o);
|
|
$u->addFlag('flag2', $p);
|
|
$v = new FlagSet(array('flag1'=>$q, 'flag2'=>$p));
|
|
$w = new FlagSet(array('flag1'=>$q));
|
|
|
|
echo "\nComposite objects u(o,p) and v(q,p)\n";
|
|
compareObjects($u, $v);
|
|
|
|
echo "\nu(o,p) and w(q)\n";
|
|
compareObjects($u, $w);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
Composite objects u(o,p) and v(q,p)
|
|
o1 == o2 : TRUE
|
|
o1 != o2 : FALSE
|
|
o1 === o2 : TRUE
|
|
o1 !== o2 : FALSE
|
|
|
|
u(o,p) and w(q)
|
|
o1 == o2 : FALSE
|
|
o1 != o2 : TRUE
|
|
o1 === o2 : FALSE
|
|
o1 !== o2 : TRUE
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
</para>
|
|
</sect1>
|
|
</appendix>
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|