2

我希望提供一个选项列表作为函数的参数。

理想场景:命名参数

如果 PHP 有命名参数,它会这样做:

function setOptions($title, $url, $public = true, $placeholder = "type here...") {
   ...
}

setOptions($title = "Hello World", $url = "example.com", $placeholder = "hi"); 

不幸的是 PHP 没有命名参数(请告诉我 PHP7 是否计划有一些作为注释)

其他人都在使用的解决方案:关联数组

我见过的大多数 PHP 脚本都使用另一种数组方法,如下所示:

function setOptions($options) {
   ...
}

setOptions(array(
   'title' => "Hello World",
   'url' => "example.com",
   'placeholder' => "hi"
));

关联数组方法的缺点

虽然这很好用,但也有以下缺点:

  • 用户不会从自动完成中受益(需要很长时间才能编写)
  • 用户很容易出现拼写错误
  • 不知道有哪些选项可用,因此可能会经常返回文档

有没有更好的办法?

有没有更好的方法可以解决这些问题(在当前的 PHP 或 PHP7 中,甚至可能是 hacklang(?))。

4

5 回答 5

3

在 Hack 中,您可以使用Shapes。形状为关联数组定义了一个结构,以便可以自动完成(取决于 IDE 支持)并且类型检查器会发现拼写错误。

例如,您的示例可以修改为:

function setOptions(shape(
  'title' => string,
  'url' => string,
  'public' => ?bool,
  'placeholder' => ?string,
) $options) {
  $title = $options['title'];
  $url = $options['url'];
  $public = Shapes::idx($options, 'public', true);
  $placeholder = Shapes::idx($options, 'placeholder', 'type here...');
  ...
}

setOptions(shape(
  'title' => 'Hello World',
  'url' => 'example.com',
  'placeholder' => 'hi',
));

这标志着titleandurl既是必需的选项,public也是placeholder可选的(形状中的所有可为空的类型都被认为是可选的)。Shapes::idx然后用于获取提供的值,或者如果未传入值,则获取默认值(第三个参数)。

于 2015-09-06T05:14:05.780 回答
2

解决方案:使用流利的设置器

我发现这个问题的一个潜在解决方案是使用类和流利的设置器,如下所示:

class PostOptions {

    protected
      $title,
      $url,
      $public = TRUE,
      $placeholder = "type here..."; //Default Values can be set here

    static function getInstance(): PostOptions {
        return new self();
    }

    public function setTitle($title) {
        $this->title = $title;
        return $this;
    }

    public function setUrl($url) {
        $this->url = $url;
        return $this;
    }

    public function setPublic($public) {
        $this->public = $public;
        return $this;
    }

    public function setPlaceholder($placeholder) {
        $this->placeholder = $placeholder;
        return $this;
    }

}

然后,您可以像这样发送选项:

function setOptions(PostOptions $postOptions) {
    //...
}

setOptions(
  PostOptions::getInstance()
             ->setTitle("Hello World")
             ->setUrl("example.com")
             ->setPlaceholder("hi")
);

做起来快!(这个看起来很长)

虽然这可能看起来很长,但实际上可以使用 IDE 工具非常快速地实现。

例如,在 InteliJ 或 PHPStorm 中,只需键入ALT+INS> Select setters> 选择要设置的字段并选中复选框fluent setters> clickOK

为什么选择流利的二传手?为什么不把所有领域都公开?

使用公共字段要慢很多。这是因为 fluent setter 可以使用链式方法,而公共字段方式必须这样编写:

$options = new PostOptions(); 
$options->title = "hello"; 
$options->placeholder = "..."; 
$options->url "..."
setOptions($options); 

与建议的解决方案相比,这需要更多的输入

为什么这样更好?

  • 在 IDE 中使用自动完成比数组方法更快
  • 不太可能出现拼写错误(感谢自动完成)
  • 易于查看可用的选项(再次感谢自动完成)
  • 可以使用 PHPDoc 为各个字段提供单独的文档
  • 可以更轻松地使用嵌套选项,例如,如果您有一个选项列表,并且该选项也有更多选项列表
  • 其他 OOP 优势,例如继承和抽象类

这种方法快多少?

我为 Wordpress 标签数组实现了一个快速类:https ://codex.wordpress.org/Function_Reference/register_post_type

我发现为每个值设置一个属性(在第二台显示器上你旁边有文档),由于自动完成,流畅的 setter 方法比数组方法快大约25% !但是,如果文档不在您身边,我预计这种方法将远远超过 25%,因为发现选项要快得多!

欢迎替代方法

于 2015-09-05T23:23:51.993 回答
1

来自数组的声明

这就是我通常声明我的类结构的方式。唯一的缺点是编写时间较长,但它允许可选参数、默认值等。

public static $defaults = array(
    'user_id' => null,
    'username' => null,
    'avatar' => null,
    'email' => null,
    'description' => null,
);

public function __construct(array $args = array()) {
    $this->dbc = Database::connection();
    $defaults = self::$defaults;
    $args = array_merge($defaults, $args);

    //Assign the object properites
    $this->user_id = (is_numeric($args['user_id'])) ? $args['user_id'] : null;
    $this->username = $args['username'];
    $this->avatar = AVATAR_DIR . $args['avatar'];
    $this->email = $args['email'];
    $this->description = $args['description'];
}

这样,您可以声明一个类似 的对象$x = new User(),并且它可以正常工作。假设您只从SQL语句中选择了几列。您可以使keysin 与您选择public static $defaults的名称相同columns,这样实例化您的对象,您可以轻松地执行以下操作:

$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$object = new User($row);

负责在array_merge他们提供的参数中包含您不需要的任何无关密钥。如果您需要更改选项,您可以__construct()使用默认数组array_merge以相同的方式声明它们并捕获参数并模仿命名参数和默认值(如在 Python 中)

于 2015-09-06T00:15:45.487 回答
0

使用语法https ://github.com/topclaudy/php-syntactic

你可以这样做:

function foo($a = 1, $b = 2, $c = 3, $d = 4){
    return $a * $b * $c * $d;
}

并用你想要的参数调用它:

//Call with argument b only
echo s('foo')->in('b', 5)->out(); //Outputs 60

//Call with argument a and argument at index/position 1 (b),
echo s('foo')->in('a', 7)->in(1, 5)->out(); //Outputs 420

//Call with argument c only through dynamic method
echo s('foo')->c(9)->out(); //Outputs 72
于 2016-06-20T17:00:07.187 回答
0

如果你有那么多参数,我会考虑创建一个对象,你将传递给类而不是 n 个参数,并且每个参数都是一个字段。在构造函数中输入所需的参数,这就是干净的解决方案。

于 2018-05-17T13:27:33.120 回答