marcojetson/php-decorators

PHP的Python-like装饰器

dev-master 2015-06-18 21:33 UTC

This package is not auto-updated.

Last update: 2024-09-14 17:42:54 UTC


README

PHP的Python-like装饰器

Build status Test coverage

免责声明

请注意,这是一个概念验证,请在自己的风险下使用。

用法

使用以下格式之一通过@Decorate注解指定装饰器

  • 类名::方法名
  • 类名(必须实现__invoke)
  • 函数名

装饰器接受原始方法和上下文作为参数,并必须返回一个接受与原始方法相同参数的函数。

可以添加任意数量的装饰器,始终为下一个装饰器返回有效的可调用对象。

(几乎)真实示例

内存缓存

class InMemoryCacheDecorator
{
    private static $cache;

    public function __invoke($callable)
    {
        return function () use ($callable) {
            $args = func_get_args();
            $key = serialize([$callable, $args]);

            if (!isset(static::$cache[$key])) {
                static::$cache[$key] = call_user_func_array($callable, $args);
            }

            return static::$cache[$key];
        };
    }
}

class MyClass
{
    use Decorator\AnnotationDecoratorFactoryTrait;

    /**
     * @Decorate InMemoryCacheDecorator
     */
    public function myMethod($name)
    {
        sleep(1);
        return 'Hello ' . $name;
    }
}

$x = MyClass::factory(); // or use new AnnotationDecorator(new MyClass());

echo $x->myMethod('Marco'), PHP_EOL;
echo $x->myMethod('Marco'), ' (this time is cached)', PHP_EOL;
echo $x->myMethod('Marco'), ' (this time is cached)', PHP_EOL;

echo $x->myMethod('World'), PHP_EOL;
echo $x->myMethod('World'), ' (this time is cached)', PHP_EOL;
echo $x->myMethod('World'), ' (this time is cached)', PHP_EOL;

购物车促销

class Cart
{
    private $total = 100;

    /**
     * @Decorate halfVat
     * @Decorate fixedDiscount
     */
    public function calcTotal($vat)
    {
        return $this->total * (1 + $vat / 100);
    }
}

function halfVat($callable)
{
    return function ($vat) use ($callable) {
        return $callable($vat / 2);
    };
}

function fixedDiscount($callable)
{
    return function ($vat) use ($callable) {
        $total = $callable($vat) - 5;
        return $total < 0 ? 0 : $total;
    };
}

/** @var Cart $order */
$order = new Decorator\AnnotationDecorator(new Cart());
echo $order->calcTotal(21), PHP_EOL;

控制器要求

function require_http_post($callable, $context)
{
    return function () use ($callable, $context) {
        if ($context->getMethod() !== 'POST') {
            throw new \Exception('Not supported');
        }

        return call_user_func_array($callable, func_get_args());
    };
}

class AddressController
{
    use Decorator\AnnotationDecoratorFactoryTrait;

    private $method;

    public function __construct($method)
    {
        $this->method = $method;
    }

    public function getMethod()
    {
        return $this->method;
    }

    /**
     * @Decorate require_http_post
     */
    public function deleteAction($id)
    {
        // delete...
        return 'success';
    }
}

echo AddressController::factory('POST')->deleteAction(1), PHP_EOL;
echo AddressController::factory('GET')->deleteAction(1), PHP_EOL;

注意事项

  • 仅适用于方法
  • 不完全透明,需要使用工厂方法
  • 装饰的类没有参数类型提示