PHP:发送选项列表作为参数(替代命名参数/参数包)
PHP: Sending a list of options as an argument (alternative to named parameters/ argument bag)
我希望提供一个选项列表作为函数的参数。
理想场景:命名参数
如果 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(?))。
解决方案:使用流畅的 setter
我发现这个问题的一个潜在解决方案是使用 classes 和 fluent setter,如下所示:
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
> Select 您要设置的字段并选中 fluent setters
的复选框 > 单击 OK
为什么要流利的二传手?为什么不把所有字段都设为 public?
使用 public 字段要慢很多。这是因为 fluent setter 可以使用链式方法,而 public 字段方式必须这样写:
$options = new PostOptions();
$options->title = "hello";
$options->placeholder = "...";
$options->url "..."
setOptions($options);
与提议的解决方案相比,需要输入更多的内容
为什么这样更好?
- 在 IDE 中使用自动完成比数组方法更快
- 不太可能出现拼写错误(感谢自动完成)
- 轻松查看可用选项(再次感谢自动完成)
- 可以使用 PHPDoc 为各个字段提供单独的文档
- 可以更轻松地使用嵌套选项,例如如果您有一个选项列表,并且该选项还有更多选项列表
- 其他 OOP 优势,例如继承与抽象 类
这种方法要快多少?
我为 Wordpress 标签数组实现了一个快速 class:https://codex.wordpress.org/Function_Reference/register_post_type
我发现为每个值设置一个 属性(在第二台显示器上有您旁边的文档),流畅的 setter 方法大约快 25%比数组方法多亏了自动完成!但是,如果文档不在您身边,我预计这种方法 将远远超过 25%,因为 发现选项 会快得多!
欢迎使用其他方法
来自数组的声明
这就是我通常声明 class 结构的方式。唯一的缺点就是写的时间比较长,但是它允许可选参数,默认值等等
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
语句中选择了几列。您可以将 public static $defaults
中的 keys
设置为与您选择的 columns
相同的名称,这样来实例化您的对象,您可以轻松地做到:
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$object = new User($row);
array_merge
负责处理在它们提供的参数中不需要的任何无关键。如果您需要更改选项,您可以使用默认数组和 array_merge
以相同的方式声明它们以捕获参数并模拟命名参数和默认值(如 Python)
在 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',
));
这标记 title
和 url
都是必需的选项,而 public
和 placeholder
是可选的(形状中的所有可为 null 的类型都被认为是可选的)。 Shapes::idx
然后用于获取提供的值,如果未传入值,则使用默认值(第三个参数)。
和Syntactic: 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
如果你有那么多参数,我会考虑创建一个你将传递给 class 的对象,而不是 n 个参数,每个参数都是一个字段。在构造函数中输入所需的参数,这就是干净的解决方案。
我希望提供一个选项列表作为函数的参数。
理想场景:命名参数
如果 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(?))。
解决方案:使用流畅的 setter
我发现这个问题的一个潜在解决方案是使用 classes 和 fluent setter,如下所示:
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
> Select 您要设置的字段并选中 fluent setters
的复选框 > 单击 OK
为什么要流利的二传手?为什么不把所有字段都设为 public?
使用 public 字段要慢很多。这是因为 fluent setter 可以使用链式方法,而 public 字段方式必须这样写:
$options = new PostOptions();
$options->title = "hello";
$options->placeholder = "...";
$options->url "..."
setOptions($options);
与提议的解决方案相比,需要输入更多的内容
为什么这样更好?
- 在 IDE 中使用自动完成比数组方法更快
- 不太可能出现拼写错误(感谢自动完成)
- 轻松查看可用选项(再次感谢自动完成)
- 可以使用 PHPDoc 为各个字段提供单独的文档
- 可以更轻松地使用嵌套选项,例如如果您有一个选项列表,并且该选项还有更多选项列表
- 其他 OOP 优势,例如继承与抽象 类
这种方法要快多少?
我为 Wordpress 标签数组实现了一个快速 class:https://codex.wordpress.org/Function_Reference/register_post_type
我发现为每个值设置一个 属性(在第二台显示器上有您旁边的文档),流畅的 setter 方法大约快 25%比数组方法多亏了自动完成!但是,如果文档不在您身边,我预计这种方法 将远远超过 25%,因为 发现选项 会快得多!
欢迎使用其他方法
来自数组的声明
这就是我通常声明 class 结构的方式。唯一的缺点就是写的时间比较长,但是它允许可选参数,默认值等等
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
语句中选择了几列。您可以将 public static $defaults
中的 keys
设置为与您选择的 columns
相同的名称,这样来实例化您的对象,您可以轻松地做到:
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$object = new User($row);
array_merge
负责处理在它们提供的参数中不需要的任何无关键。如果您需要更改选项,您可以使用默认数组和 array_merge
以相同的方式声明它们以捕获参数并模拟命名参数和默认值(如 Python)
在 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',
));
这标记 title
和 url
都是必需的选项,而 public
和 placeholder
是可选的(形状中的所有可为 null 的类型都被认为是可选的)。 Shapes::idx
然后用于获取提供的值,如果未传入值,则使用默认值(第三个参数)。
和Syntactic: 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
如果你有那么多参数,我会考虑创建一个你将传递给 class 的对象,而不是 n 个参数,每个参数都是一个字段。在构造函数中输入所需的参数,这就是干净的解决方案。