2010-07-28 15 views
18

me gusta la forma en C# en el que puede escribir un método de extensión, y luego hacer cosas como esta:C# métodos de extensión similares en PHP?

string ourString = "hello"; 
ourString.MyExtension("another"); 

o incluso

"hello".MyExtention("another"); 

¿Hay una manera de tener un comportamiento similar en PHP?

+5

Las cadenas no son objetos en PHP. –

+2

Lo siento, ¿qué es un "método de extensión"? ¿Cómo difiere de otros métodos? – Artefacto

+1

@Artefacto: El '" hola ".MyExtention (" otro ");' no lo explicó? – caesay

Respuesta

9

Usted podría si Reimplementado todas sus cadenas como objetos.

class MyString { 
    ... 
    function foo() { ... } 
} 

$str = new MyString('Bar'); 
$str->foo('baz'); 

Pero realmente no quisiera hacer esto. PHP simplemente no es un lenguaje orientado a objetos en su núcleo, las cadenas son solo tipos primitivos y no tienen ningún método.

La sintaxis 'Bar'->foo('baz') es imposible de lograr en PHP sin extender el motor central (que tampoco es algo en lo que desea entrar, al menos no para este propósito :)).


También hay nada acerca de la ampliación de la funcionalidad de un objeto que hace que sea intrínsecamente mejor que simplemente escribir una nueva función que acepta un primitivo. En otras palabras, el PHP equivalente a

"hello".MyExtention("another"); 

es

my_extension("hello", "another"); 

Para todos tiene la intención y los propósitos que tiene la misma funcionalidad, simplemente sintaxis diferente.

+10

Infierno sí, a los diseñadores de PHP les gusta mucho el enfoque 'my_extension()' y aquí está [** el precioso resultado **] (http://www.php.net/manual/en/indexes.functions.php):) – cvsguimaraes

+0

Buena suerte encadenando muchos métodos. En C#, podría hacer "prueba" .ToUpper(). ToLower.Split(). Join(). Trim(). Ahora ni siquiera me molestaré en escribir el código PHP para hacer eso. – Phiter

+0

@Phiter Esa es solo la diferencia entre los lenguajes OO y los idiomas que no son OO. Principalmente en idiomas que no son OO, lo escribe "de adentro hacia afuera" ('trim (join (...))') en lugar de hacerlo encadenado. No estoy comentando qué es mejor o peor, así son las cosas. – deceze

4

Desde PHP 5.4 hay traits que se puede utilizar como métodos de extensión.

Ejemplo:

<?php 
trait HelloWorld { 
    public function sayHelloWorld() { 
     echo 'Hello World'; 
    } 
} 

class MyHelloWorld { 
    use HelloWorld; 
    public function sayExclamationMark() { 
     echo '!'; 
    } 
} 

$o = new MyHelloWorld(); 
$o->sayHelloWorld(); 
$o->sayExclamationMark(); 
?> 

resultado:

Hello World! 

Una vez que se incluye un rasgo de una clase, le llaman por ejemplo, con un nombre Extension, puede añadir cualquier método que desee y localizar ellos en otro lugar. Luego, en el que el ejemplo use Extension se convierte simplemente en la decoración de una sola vez para las clases extensibles.

+13

Los rasgos no son lo mismo que los métodos de extensión porque todavía requieren modificar la clase original. – zzzzBov

+0

Sí, pero una vez Incluye un rasgo, vamos a llamarlo con el nombre "Extensión". Puede agregar los métodos que desee y ubicarlos en otro lugar. Luego, el "uso de TraitName" se convierte en una decoración única. –

4

Alejándose de la emisión de primitivas no a objetos en PHP, cuando se trata de clases reales en PHP, si su entorno es sano, es probable que se puede decorar una clase dado a un género de § extensión emular métodos.

Dada una interfaz y la implementación:

interface FooInterface { 
    function sayHello(); 
    function sayWorld(); 
} 

class Foo implements FooInterface { 
    public function sayHello() { 
     echo 'Hello'; 
    } 
    public function sayWorld() { 
     echo 'World'; 
    } 
} 

En tanto que las dependencias en Foo son en realidad depende de la interfaz FooInterface (esto es lo que quiero decir sobre cuerdo) se puede implementar FooInterface a sí mismo como un envoltorio a Foo, desvía las llamadas a Foo cuando sea necesario, y añadir métodos adicionales como sea necesario:

class DecoratedFoo implements FooInterface { 
    private $foo; 
    public function __construct(FooInterface $foo) { 
     $this->foo = $foo; 
    } 
    public function sayHello() { 
     $this->foo->sayHello(); 
    } 
    public function sayWorld() { 
     $this->foo->sayWorld(); 
    } 
    public function sayDanke() { 
     echo 'Danke'; 
    } 
    public function sayShoen() { 
     echo 'Shoen'; 
    } 
} 

Los métodos sayHello() y sayWorld() están actualizados en el objeto Foo, sin embargo, hemos agregado sayDanke() y sayShoen().

la siguiente:

function acceptsFooInterface(FooInterface $foo) { 
    $foo->sayHello(); 
    $foo->sayWorld(); 
} 

$foo = new Foo(); 
acceptsFooInterface($foo); 

funciona como se espera, produciendo HelloWorld; pero también lo hace esto:

$decoratedFoo = new DecoratedFoo($foo); 
acceptsFooInterface($decoratedFoo); 

$decoratedFoo->sayDanke(); 
$decoratedFoo->sayShoen(); 

que se traduce en HelloWorldDankeShoen.

Este es un uso limitado del potencial en el patrón de decorador; Puede modificar el comportamiento de los métodos implementados, o simplemente no reenviarlos (; sin embargo, queremos mantener el comportamiento previsto por la definición de clase original en este ejemplo)

¿Es esto uno a uno? solución para implementar métodos de extensión (según C#) en PHP? No, definitivamente no; pero la extensibilidad permitida por este enfoque ayudará a resolver problemas de manera más flexible.


§ pensé en elaboro basado en una conversación de chat del sujeto: nunca se va a replicar (no hoy, y probablemente no Tommorow) en PHP, sin embargo, la clave en mi respuesta es patrones de diseño. Brindan la oportunidad de portar estrategias de un idioma a otro cuando no necesariamente (típicamente, o alguna vez) características del puerto.

+1

+1 para programar interfaces. – rdlowrey

2

Tengo otra implementación para eso en PHP> = 5.3.0 y es como un Decorador que explicó Northborn Design.

Todo lo que necesitamos es una API para crear extensiones y un decorador para aplicar las extensiones.

Tenemos que recordar que en los métodos de extensión C# no rompen la encapsulación del objeto extendido, y no modifican el objeto (no hay punto para eso, sino que la implementación del método sería más efectiva). Y los métodos de extensiones son pura estática y que reciben la instancia del objeto, como en el siguiente ejemplo (C#, from MSDN):

public static int WordCount(this String str) 
{ 
    return str.Split(new char[] { ' ', '.', '?' }, 
        StringSplitOptions.RemoveEmptyEntries).Length; 
} 

Por supuesto que no tienen objetos de cadena en PHP, pero para todos los demás objetos podemos crear decoradores genéricos para ese tipo de hechicería.

deja ver mi aplicación para eso:

La API:

<?php 

namespace Pattern\Extension; 

/** 
* API for extension methods in PHP (like C#). 
*/ 
class Extension 
{ 
    /** 
    * Apply extension to an instance. 
    * 
    * @param object $instance 
    * @return \Pattern\Extension\ExtensionWrapper 
    */ 
    public function __invoke($instance) 
    { 
     return Extension::apply($instance); 
    } 

    /** 
    * Apply extension to an instance. 
    * 
    * @param object $instance 
    * @return \Pattern\Extension\ExtensionWrapper 
    */ 
    public static function apply($instance) 
    { 
     return new ExtensionWrapper($instance, \get_called_class()); 
    } 

    /** 
    * @param mixed $instance 
    * @return boolean 
    */ 
    public static function isExtensible($instance) 
    { 
     return ($instance instanceof Extensible); 
    } 
} 
?> 

el decorador:

<?php 

namespace Pattern\Extension; 

/** 
* Demarcate decorators that resolve the extension. 
*/ 
interface Extensible 
{ 
    /** 
    * Verify the instance of the holded object. 
    * 
    * @param string $className 
    * @return bool true if the instance is of the type $className, false otherwise. 
    */ 
    public function holdsInstanceOf($className); 

    /** 
    * Returns the wrapped object. 
    * If the wrapped object is a Extensible the returns the unwrap of it and so on. 
    * 
    * @return mixed 
    */ 
    public function unwrap(); 

    /** 
    * Magic method for the extension methods. 
    * 
    * @param string $name 
    * @param array $args 
    * @return mixed 
    */ 
    public function __call($name, array $args); 
} 
?> 

Y la implementación genérica:

<?php 

namespace Pattern\Extension; 

/** 
* Generic version for the Extensible Interface. 
*/ 
final class ExtensionWrapper implements Extensible 
{ 
    /** 
    * @var mixed 
    */ 
    private $that; 

    /** 
    * @var Extension 
    */ 
    private $extension; 

    /** 
    * @param object $instance 
    * @param string | Extension $extensionClass 
    * @throws \InvalidArgumentException 
    */ 
    public function __construct($instance, $extensionClass) 
    { 
     if (!\is_object($instance)) { 
      throw new \InvalidArgumentException('ExtensionWrapper works only with objects.'); 
     } 

     $this->that = $instance; 
     $this->extension = $extensionClass; 
    } 

    /** 
    * {@inheritDoc} 
    * @see \Pattern\Extension\Extensible::__call() 
    */ 
    public function __call($name, array $args) 
    { 
     $call = null; 
     if (\method_exists($this->extension, '_'.$name)) { 
      // this is for abstract default interface implementation 
      \array_unshift($args, $this->unwrap()); 
      $call = array($this->extension, '_'.$name); 
     } elseif (\method_exists($this->extension, $name)) { 
      // this is for real implementations 
      \array_unshift($args, $this->unwrap()); 
      $call = array($this->extension, $name); 
     } else { 
      // this is for real call on object 
      $call = array($this->that, $name); 
     } 
     return \call_user_func_array($call, $args); 
    } 

    /** 
    * {@inheritDoc} 
    * @see \Pattern\Extension\Extensible::unwrap() 
    */ 
    public function unwrap() 
    { 
     return (Extension::isExtensible($this->that) ? $this->that->unwrap() : $this->that); 
    } 

    /** 
    * {@inheritDoc} 
    * @see \Pattern\Extension\Extensible::holdsInstanceof() 
    */ 
    public function holdsInstanceOf($className) 
    { 
     return \is_a($this->unwrap(), $className); 
    } 
} 
?> 

El uso:

Suponga existe una tercera clase del partido:

class ThirdPartyHello 
{ 
    public function sayHello() 
    { 
     return "Hello"; 
    } 
} 

Crear su extensión:

use Pattern\Extension\Extension; 

class HelloWorldExtension extends Extension 
{ 
    public static function sayHelloWorld(ThirdPartyHello $that) 
    { 
     return $that->sayHello().' World!'; 
    } 
} 

Plus: Para los amantes de la interfaz, crear una extensión Resumen:

<?php 
interface HelloInterfaceExtension 
{ 
    public function sayHelloFromInterface(); 
} 
?> 
<?php 
use Pattern\Extension\Extension; 

abstract class AbstractHelloExtension extends Extension implements HelloInterfaceExtension 
{ 
    public static function _sayHelloFromInterface(ThirdPartyOrLegacyClass $that) 
    { 
     return $that->sayHello(). ' from Hello Interface'; 
    } 
} 
?> 

Entonces usarlo :

//////////////////////////// 
// You can hide this snippet in a Dependency Injection method 

$thatClass = new ThirdPartyHello(); 

/** @var ThirdPartyHello|HelloWorldExtension $extension */ 
$extension = HelloWorldExtension::apply($thatClass); 

////////////////////////////////////////// 

$extension->sayHello(); // returns 'Hello' 
$extension->sayHelloWorld(); // returns 'Hello World!' 

////////////////////////////////////////// 
// Abstract extension 

$thatClass = new ThirdPartyHello(); 

/** @var ThirdPartyHello|HelloInterfaceExtension $extension */ 
$extension = AbstractHelloExtension::apply($instance); 

$extension->sayHello(); // returns 'Hello' 
$extension->sayHelloFromInterface(); // returns 'Hello from Hello Interface' 

Pros: forma

  • muy similar de C métodos # de extensión en PHP;
  • No se puede probar directamente una instancia de extensión como una instancia del objeto extendido, pero esto es bueno porque es más seguro porque podemos tener lugares donde la instancia de esa clase no se extiende;
  • Como la intención de un marco para mejorar la agilidad del equipo, tiene que escribir menos;
  • El uso de la extensión parece ser parte del objeto, pero está decorado (tal vez sea interesante para los equipos desarrollarse rápidamente, pero revise en el futuro la implementación de ese objeto extendido si se trata de un legado);
  • Puede utilizar el método estático de la extensión directamente para mejorar el rendimiento pero al hacerlo pierde la capacidad de simular partes de su código (DI está muy indicado).

Contras:

  • La extensión deben ser declarados al objeto, Su es no sólo como una importación como en C#, se debe "decorar" la instancia deseado para dar la extensión a la misma.
  • No se puede probar directamente una instancia de extensión como una instancia del objeto extendido, más código para probar con la API;
  • Inconvenientes de rendimiento debido al uso de métodos mágicos (pero cuando se necesita rendimiento, cambiamos el lenguaje, recreamos el núcleo, usamos marcos minimalistas, ensamblador si es necesario);

Aquí un Gist para que Api: https://gist.github.com/tennaito/9ab4331a4b837f836ccdee78ba58dff8

Cuestiones relacionadas