即使您可以在 PHP 中指定类型,但将所有内容都指定为数组类型,这不是很恶心吗?
你想让我做什么
PHP 中的数组非常有用。
然而,正如标题所说,我想用一个数组,但是因为我可以在PHP中指定类型,把所有东西都指定为数组类型不是很不舒服吗?是条目。
在最近的 PHP 中,不仅可以指定函数参数的类型,还可以指定返回值的类型,但是用于数据交换的数据类型是数组类型,这很容易处理。首先,存在的问题是数组类型不保证其内容的任何内容,并且开始时感觉用数组指定类型会很恶心。
如果可以轻松地将具有不同类型名称的数组(几乎)用作数组,那就太好了。
使用数组类型的示例,因为它不舒服
从包含在合适的 CSV 文件中的每个用户的分数数据中提取最佳分数数据的示例。
如果你说你应该输入 sqlite,你是对的。
<?php
/**
指定されたファイルを開いて、1行目をタイトル行として連想配列に読み込んで返却する
*/
function readCsvData(string $filename): array
{
// 実装は省略
return [
[
"id" => 1,
"name" => "Taru8",
"score" => 50,
],
[
"id" => 2,
"name" => "Taru9",
"score" => 100,
],
];
}
/**
scoreのmax()を出して、一致したレコードを一つ返却するものとする
input : $score_array: readCsvData()の戻り値を想定する
*/
function getHighScorePlayer(array $score_array): array
{
// 実装は省略
return [
"id" => 2,
"name" => "Taru9",
"score" => 100,
];
}
$score_array = readCsvData("./score_file.csv");
$high_score_player = getHighScorePlayer($score_array); // この関数の引数はreadCsvData()の戻り値である必要があるが、関数の定義上はこれを担保していない。
echo "ハイスコアは".$high_score_player["name"]."さんで".$high_score_player["score"]."点でした。\n";
解释什么让你不舒服
当 getHighScorePlayer() 的参数不是 readCsvData() 的返回值时,无法保证操作。
function readCsvData(string $filename): array
确保此函数的返回类型是数组。
function getHighScorePlayer(array $score_array): array
保证这个函数的参数是一个数组。
但是,代码不保证 getHighScorePlayer() 的参数必须是使用 readCsvData() 创建的数组,可能会导致不会发生的错误。
我想确保readCsvData()
的返回值类型和getHighScorePlayer()
的参数类型匹配。
这一次我敢让你忘记它。
然而,对于训练有素的 PHPer,众所周知,函数参数类型可能与预期不同。因为
“我不认为这很恶心。这很麻烦,你应该在代码审查中确保这一点。处理类型不是问题。”
来了答复。 (过去对Taruhachi的问卷调查。)
$score_array = readCsvData("./score_file.csv");
$high_score_player = getHighScorePlayer($score_array);
如果这部分的代码写得简单,那这段时间不会有任何变化是不言而喻的。和。
$high_score_player = getHighScorePlayer(readCsvData("./score_file.csv"));
如果你这样写,你不会得到任何错误。
和。
现在,让我们忘记这些言论。
还有很多更复杂的案例。
[这个几乎没问题] 几乎按原样易于使用的数组并给它一个类型名称。
尝试通过将数组插入继承 ArrayObject 的类来定义新类型
首先,只要进行这种改进,代码的前景就会改善(我认为)。
我很高兴这里创建的所有类都可以像数组一样处理。
伪代码在这里。
<?php
// ScoreArrayというほぼarray型のクラスを定義する
class ScoreArray extends ArrayObject {}
/**
指定されたファイルを開いて、1行目をタイトル行として連想配列に読み込んで返却する
*/
function readCsvData(string $filename): ScoreArray
{
// 実装は省略するが、 今までarrayのまま返答してたのを、 new ScoreArray()の中に入れた部分だけ変わっている
return new ScoreArray(
[
[
"id" => 1,
"name" => "Taru8",
"score" => 50,
],
[
"id" => 2,
"name" => "Taru9",
"score" => 100,
],
]
);
}
/**
scoreのmax()を出して、一致したレコードを一つ返却するものとする
input : $score_array: readCsvData()の戻り値(ScoreArray型)を想定する
*/
function getHighScorePlayer(ScoreArray $score_array): array
{
// 実装は省略
return [省略]:
}
$score_array = readCsvData("./score_file.csv");
$high_score_player = getHighScorePlayer($score_array); // 引数が型で指定されているので、間違いなくreadCsvData()の戻り値である
echo "ハイスコアは".$high_score_player["name"]."さんで".$high_score_player["score"]."点でした。\n";
评论
变化是
刚刚添加了class ScoreArray extends ArrayObject {}
并将 readCsvData 的数据交换更改为 ScoreArray 类型。
只因这一变化,
function readCsvData(string $filename): ScoreArray
确保此函数的返回类型是 ScoreArray。
function getHighScorePlayer(ScoreArray $score_array): array
确保此函数的参数是 ScoreArray。
ScoreArray 内部根本不检查类型的格式,但它继承了 ArrayObject 并且可以将数组原样转换为对象。
此外,它实现了所有可以充当数组的接口,因此它接受所有正常的数组操作。
* (Object)$array
也可以转换相同的类型,但在这种情况下它将是 Object 类型,您不能给出任何类型名称。
这样,可以消除关于类型规范的草率数组类型的规范。
再次,
function getHighScorePlayer(score_array $score_array): array{...}
也可以修改如下
class HighScorePlayer extends ArrayObject {}
function getHighScorePlayer(score_array $score_array): HighScorePlayer{...}
[Bonus 1] 如果您想更正确地保护内容的格式
添加class ScoreArray extends ArrayObject {}
给上面的类型名称已经确保了我想做的90%。
从代码中我们可以看出,这个类是作为readCsvData()的返回值生成的。
但是,也可以编写如下代码:
$score_array = new ScoreArray(["hoge" => "fuga"]);
无法使用getHighScorePlayer()
处理此 ScoreArray 对象。
在这种情况下,可以修改以验证在 ScoreArray 类构造函数中传递的参数,如下所示。
<?php
class ScoreArray extends ArrayObject {
public function __construct(array $array = [], int $flags = 0, string $iteratorClass = ArrayIterator::class){
self::__checkFormat($array);
parent::__construct($array, $flags, $iteratorClass);
}
// ここでは配列の各要素のindexがid, name, scoreで構成されている事をチェックしている
private function __checkFormat(array $array){
$expected_keys = ["id","name", "score"];
sort($expected_keys);
foreach($array as $player){
$keys = array_keys($player);
sort($keys);
if ($keys !== $expected_keys) {
throw new exception("Type mismatch. Each rows must have id, name, and score.");
}
}
}
如果你已经写到现在,
$score_array = new ScoreArray(["hoge" => "fuga"]);
__construct()
在尝试声明时被调用,__checkFormat()
在其中执行,所以抛出异常。
【补充2】如果想检查类型的内容,即使以后改了,也不是新建的时候
$score_array = new ScoreArray(["hoge" => "fuga"]);
如果在以下时间以外进行了以下更改,该怎么办
$score_array = readCsvData("./score_file.csv"); // ここでは正しいScoreArrayオブジェクトが返答される
unset($score_array[0]["name"]); // 各要素に必ずnameを含めたいので、エラーにしたい。
$score_array[0]["age"] = 20; // ScoreArrayにageはいらないので、エラーにしたい。
在这种情况下,您可以将其连接起来,以便每次调用相应的方法时再次执行格式检查,或者首先使其不可变。
这是一个使继承 ArrayObject 的对象不可变的示例
<?php
// ArrayObject の更新系の各メソッドを上書きすることで更新を禁止する。
trait immutableArrayObject{
// append()禁止
public function append($value){
throw new exception("This object is immutable");
}
// $arr[0]["name"] = "Taru10"; といった直接の上書きを禁止
public function offsetSet($index, $newval){
throw new exception("This object is immutable");
}
// unset($arr[0]["name"])などの操作を禁止
public function offsetUnset($key){
throw new exception("This object is immutable");
}
// 中身を全部差し替えるメソッドの禁止
public function exchangeArray($array){
throw new exception("This object is immutable");
}
};
class ScoreArray extends ArrayObject {
use immutableArrayObject;
public function __construct(array $array = [], int $flags = 0, string $iteratorClass = ArrayIterator::class){
self::__checkFormat($array);
parent::__construct($array, $flags, $iteratorClass);
}
// ここでは配列のindexがid, name, scoreで構成されている事をチェックしている
private function __checkFormat(array $array){
// 省略
}
}
结论
当与数组类型交换数据时,您可以通过将其插入到另一个继承 ArrayObject 的对象中来为其命名,因此您可以从类型检查中受益。
class ScoreArray extends ArrayObject {}
在最后
我写了一个备忘录,我尽力自己解决它,但如果有更好更聪明的方法,请告诉我。
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308622224.html