¿Instanciación de clase “en línea” en PHP? (Por facilidad de encadenamiento de métodos)

Una expresión idiomática comúnmente utilizada en lenguajes OO como Python y Ruby es crear instancias de un objeto y métodos de encadenamiento que devuelven una referencia al objeto en sí, como por ejemplo:

s = User.new.login.get_db_data.get_session_data 

En PHP, es posible replicar este comportamiento de la siguiente manera:

 $u = new User(); $s = $u->login()->get_db_data()->get_session_data(); 

Intentando los siguientes resultados en el syntax error, unexpected T_OBJECT_OPERATOR :

 $s = new User()->login()->get_db_data()->get_session_data(); 

Parece que esto podría lograrse utilizando métodos estáticos, que es probablemente lo que terminaré haciendo, pero quería comprobar el lazyweb: ¿Existe realmente una manera limpia y simple de crear instancias de clases PHP “en línea” (como se muestra en el fragmento de arriba) para este propósito?

Si decido usar métodos estáticos, ¿ es demasiado mágico que el método estático de una clase devuelva una instanciación de la clase en sí misma ? (¿Escribir mi propio constructor-que-no-es-un-constructor efectivamente?) Se siente un poco sucio, pero si no hay demasiados efectos secundarios de miedo, podría hacerlo.

Supongo que también podría crear una instancia previa de UserFactory con un método get_user (), pero tengo curiosidad acerca de las soluciones a lo que pregunté anteriormente.

Todas estas soluciones propuestas complican tu código para doblar PHP y lograr cierta sutileza sintáctica. Querer que PHP sea algo que no es (como bueno) es el camino a la locura.

Yo solo usaría:

 $u = new User(); $s = $u->login()->get_db_data()->get_session_data(); 

Es claro, relativamente conciso y no implica magia negra que pueda introducir errores.

Y, por supuesto, siempre puedes moverte a Ruby o Python. Cambiará tu vida.

  • Y sí, soy duro con PHP. Lo uso todos los dias. Lo he estado usando por años. La realidad es que se ha acrecentado , en lugar de haber sido diseñado y se nota.
  "world"); } } function Create($name) { return new $name(); } $s = Create("User")->Login()->GetDbData()->GetSession(); var_dump($s); ?> 

Esta es una posible solución 🙂 Por supuesto, debes elegir un mejor nombre para la función …

O si no te importa un poco por encima:

 newInstanceArgs(array_slice(func_get_args(), 1)); } $s = CreateArgs("User", "hi")->Login()->GetDbData()->GetSession(); var_dump($s); ?> 

La única manera de obtener algo similar es con un método estático de fábrica o simple. Por ejemplo:

 class User { //... /** * * @return User */ public static function instance() { $args = func_get_args(); $class = new ReflectionClass(__CLASS__); return $class->newInstanceArgs($args); } //... } 

Utiliza la API de reflexión PHP5 para crear una nueva instancia (usando cualquier argumento enviado a :: instancia ()) y la devuelve, lo que le permite hacer el encadenamiento:

 $s = User::instance()->login()->get_db_data()->get_session_data(); 

Por cierto, ese código es lo suficientemente flexible como para que lo único que deba cambiar al copiar ese método estático sea @return, el comentario de PHPDoc.


Si quiere optimizar prematuramente su código como nuestro amigo Nelson, puede reemplazar el contenido de User :: instance () por:

 return new self(); 

No hay ninguna razón para unir un hack (para solucionar el problema de la syntax) y el código de creación de objeto.

Como una nueva expresión no puede usarse como el miembro izquierdo de su operador de objetos, pero sí puede hacerlo una expresión de llamada de función, solo necesita envolver su objeto en una llamada a una función que devuelve su propio argumento:

 function hack($obj) { return $obj; } $mail = hack(new Zend_Mail()) -> setBodyText('This is the text of the mail.') -> setFrom('somebody@example.com', 'Some Sender') -> addTo('somebody_else@example.com', 'Some Recipient') -> setSubject('TestSubject'); 

Un atajo simple para

 $Obj = new ClassName(); $result = $Obj->memberFunction(); 

es

 $result = (new ClassName())->memberFunction(); 
 login()->get_db_data()->get_session_data();