2010-06-09 11 views
5

Estoy trabajando en un framework que intento escribir lo más fuerte posible. (Estoy trabajando con PHP y tomando algunas de las ideas que me gustan de C# y tratando de utilizarlas dentro de este marco.) Estoy creando una clase Collection que es una colección de entidades/objetos de dominio. Es un poco modelado después del objeto List<T> en .Net.PHP 'instanceof' falla con constante de clase

Me encontré con un obstáculo que me impide escribir esta clase. Si tengo UserCollection, solo debería permitir objetos User en él. Si tengo una PostCollection, solo debería permitir objetos Post.

Todas las colecciones en este marco deben tener ciertas funciones básicas, como agregar, eliminar, iterar. He creado una interfaz, pero descubrió que no podía hacer lo siguiente:

interface ICollection { public function add($obj) } 
class PostCollection implements ICollection { public function add(Post $obj) {} } 

Esto rompió es el cumplimiento de la interfaz. Pero no puedo tener la interfaz fuertemente tipada porque todas las colecciones son del mismo tipo. Así que he intentado lo siguiente:

interface ICollection { public function add($obj) } 
abstract class Collection implements ICollection { const type = 'null'; } 
class PostCollection extends Collection { 
const type = 'Post'; 
public function add($obj) { 
    if(!($obj instanceof self::type)) { 
    throw new UhOhException(); 
    } 
} 
} 

Cuando intento ejecutar este código, me sale syntax error, unexpected T_STRING, expecting T_VARIABLE or '$' en la cuenta de instanceof. Investiga un poco sobre el tema y parece que la raíz de la causa es que $obj instanceof self es válido para probar contra la clase. Parece que PHP no procesa toda la declaración constante self::type en la expresión. La adición de paréntesis alrededor de la variable self::type arrojó un error con respecto a un inesperado '('.

Una solución obvia es no hacer la variable type una constante. La expresión $obj instanceof $this->type funciona bien (si $type se declara como una variable, por supuesto)

Espero que haya una forma de evitar eso, ya que me gustaría definir el valor como una constante para evitar cualquier cambio posible en la variable más adelante. Cualquier idea sobre cómo puedo lograr esto, o ¿He llevado PHP a su límite en este sentido? ¿Hay alguna forma de "escapar" o encapsular self::this para que PHP no muera al procesarlo?

ACTUALIZACIÓN Basado en los comentarios a continuación, pensé en algo para probar - ¡funciona el siguiente código! ¿Puede alguien pensar en 1) una razón para no hacer esto, 2) una razón por la cual esto no funcionará en última instancia, o 3) una mejor manera de lograrlo?

interface ICollection { public function add($obj) } 
abstract class Collection { const type = null; protected $type = self::type; } 
class PostCollection extends Collection { 
const type = 'Post'; 
public function add($obj) { 
    if(!($obj instanceof $this->type)) { 
    throw new UhOhException(); 
    } 
} 
} 

ACTUALIZACIÓN # 2: Después de poner el código anterior en la producción, resulta que no funciona. No tengo idea de cómo funcionó cuando lo probé, pero no funciona en absoluto. Estoy atrapado con el uso de una variable protected, creo.

+0

creo que respondí 1 o 2 - a pesar de que t La variable está 'protegida', aún se puede cambiar en el código. No hay seguridad real al hacerlo de esta manera que la variable '$ type' no se cambiará accidentalmente. Lo que esperaba es un seguro contra eso. –

+0

Si entiendo correctamente, le está dando a la instancia de operación una cadena ($ this-> type) cuando debería ser solo un nombre de clase (vea http://php.net/manual/en/language.operators.type.php) . Creo que deberías usar 'is_a' ya que espera una cadena que es lo que es $ this-> type. – gacrux

+0

No es una afirmación verdadera: consulte el Ejemplo # 5 en la página a la que se ha vinculado. –

Respuesta

0

Esto también funciona correctamente, utilizando un estático:

<?php 

interface ICollection { 
    public function add($obj); 
} 
abstract class Collection implements ICollection { 
    static protected $_type = 'null'; 
} 
class PostCollection extends Collection { 
static protected $_type = 'Post'; 
public function add($obj) { 
    if(!($obj instanceof self::$_type)) { 
    throw new UhOhException(); 
    } 
} 
} 


class Post {} 

$coll = new PostCollection(); 
$coll->add(new Post()); 

Y, de hecho, es probable que desee para definir el método de add() en la clase Collection todos modos, lo que significa que tendrá que utilizar get_class() de moverse por algunos rareza con self::type o incluso self::$_type siempre con ganas de volver la clase base Collection de todos modos, así que esto probablemente funcionaría:

abstract class Collection implements ICollection { 
    const type = 'null'; 
    public function add($obj) { 
    $c = get_class($this); 
    $type = $c::type; 
    if(!($obj instanceof $type)) { 
    throw new UhOhException(); 
    } 
    } 
} 

class PostCollection extends Collection { 
const type = 'Post'; 
} 
class Post {} 

$coll = new PostCollection(); 
$coll->add(new Post()); 
1

Estoy sorprendido por este comportamiento, así, pero esto debería funcionar:

$type = self::type; 
if(!($obj instanceof $type)) { 
    throw new UhOhException(); 
} 

EDIT:

que podría hacer

abstract class Collection { 
    const type = null; 
    protected $type = self::type; 
} 
class PostCollection extends Collection { 
    const type = "User"; 
    public function add($obj) { 
     if(!($obj instanceof $this->type)) { 
      throw new WhateverException(); 
     } 
    } 
} 

pero eres encender el complicometer . Esto tiene la sobrecarga adicional de crear una variable instancia $type para cada instancia de PostCollection (y no, no puede simplemente agregar static a la propiedad $type).

+0

Eso me dio una idea: actualicé mi publicación. ¡Gracias! –

+0

@Nathan Loding Tu nuevo código me dio 'Aviso de PHP: Propiedad indefinida: PostCollection :: $ type'. – Artefacto

+0

¡Hice un error tipográfico!La definición de la clase debe ser 'class Colección de extensiones de PostCollection' - Lo actualizaré, buena captura. –

3

Otra solución es hacer:

$type = self::type; 
if (!($obj instanceof $type)) 

Simplemente porque es una constante no significa que no se puede poner en una variable por un momento para satisfacer el analizador.

+0

Eso me dio una idea: actualicé mi publicación. ¡Gracias! –

+0

o ¿qué pasa con el paréntesis? '! ($ obj instanceof (self :: type))' – seanmonstar

+0

@seanmonstar - esa expresión exacta me arrojó un error en PHP. ¿Funcionó para ti? Obtuve un error con respecto a "inesperado" ("'. –

-2

Pruebe utilizar la función is_a de PHP en lugar de instanceof, ya que espera una cadena como nombre de clase.

+0

Esta no es una afirmación verdadera. Mire el Ejemplo # 5 (" Using instanceof con otras variables ") en http://php.net/manual/en/language.operators.type.php. –

+0

Ah cierto, mi mal, gracias por su comentario. – gacrux

2

Estoy creando una clase Collection que es una colección de entidades/objetos de dominio. Es un poco modelado después del objeto List<T> en .Net.

En general, no es una buena idea escribir un idioma en otro idioma. No necesita Colecciones en PHP.

Si va a continuar por ese camino, tal vez debería considerar el uso de herramientas proporcionadas por PHP. Por ejemplo, está ArrayObject, del que puede heredar y anular los métodos necesarios para asegurarse de que solo ingresen las cosas ingresadas correctamente en la matriz. ArrayObjects se puede usar en cualquier parte de PHP donde se pueda usar una matriz normal. Además, los bits y las piezas subyacentes ya se han escrito para usted.

The rest of the Standard PHP Library puede ser de su interés, la clase SplObjectStorage en particular.

+0

Excepto que dije que es" un poco modelado después ". necesita un contenedor para múltiples objetos del mismo tipo y puede agregar, eliminar, encontrar y iterar a través de ellos, y está en una aplicación de PHP. Si se tratara de una aplicación .Net, usaría un objeto 'List '. En PHP, tengo el placer de crear mi propia variación sobre eso. –

+0

Es posible que desee leer mi publicación más allá del primer párrafo, ya que paso a explicar cómo PHP ya puede darle las herramientas que necesita para crear este comportamiento sin implementar una construcción desde un idioma muy diferente. :) – Charles

+2

Las opciones SplObjectStorage y ArrayObject no son exactamente lo que quiero. No veo ningún motivo para no intentar crear una construcción de un idioma a otro, a menos que existan razones técnicas que impidan su realización. En este caso, la creación de una clase de colección que imita el objeto List en .Net es muy posible dentro de PHP, es la parte fuertemente tipada que no lo es. La alegría de PHP sobre .Net es que puedes construir estos objetos tú mismo. No estoy escribiendo un idioma en otro. Estoy explorando posibilidades en un idioma basado en la experiencia con otro idioma. –

1

debe ser readly como esto

función pública add (ICollection $ obj) {

}

ahora si intenta agregar un objeto a la función de añadir que no es una instancia de Icollection (eso es un ejemplo) entonces fallará, incluso antes de que puedas verificar usando el instanceof.

+0

Eso es una buena idea, en este caso, será un objeto Entity, pero una buena base. –

Cuestiones relacionadas