Ver índice
Clases y objetos (III)

        Ocultar índice  

   Índice de contenidos
   Instalación en Windows
   Instalación en Ubuntu
   Servidores seguros
   Páginas dinámicas
   Sintaxis básica
   Operaciones
   Arrays
   Formatos de presentación
   Operadores
   Bucles
   Extraer y ord. información
   Funciones
   Ficheros externos
   Imágenes dinámicas
   Gestión de directorios
   Cookies y sesiones
   Clases y objetos
   Ficheros en formato PDF
   Bases de datos MySQL
   PHP y XML
   PDO - Bases SQLite / MySQL
   MySQL a traves de misqli
   Algo de JavaScript y AJAX


Sobrecargas de propiedades

Al hablar de la privacidad decíamos que las propiedades privadas y protegidas únicamente son accesibles desde funciones incluidas en la propia clase (o en una clase extendida si es protected). Eso significaría que opciones del tipo print $objeto->propiedad o $objeto->propiedad=valor solo funcionarían en el caso de que la propiedad tuviera condición de pública.

En esas situaciones la solución para acceder a las propiedades no visibles (private y protected) sería agregar dentro de la propia clase algo como esto:

......
public function lee_privado($propiedad){
        return $this->propiedad
} ......

que haría de intermediario y que mediante algo similar a: print $objeto->lee_privado(propiedad) nos permitiría conocer el valor de tal propiedad. Mediante procedimientos similares podríamos modificar valores, eliminar propiedades o comprobar su existencia.

Para simplificar este proceso disponemos de palabras reservadas que permiten que determinadas funciones se ejecuten, si están incluidas en la propia clase, de forma automática. Enumeraremos el comportamiento de algunas de ellas.

public function __get($propiedad) {
        return $this->$propiedad;
}

cuando incluimos una función idéntica a esta en una clase (es imprescindible que tenga la condición de public y que el nombre de la función sea __get) podemos despreocuparnos de si una propiedad es public o tiene la condición de private, protected. En el momento que intentamos conocer el valor de una propiedad del objeto escribiendo: $objeto->propiedad se ejecutará (si es necesario) de forma automática esa función y nos devolverá el valor requerido.

public function __set($propiedad,$valor) {
        $this->$propiedad=$valor;
}

Una función idéntica a esta (también es imprescindible la condición de public y que el nombre de la función sea __set) nos permite asignar un valor a cualquier propiedad del objeto independientemente de que su condición sea public, private o protected. En el momento que intentamos asignar un valor escribiendo: $objeto->propiedad=valor se ejecutará (si es necesario) de forma automática esa función y asignará a la propiedad el valor indicado.

public function __unset($propiedad) {
        unset ($this->$propiedad);
}

Eliminará la propiedad cuando escribamos: unset ($objeto->propiedad) ejecutándose de forma automática, cuando sea necesario, la función __unset.

public function __isset($propiedad) {
        return isset($this->$propiedad);
}

Devolverá TRUE o FALSE (dependiendo de que exista o no exista la propiedad) cuando escribamos: isset($objeto->propiedad).

En el ejemplo puedes ver el funcionamiento de estos métodos incluso en los casos en los que los valores de las propiedades sean arrays u objetos.

<?php
/* esta clase tiene como una finalidad la creación de un objeto para pruebas
lo único que hace cuando es instanciada es asignar valores a las propiedades
publicas a y b.*/
class ObjetoAuxiliar{
      public $a;
      public $b;
      function __construct($a=7,$b=14){
             $this->a=$a;
             $this->b=$b;
    }
}
/* esta nueva clase es la que usaremos para nuestras pruebas */
class Pruebas {
  protected $propiedad1="13";
  protected $matriz=array('Uno','Dos','Tres');
  protected $objeto;
  function __construct(){
    $this->objeto=new ObjetoAuxiliar;
  }
/* esta funcion mágica __get nos permitirá ver desde fuera de la propia clase los contenidos
    de sus propiedades protegidas y privadas */
 public function __get($variable){
   return $this->$variable;
 }
 /* esta funcion mágica __get nos permitirá, desde fuera de la propia clase, asignar valores
    a sus propiedades protegidas y privadas */
 public function __set($propiedad,$valor){
   $this->$propiedad=$valor;
 }
 /* esta funcion mágica __unset nos permitirá, desde fuera de la propia clase,
  eliminar propiedades protegidas y privadas */
 public function __unset($propiedad){
   print "Eliminando: ".$propiedad."<br />";
   unset($this->$propiedad);
 }
 /* esta funcion mágica __isset nos permitirá, desde fuera de la propia clase,
 comprobar si está definidan  propiedades protegidas y privadas */
 public function __isset($propiedad){
   return isset($this->$propiedad);
 }
}
/* creamos un nuevo objeto de la clase pruebas */
$miObjeto=new Pruebas();
/* la propiedad1 es protected. Eso significa que solo se visualizaría
desde una función de la propia clase o de una de sus extendidas.
Cuando al invocarla resulta no ser accesible PHP comprueba si está definido
el método __get en la clase. Si lo está lo ejecuta de forma automática
considerando la propiedad1 como parámetro de la función __get. Dado que esa
función pertenece a la propia clase si puede leer la propiedad y devolver
el resultado */

print $miObjeto->propiedad1."<br>";
/* comprobamos que __get funciona tanto cuando la propiedad contiene un array
como cuando se trata de un objeto. */
print_r($miObjeto->matriz)."<br>";
print_r($miObjeto->objeto)."<br>";
/* comprobemos ahora la función mágica __set. Se comporta identica a la anterior
con la diferencia de que ahora recoge como parámetros el nombre de la propiedad y el valor
que se pretende asignar */
$miObjeto->propiedad1=7456;
print $miObjeto->propiedad1."<br>";
$miObjeto->matriz=array(7,4,5,6);
 print_r($miObjeto->matriz)."<br>";
$miObjeto->objeto= new ObjetoAuxiliar(25,987);
  print_r($miObjeto->objeto)."<br>";
/* comprobamos que se pueden borrar propiedades con los tres tipos de valores */
unset($miObjeto->propiedad1);
unset($miObjeto->matriz);
unset($miObjeto->objeto);
/* comprobamos la existencia de la propiedad utilizando de forma idéntica a los casos
anteriores la función mágica __isset */
if (isset($miObjeto->propiedad1)){
    print "La propiedad1 si existe";
}else{
    print "La propiedad1 no existe";
}
?>
ejemplo319.php

  ¡Cuidado!  

Presta atención a la sintaxis de las funciones que hemos descrito. Observa que hemos utilizado $this->$propiedad cuando lo habitual en las funciones sería $this->propiedad. Si omitimos el $ nos dará error.

Clases y métodos abstractos

Quizá nos sea de utilidad detenernos en un caso de la vida cotidiana. Pensemos en una ITV. Los inspectores han de seguir un protocolo que consiste en verificar el funcionamiento de: carroceria, gases, bocina, luces, suspensión, frenos, dirección, etcétera, de cada vehículo y, posteriormente, emitir y firmar un documento acreditativo de los resultados de tal inspección. Este protocolo contiene (o puede contener) instrucciones precisas para emitir y firmar los documentos (se sigue el mismo procedimiento sea cual sea el tipo de vehículo). Nos encontramos ante dos situaciones distintas: especificaciones concretas (métodos) para la burocracia y especificaciones abstractas para la parte técnica. Nadie nos ha dicho aún como inspeccionar una dirección o unas luces. Este protocolo inicial podríamos entenderlo como una clase abstracta con métodos abstractos.

El inspector necesita con carácter imprescindible métodos concretos para los diferentes tipos de vehículos. Si se trata de una motocicleta el método de inspección será radicalmente distinto al de un turismo y el de este será también distinto al de un autobús. Así pues, habrá de contar como métodos de comprobación de cada uno de los elementos para cada una de las clases de vehículo. En nuestro argot diríamos que son necesarias clases extendidas: motocicleta, turismo y autobús, y además, cada una de esas clases ha de disponer de métodos para todas y cada una de las verificaciones: carroceria, gases, etcétera. Dicho de otra forma: es imprescindible redefinir cada uno de los métodos abstractos para cada una de las clases extendidas. Algo muy similar ocurre con la Programación Orientada a Objetos.

Clases y métodos abstractos

Para definir un método como la condición de abstracto hemos de utilizar la siguiente sintaxis:

abstract protected function nombre_del_metodo ()

dónde la palabra reservada abstract especifica su condición de abstracto, protected establece el carácter protegido del mismo y además, con caracter imprescidible, la function nombre_del_metodo () ha de estar declarada pero no definida (observa que faltan las {} y el habitual contenido de ellas que realmente es lo que define el método o función).

Otra cuestión sumamente importante. Para que una clase pueda incluir un método abstracto también ella ha de tener la condición de abstracta. Es decir, no podemos definir un método como abstracto sin que la clase que lo contiene lo sea. Para definir como abstracta una clase hemos de hacer utilizar la siguiente sintaxis:

abstract class nombre_de_la_clase

una clase definida como abstracta puede no contener métodos abstractos y propiedades con cualquier tipo de visibilidad. Por el contrario, un método abstracto solo puede ser incluido en una clase abstracta.

Restricciones de las clases y métodos abstractos

El uso de clases abstractas requiere tener muy presentes las siguientes cuestiones:



<?php
/*creamos una clase abstracta en la que incluimos métodos
  tanto abstractos como no abstractos, variables y constantes.
  Los método abstractos solo se declaran pero no se define lo
  que deben hacer cuando sean invocados */
 abstract class Figuras{
        /*incluimos los métodos abstractos */
        abstract protected function calculaArea();
        abstract protected function calculaPerimetro();
        /* los restantes metodos, propiedades y constantes de la clase */
        const PI=3.141592;
        protected $tipo;
        public function Imprime($a,$b){
            print "La ".$this->tipo." cuyos radios son: ". $a." y ".$b."<br />";
            print "El area es:".$this->calculaArea($a,$b)."<br />";
            print "El perímetro es:".$this->calculaPerimetro($a,$b)."<br />";
        }
}

/* las clases abstractas no pueden ser instanciadas para crear objetos. Por tanto
   es imprescindible una clase extendida */

class Redondas extends Figuras{
        public function calculaArea($radio1=0,$radio2=''){
                    if ($radio2=='') $radio2=$radio1;
                    return self::PI*$radio1*$radio2;
        }
        public function calculaPerimetro($radio1=0,$radio2=''){
          if ($radio2==''){
                  $radio2=$radio1;
                  return self::PI*2*$radio1;
          }else{
                return self::PI*(3*($radio1+$radio2)-sqrt((3*$radio1+$radio2)*($radio1+3*$radio2)));
           }

       }
}
/* instanciamos un objeto de la clase extendida (la abstracta no lo permitiria)*/
 $circulo= new Redondas();
/* desde este objeto ya se puede llamar a métodos de cualquiera de las clases */
 $circulo->Imprime (5,5);
?>
ejemplo320.php

Interfaces

Las interfaces tienen un cierto paralelismo con las clases y métodos abstractos pero ni su sintaxis ni su comportamiento son iguales. Podemos decir que una interface es un declaración o lista de métodos publicos no definidos que han de incluirse (y definirse) con carácter obligatorio en todas las clases que implementen esa interface.

Desde una perspectiva utilitarista podríamos entenderlas como una lista de obligaciones y tareas obligatorias (métodos que han de incluirse en una clase) que imposibilitará el olvido ya que de no realizarse todas (omitir algún método establecido en la interface) las instancias de la clase producirán un error.

En una interface solo pueden incluirse métodos (funciones) con visibilidad public y constantes.

Sintaxis de las interfaces

Para declarar una interface es necesario una sintaxis como esta:

interface nombre_de_la_interface {
        public function nombre_de_una_funcion (argumento1, argumento2,...)
        public function nombre_de_otra_funcion (argumento1, argumento2,...)
        .......

        const nombre = valor (opcional)
}

Para utilizar interface es necesario una sintaxis como esta:

class nombre_de_la_clase implements nombre_de_una_interface, nombre_de_otra_interface {

        /*TODOS los métodos de nombre_de_una_interface */

        public function nombre_de_una_funcion (argumento1, argumento2,...){
        .......(código del método) .........
        ................
        }
        public function nombre_de_otra_funcion (argumento1, argumento2,...){
        .......(código del método) .........
        ................
        }

        /* TODOS los métodos de nombre_de_otra_interface */
        ................
        ................
}

Restricciones de las interfaces y sus clases



<?php
/* creamos  una interface que solo puede contener
   métodos publicos no definidos y constantes */
interface Figuras{
        public function calculaArea();
        public function calculaPerimetro();
        const PI=3.141592;
        }
/* una nueva interface que en este caso no incluye ninguna constante */
interface Cuerpos{
        public function calculaVolumen();
        }

/* definimos una clase que implementa las dos interfaces (Figuras y Cuerpos)
  ello le obliga a definir todas las funciones declaradas en ambas clases
  Si hubiera dos funciones con el mismo nombre se produciría una ambiguedad que
  generaría un error */
class Redondas implements Figuras, Cuerpos{
    public $tipo;
    public function calculaArea($radio1=0,$radio2=''){
            if ($radio2=='') $radio2=$radio1;
            return $this::PI*$radio1*$radio2;
    }

    public function  calculaPerimetro($radio1=0,$radio2=''){
            if ($radio2==''){
                      $radio2=$radio1;
                      return self::PI*2*$radio1;
            }else{
              return self::PI*(3*($radio1+$radio2)-sqrt((3*$radio1+$radio2)*($radio1+3*$radio2)));
            }
    }

    public function  calculaVolumen($radio1=0,$radio2=''){
            if ($radio2=='') {
                    $radio2=$radio1;
                    return 4/3*self::PI*pow($radio1,3);
            }else{
                    return 4/3*self::PI*$radio1*pow($radio2,2);
            }

    }
}

/* instanciamos objetos pertenecientes a la clase de la forma habitual. La constante PI
   definida en el interface es asumida como propia por la clase */
$circulo= new Redondas();
print "El area de la figura es: ". $circulo->calculaArea(5,5) ."<br>";
print "El perimetro de la figura es: ". $circulo->calculaPerimetro(5,5) ."<br>";
print "El volumen del cuerpo es: ". $circulo->calculaVolumen(5,5) ."<br>";
?>
ejemplo321.php

Métodos que manejan objetos o arrays

Los métodos definidos en una clase pueden manejar objetos de la propia clase o de otra. Esta última posibilidad requiere disponer de un objeto de cada una de las dos clases y, además, que en una de ellas se incluyan métodos con esta sintaxis:

function nombre_del_metodo ( Otra_Clase, $objeto_cualquier_nombre , $argumento1, $argumento2,...) {
        $objeto_cualquier_nombre->propiedad
        $objeto_cualquier_nombre->metodo ( $argumento1, $argumento2,...)
}

La utilización de estos métodos requiere haber creado previamente un objeto de cada una de las clases que vamos a llamar $Objeto_Base y $Objeto_Externo. Con estas premisas la ejecución de un método requeriría esta sintaxis:

$Objeto_Base->nombre_del_metodo ($Objeto_Externo, argumento1, argumento2)

los nombres corresponden a los anteriormente descritos y los argumentos incluidos en la llamada se corresponden con los eventualmente requeridos por el método referenciado en la que hemos llamado Otra_Clase tal como puedes ver en este ejemplo.

También es posible pasar arrays a los métodos de una clase y manejar sus contenidos mediante funciones incluidas en el método. Para ello se requiere la siguiente sintaxis:

function nombre_del_metodo ( array, $variable) {
        .........
        funciones php que manejan el array $variable
        .........
}

dónde array es una palabra reservada que especifica la condición de tal de lo recogido por $variable. El método debe ser invocado de la forma siguiente:

$objeto -> nombre_del_metodo( array('elemento1','elemento2',...) )
o, como es lógico
$matriz = array('elemento1','elemento2',...);
$objeto -> nombre_del_metodo ( $matriz )

<?php

/* definimos una clase que llamaremos externa (será la  auxiliar) que incluye métodos aritméticos.
La funcion suma puede recibir valores como argumentos y, en caso de recibirnos
modificará las propiedades de sumando1 y sumando 2 */

class Externa {
    public $sumando1=1;
    public $sumando2=2;

   function Suma($a='',$b=''){
     if ($a !='') $this->sumando1=$a;
     if ($b !='') $this->sumando2=$b;
     return $this->sumando1+$this->sumando2;
   }

   function Resta(){
       return $this->sumando1-$this->sumando2;
   }

   function Multiplica(){
        return $this->sumando1*$this->sumando2;
   }

    function Divide(){
        return $this->sumando1/$this->sumando2;
   }
}

/* definiremos ahora una nueva clase (que será la Base de nuestro ejemplo)
   con la peculiaridad de que sus métodos hacen referencia a otra clase
   y a objetos de esa otra clase.
   En todos los métodos de esta clase (con una excepción que comentaremos)
   se incluyen como parámetros el nombre de la clase auxiliar (Externa),
   una variable que recibirá los objetos de la clase Externa y los eventuales
   argumentos requeridos por la función que en el caso de la suma son dos */

class Base {

    function adicion (Externa $exterior, $a='',$b='') {
    /* este método pasará el objeto exterior (recibido en la llamada)
       junto con los valores de las variables a y b.
       Devolverá el resultado de ejecutar el método Suma (de la clase externa)
       sobre el objeto exterior también de la clase externa */
            return $exterior->Suma($a,$b);
    }
    function sustracion (Externa $exterior) {
        return $exterior->Resta();
}
    function multiplicacion (Externa $exterior) {
        return $exterior->Multiplica();
    }

    function division (Externa $exterior) {
        return $exterior->Divide();
    }

    function cambia_a (Externa $exterior, $a) {
        return $exterior->sumando1=$a;
    }
    /* en este caso el objeto recibido en la petición es una instancia
       de la propia clase Base y se le aplicará un método (neperiano) definido
       en esta misma clase. */
    function lee_logaritmo (Base $exterior,$a) {
        return $exterior->neperiano($a);
    }
    /* el método neperiano que será instanciados por anterior */
    function neperiano($a){
        return log($a);
    }
    /* esta función recibe como argumento un array que será
    recogido por la función en la variable la_matriz y tratado como tal.
    Requiere obligatoriamente la palabra reservada array en la misma posición
    que en las funciones anteriores incluyen el nombre de la clase
    a la que pertenece el objeto. Ahora podría decirse que el array la_matriz es un objeto
    de la clase array */
    function lee_matriz (array $la_matriz){
                print "El array tiene: ".sizeof ($la_matriz)." elementos<br>";
                foreach ($la_matriz as $indice=>$valor){
                        print $indice." * ".$valor."<br>";
                }
    }


}

/*manejo de las clases anteriores */

/* Creamos un objeto de cada de las dos clases */
$Objeto_Externo = new Externa;
$Objeto_Base = new Base;
/* el objeto base ejecuta sus métodos incluyendo como argumentos el identificado del objeto externo
junto con los eventuales argumentos*/

print "La suma es: ". $Objeto_Base->adicion($Objeto_Externo)."<br />";

print "La suma con parámetros es: ". $Objeto_Base->adicion($Objeto_Externo,25,89)."<br />";

print "Los valores pasados a la función producirán una modificación ";
print "en los valores de las propiedades sumando1 y sumando 2 del Objeto_externo. ";
print " Esas nuevos valores se mantendrán en tanto no vuelvan a ser modificados.<br>";
print "La suma con un parámetro es: ". $Objeto_Base->adicion($Objeto_Externo,749)."<br />";

print "La suma con un parámetro es: ". $Objeto_Base->adicion($Objeto_Externo,'',125)."<br />";

print "La diferencia es: ".$Objeto_Base->sustracion($Objeto_Externo)."<br />";

print "El producto es: ".$Objeto_Base->multiplicacion($Objeto_Externo)."<br />";

print "El cociente es: ".$Objeto_Base->division($Objeto_Externo)."<br />";

print "El nuevo valor de a: ".$Objeto_Base->cambia_a($Objeto_Externo,1013)."<br />";

print "La suma es: ". $Objeto_Base->adicion($Objeto_Externo)."<br />";

print "El logaritmo neperiano de 10 es: ". $Objeto_Base->lee_logaritmo($Objeto_Base,10)."<br />";

print "Probando un array<br> ";
print  $Objeto_Base->lee_matriz(array(13,27,128,945,'pepe'))."<br>";

?>
ejemplo322.php