tightenco/overload

此包已废弃,不再维护。没有建议的替代包。

PHP的方法重载

v0.1.3 2020-09-08 16:12 UTC

This package is auto-updated.

Last update: 2024-03-14 14:08:03 UTC


README

Overload logo

PHP的方法重载

Run tests Latest Version on Packagist Total Downloads

注意:这是一个测试版发布。这是Adam的原始代码,几乎完全一样,以及他的文档;如果很多人感兴趣,我们作为社区可以找到它的限制、边缘以及它需要成长的地方。**请注意,尽管所有的赞誉都归功于Adam编写了这段代码,但维护的责任并不在他身上。Tighten将尽最大努力维护它,但如果这个项目有任何进展,那将是因为社区的支持。这是一个测试版发布,不保证没有错误或漏洞。**

安装

您可以通过composer安装此包

composer require tightenco/overload

用法

此包提供了一种声明式的方式来支持同一方法的多重签名。

基本示例

假设我们有一个Ticket类,它有一个holdUntil方法,允许我们通过传递DateTime对象将此票据挂起,直到某个日期和时间

class Ticket extends Model
{
    // ...

    public function holdUntil(DateTime $dateTime)
    {
        $this->update(['hold_until' => $dateTime]);
    }

    // ...
}

...但现在你决定如果它能接受一个格式良好的日期字符串那就更方便了。

通常你会这样做

class Ticket extends Model
{
    // ...

    public function holdUntil($dateTime)
    {
        if (is_string($dateTime)) {
            $dateTime = Carbon::parse($dateTime);
        }

        $this->update(['hold_until' => $dateTime]);
    }

    // ...
}

可重载特性允许你以声明式的方式而不是条件检查参数进行模式匹配

class Ticket extends Model
{
    use Overloadable;

    // ...

    public function holdUntil(...$args)
    {
        return $this->overload($args, [
            function (string $dateTime) {
               $this->update(['hold_until' => Carbon::parse($dateTime)]);
            },
            function (DateTime $dateTime) {
               $this->update(['hold_until' => $dateTime]);
            },
        ]);
    }

    // ...
}

如果你想要避免这种重复,甚至可以这样做疯狂的递归

class Ticket extends Model
{
    use Overloadable;

    // ...

    public function holdUntil(...$args)
    {
        return $this->overload($args, [
            function (string $dateTime) {
               $this->holdUntil(Carbon::parse($dateTime));
            },
            function (DateTime $dateTime) {
               $this->update(['hold_until' => $dateTime]);
            },
        ]);
    }

    // ...
}

更酷的示例

你可能认为

"Uhhh bro,这看起来更像是更多的代码。"

是的,因为那个示例很无聊。这个示例会更有趣一些。

我一直希望Laravel的validate控制器助手可以接受一个闭包作为最后一个参数,这样如果验证失败,我可以返回任何我想要的HTTP响应。

validate方法的签名需要很多参数,我不想传递很多空数组,例如

public function store()
{
    //                             Super grim! 😭
    //                                ⬇️  ⬇️
    $this->validate($request, $rules, [], [], function ($errors) {
        return response()->json([
            'someOtherInfo' => 'toInclude',
            'errors' => $errors
        ], 422);
    });
}

我希望能这样做

public function store()
{
    $this->validate($request, $rules, function ($errors) {
        return response()->json([
            'someOtherInfo' => 'toInclude',
            'errors' => $errors
        ], 422);
    });
}

...并且它能神奇地工作,知道我明显不关心$messages$customAttributes参数,但你想想,如果在validate方法内部添加这些检查来做所有的参数计数和类型检查会多么糟糕?!

看看这个来自神灵的牛逼特性是如何工作的

trait ValidatesRequests
{
    // ...

    public function validate(...$args)
    {
        return $this->overload($args, [
            function ($request, $rules, Closure $callback) {
                return $this->validateRequest($request, $rules, [], [], $callback);
            },
            function ($request, $rules, $messages, Closure $callback) {
                return $this->validateRequest($request, $rules, $messages, [], $callback);
            },
            'validateRequest',
        ]);
    }

    // Move the real logic into a new private function...
    protected function validateRequest(Request $request, array $rules, array $messages = [], array $customAttributes = [], Closure $onErrorCallback = null)
    {
        $validator = $this->getValidationFactory()->make($request->all(), $rules, $messages, $customAttributes);

        if ($validator->fails()) {
            $this->throwValidationException($request, $validator, $onErrorCallback);
        }
    }

    // ...
}

匹配选项

可重载不仅适用于闭包;你可以做所有 sorts of 疯狂的事情!

查看测试中的这个示例

class SomeOverloadable
{
    use Overloadable;

    public function someMethod(...$args)
    {
        return $this->overload($args, [
            // Call this closure if two args are passed and the first is an int
            function (int $a, $b) {
                return 'From the Closure';
            },

            // Call this method if the args match the args of `methodA` (uses reflection)
            'methodA',

            // Call this method if the args match the args of `methodB` (uses reflection)
            'methodB',

            // Call methodC if exactly 2 arguments of any type are passed
            'methodC' => ['*', '*'],

            // Call methodD if 3 args are passed and the first is an array
            'methodD' => ['array', '*', '*'],

            // Call methodE if 3 args are passed and the last is a closure
            'methodE' => ['*', '*', Closure::class],
        ]);
    }

    private function methodA($arg1)
    {
        return 'Method A';
    }

    private function methodB(\DateTime $arg1, array $arg2, int $arg3)
    {
        return 'Method B';
    }

    private function methodC($arg1, $arg2)
    {
        return 'Method C';
    }

    private function methodD($arg1, $arg2, $arg3)
    {
        return 'Method D';
    }

    private function methodE($arg1, $arg2, $arg3)
    {
        return 'Method E';
    }
}

当你调用overload时,会按照指定的顺序匹配方法。

Adam原始工作的注释

我还在对这个东西进行黑客攻击,可能还有很多东西我忽视了。

例如,我刚刚想到,我没有真正考虑基于反射的检测功能应该如何处理可选参数,而且我脑子里甚至不知道它应该做什么 ¯\_(ツ)_/¯

无论如何,我认为这是一段很有趣的代码,而且我觉得我们甚至能够为它开发一个API,这真是太酷了。

即将进行的计划

  • 发布带有Adam原始代码的beta版
  • 发现已知的不足并记录为问题(例如,可选参数和即将到来的联合类型)
  • 修复^^
  • 盈利?🤣好吧,不是盈利。

测试

composer test

贡献

请参阅CONTRIBUTING以获取详细信息。

安全

如果您发现任何与安全相关的问题,请发送电子邮件至hello@tighten.co,而不是使用问题跟踪器。

鸣谢

方法重载的想法来自具有原生支持的其他语言。我(Matt)多次听说,包括从我的朋友Adam Wathan那里听到,所以当我决定最终着手做这件事时,我工作了几小时后停了下来,问Adam他是否见过有人做过这件事。结果……他见过。

他发给我一个gist链接。然而,Adam不想维护一个包,所以,在他的祝福下,我把这个项目独立出来,使其对全世界更易访问。

许可

MIT许可(MIT)。请参阅许可文件以获取更多信息。