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
Las cadenas no son objetos en PHP. –
Lo siento, ¿qué es un "método de extensión"? ¿Cómo difiere de otros métodos? – Artefacto
@Artefacto: El '" hola ".MyExtention (" otro ");' no lo explicó? – caesay