【问题标题】:Is this code is a good PHP practice for treat results from my query?这段代码是一个很好的 PHP 实践来处理我的查询结果吗?
【发布时间】:2015-04-28 14:54:08
【问题描述】:

我想知道这是否是一个好习惯,我的查询是否有很大的结果。

当我只有几行时,一切似乎都很好(即使这种方法不是一个好习惯),但当我有 10,000 行时,一切都变得疯狂(致命错误:允许的内存大小)。

abstract class System
{
    public static function arrayOfObjectTo($reference, $rows)
    {
        $response = array();
        foreach($rows as $row) {
            $response[] = new $reference($row)
        }
        return $response;
    }

    public static function arrayOfObjectToJson($rows)
    {
        $response = array();
        foreach ($rows as $object) {
            $response[] = $object->toObject();
        }
        return json_encode($response);
    }
}

class Report
{
    public function __construct($data)
    {
        $this->setName($data['name']);
        $this->setEmail($data['email']);
        $this->setPassword($data['password']);
    }

    public function toObject() 
    {
        $object = new stdClass();
        $object->name = $this->getName();
        $object->email = $this->getEmail();
        $object->password = $this->getPassword();
        return $object;
    }

    // getters and setters ...
}

class ReportModel
{
    public function getAll()
    {
        $this->query('SELECT * FROM ...');
        return System::arrayOfObjectTo('Report', $this->rows);
    }
}

class ReportController
{
    public function show()
    {
        $model = new ReportModel();
        return System::arrayOfObjectToJson($model->getAll());
    }
}

关注我的问题是处理我的报告对象、验证数据类型和其他内容的最佳方式。

最终的结果是向客户端显示所有经过处理和格式化的行。

[
    { name:"A", email:"a@.com", password:"1" },
    { name:"B", email:"b@.com", password:"2" },
    { name:"C", email:"c@.com", password:"3" }
    ...
]

这只是一个例子。有时我有一个需要显示许多列和行的 BIG 查询。

【问题讨论】:

  • “发疯”到底是什么样的?
  • 我怀疑你会用大结果集耗尽内存。考虑将查询分块以将结果流式传输到客户端。
  • 在您编辑之后,@ceejayoz 所说的 - 分块交付行,而不是一次。这意味着您将执行多个请求。另外,向客户端显示 10 000 行?什么人能够阅读 10 000 行?
  • 看起来您正在这里构建自己的框架。您首先应该做的是评估各种现有的development frameworks,例如Laravel,因为可能有一个适合您的需求和风格。您在密码处理方面犯了一些需要解决的严重错误。
  • 好的,太好了,这意味着您基本上是在构建某种 REST-ful 服务。一定要对响应进行分块,永远不要将表的全部内容粘贴到输出中。您可以为此提出一个小协议 - 例如,当“客户端”要求数据时,检查有多少行以及它们是否高于 X(例如,100)然后您可以发送一个响应说有 Y 行,客户端应该发送偏移量(您根据客户端的要求提供块,这正是 @ceejayoz 所指的内容)。

标签: php mysql


【解决方案1】:

我会稍微修改一下代码,将ReportModel 重命名为ReportList 以清楚起见,然后将getAll 的结果保存为属性。实现接口JsonSerializable,直接序列化对象。您还必须在Report 对象上实现接口,并指定要返回哪些字段进行序列化。

class ReportList implements JsonSerializable
{
    protected $reports = array();

    public function getAll()
    {
        $this->query('SELECT * FROM ...');
        $this->reports = System::arrayOfObjectTo('Report', $this->rows);
        return $this;
    }

    public function jsonSerialize(){
        return $this->reports;
    }
}

class Report implements JsonSerializable
{
    public function __construct($data)
    {
        $this->setName($data['name']);
        $this->setEmail($data['email']);
        $this->setPassword($data['password']);
    }

    public function jsonSerialize(){
        return ['name' => $this->name, 'email' => $this->email]; // array of private fields
    }
}

然后

class ReportController
{
    public function show()
    {
        $model = new ReportList();
        return json_encode($model->getAll());
    }
}

此外,如果您需要一些说明,您的代码并不十分灵活或可靠。你会得到各种各样的错误,例如如果给Report的构造函数的值不是一个数组或者是一个数组但不包含你使用的键。这与问题并没有真正的关系,所以我会在这里剪掉它,只是想让你知道。您可能可以在 code review 上获得有关此主题的更好建议。

关于多行时的错误,取决于很多东西,主要是你的机器和php设置。假设您在 10k 行上收到此错误,您要么有非常大的行,要么是 php.ini 允许的内存设置非常低。在 100 字节的平均行长度下,这可能比大多数情况下要长得多,100 字节的 10k 行大约是 1 兆字节。我不太了解 php 内部结构,但要将其转换为 json,您最多需要 5 倍于信息本身的空间,这意味着您需要 5 兆字节的内存(在最极端的情况下)来处理异常大的json 字符串,添加原始的 1 兆字节数据本身 + php 使用的 200 KB 之类的东西,对于 10k 行,平均大小为 100 字节,最终的内存不超过 7 - 10 兆字节。如果您的脚本不允许分配 10 兆字节的内存,那么好吧。

【讨论】:

  • 我不知道“实现 JsonSerializable”,那是一个非常好的接触。谢谢你的建议。
  • @Pablo 是的,这很有趣,我最近也发现了它,而且我已经与 PHP 打交道多年了。
猜你喜欢
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-04
  • 1970-01-01
  • 2016-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多