即使您可以在 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