upscale/stdlib-overloading

PHP7函数/方法参数重载

1.0.3 2020-02-23 05:02 UTC

This package is auto-updated.

Last update: 2024-09-18 06:52:20 UTC


README

此库引入了函数/方法重载——根据输入参数进行不同的实现。

特性

  • 通过参数类型进行重载
  • 通过参数数量进行重载
  • PHP7高效的原生类型检查
  • TypeError的有用原生错误信息
  • 轻量级:无OOP,无反射

安装

该库通过Composer作为依赖项进行安装

composer require upscale/stdlib-overloading

用法

语法

重载自定义函数/方法

<?php
declare(strict_types=1);

use function Upscale\Stdlib\Overloading\overload;

function func(...$args)
{
    return overload(
        function (int $num1, int $num2) {
            // ...
        },
        function (string $str1, string $str2) {
            // ...
        }
        // ...
    )(...$args);
}

可以声明任意数量的有效callable实现。顺序定义了评估优先级。

调用重载的函数

func(1, 2);
func('a', 'b');

示例

一个可以处理普通整数、任意长度的GMP整数和Money对象的算术计算器。

<?php
declare(strict_types=1);

use function Upscale\Stdlib\Overloading\overload;

class Money
{
    private $amount;
    private $currency;

    public function __construct(int $amount, string $currency)
    {
        $this->amount = $amount;
        $this->currency = $currency;
    }

    public function add(self $sum): self
    {
        if ($sum->currency != $this->currency) {
            throw new \InvalidArgumentException('Money currency mismatch');
        }
        return new self($this->amount + $sum->amount, $this->currency);
    }
}

class Calculator
{
    public function add(...$args)
    {
        return overload(
            function (int $num1, int $num2): int {
                return $num1 + $num2;
            },
            function (\GMP $num1, \GMP $num2): \GMP {
                return gmp_add($num1, $num2);
            },
            function (Money $sum1, Money $sum2): Money {
                return $sum1->add($sum2);
            }
        )(...$args);
    }
}


$calc = new Calculator();

$one = gmp_init(1);
$two = gmp_init(2);

$oneUsd = new Money(1, 'USD');
$twoUsd = new Money(2, 'USD');

print_r($calc->add(1, 2));
// 3

print_r($calc->add($one, $two));
// GMP Object([num] => 3)

print_r($calc->add($oneUsd, $twoUsd));
// Money Object([amount:Money:private] => 3 [currency:Money:private] => USD)

print_r($calc->add(1.25, 2));
// TypeError: Argument 1 passed to Calculator::{closure}() must be an instance of Money, float given

架构

重载机制利用了PHP7的原生类型系统,并依赖于声明严格的类型注解。它按声明的顺序遍历实现回调,并尝试用提供的参数调用每个回调。返回第一个兼容调用的结果,并丢弃后续的回调。

限制

PHP引擎允许传递给函数/方法的运行时参数多于其签名声明中计数的参数。多余的参数将被静默丢弃,不会触发任何可捕获的错误,甚至不包括ArgumentCountError。解决方案是在更具体的较长的签名之前声明更不具体的较短的签名,具有匹配的参数子集。

可选参数存在问题,因为它们仅在调用时与所需参数的较短签名兼容。考虑以下无法通过重新排序解决的模糊声明

overload(
    function (int $num1, int $num2) {
        // ... 
    },
    function (int $num1) {
        // ... 
    },
    function (int $num1, string $str2 = 'default') {
        // Unreachable because preceding declaration matches first and swallows excess arguments
        // ...
    }
)

解决方案是验证参数数量不超过声明

overload(
    function (int $num1, int $num2) {
        // ... 
    },
    function (int $num1) {
        if (func_num_args() > 1) {
            throw new \ArgumentCountError('Too many arguments provided');
        }
        // ... 
    },
    function (int $num1, string $str2 = 'default') {
        // Reachable with the optional argument passed, but still unreacable without it
        // ... 
    }
)

贡献

欢迎提交带有修复和改进的拉取请求!

许可证

版权所有 © Upscale Software。保留所有权利。

许可协议Apache License, Version 2.0