PHP规范PSR7(HTTP消息接口)介绍(三)

1.5 服务端请求

RequestInterface提供HTTP请求消息的一般表示。但是,由于服务器端环境的性质,服务器端请求需要额外的处理。服务器端处理需要考虑通用网关接口(CGI),更具体地说,需要考虑PHP通过其服务器API(SAPI)对CGI的抽象和扩展。 PHP通过超级全局提供了关于输入编组的简化,例如:

  • $ _COOKIE,反序列化并提供对HTTP cookie的简化访问。
  • $ _GET,反序列化并提供对查询字符串参数的简化访问。
  • $ _POST,反序列化并提供对通过HTTP POST提交的urlencoded参数的简化访问;通常,它可以被认为是解析消息体的结果。
  • $ _FILES,提供​​有关文件上传的序列化元数据。
  • $ _SERVER,提供对CGI / SAPI环境变量的访问,这些变量通常包括请求方法,请求方案,请求URI和标头。

ServerRequestInterface扩展了RequestInterface,以提供围绕这些各种超级全局的抽象。这种做法有助于减少消费者对超全球的耦合,并鼓励和提升测试请求消费者的能力。

服务器请求提供了一个附加属性“属性”,以允许消费者根据特定于应用程序的规则(例如路径匹配,方案匹配,主机匹配等)内省,分解和匹配请求。因此,服务器请求还可以在多个请求消费者之间提供消息传递。

1.6 上传文件

ServerRequestInterface指定用于在规范化结构中检索上载文件树的方法,每个叶子都是UploadedFileInterface的实例。

$ _FILES超全局在处理文件输入数组时有一些众所周知的问题。例如,如果您有一个提交文件数组的表单 - 例如输入名称“files”,提交文件[0]和文件[1] - PHP将表示为:

array(
    'files' => array(
        'name' => array(
            0 => 'file0.txt',
            1 => 'file1.html',
        ),
        'type' => array(
            0 => 'text/plain',
            1 => 'text/html',
        ),
        /* etc. */
    ),
)

而不是预期的:

array(
    'files' => array(
        0 => array(
            'name' => 'file0.txt',
            'type' => 'text/plain',
            /* etc. */
        ),
        1 => array(
            'name' => 'file1.html',
            'type' => 'text/html',
            /* etc. */
        ),
    ),
)

结果是消费者需要知道这种语言实现细节,并编写用于收集给定上载数据的代码。

此外,存在以下情况:在发生文件上载时未填充$ _FILES:

  • 当HTTP方法不是POST时。
  • 单元测试时。
  • 在非SAPI环境下运行时,例如ReactPHP。

在这种情况下,数据需要以不同方式播种。例如:

  • 进程可能会解析邮件正文以发现文件上载。在这种情况下,实现可以选择不将文件上载写入文件系统,而是将它们包装在流中以减少内存,I / O和存储开销。
  • 在单元测试场景中,开发人员需要能够存根和/或模拟文件上载元数据,以便验证和验证不同的场景。

引用的树结构应该模仿提交文件的命名结构。

在最简单的示例中,这可能是提交的单个命名表单元素:

<input type="file" name="avatar" />

在这种情况下,$ _FILES中的结构如下所示:

array(
    'avatar' => array(
        'tmp_name' => 'phpUxcOty',
        'name' => 'my-avatar.png',
        'size' => 90996,
        'type' => 'image/png',
        'error' => 0,
    ),
)

getUploadedFiles()返回的规范化表单将是:

array(
    'avatar' => /* UploadedFileInterface instance */
)

对于使用数组表示法输入名称的输入:

<input type="file" name="my-form[details][avatar]" />

$ _FILES最终看起来像这样:

array(
    'my-form' => array(
        'details' => array(
            'avatar' => array(
                'tmp_name' => 'phpUxcOty',
                'name' => 'my-avatar.png',
                'size' => 90996,
                'type' => 'image/png',
                'error' => 0,
            ),
        ),
    ),
)

getUploadedFiles()返回的相应树应该是:

array(
    'my-form' => array(
        'details' => array(
            'avatar' => /* UploadedFileInterface instance */
        ),
    ),
)

在某些情况下,您可以指定一个文件数组:

Upload an avatar: <input type="file" name="my-form[details][avatars][]" />
Upload an avatar: <input type="file" name="my-form[details][avatars][]" />

(例如,JavaScript控件可能会产生额外的文件上传输入,以允许一次上传多个文件。)

在这种情况下,规范实现必须聚合与给定索引处的文件相关的所有信息。原因是因为$ _FILES在这种情况下偏离其正常结构:

array(
    'my-form' => array(
        'details' => array(
            'avatars' => array(
                'tmp_name' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
                'name' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
                'size' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
                'type' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
                'error' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
            ),
        ),
    ),
)

上面的$ _FILES数组将对应于getUploadedFiles()返回的以下结构

array(
    'my-form' => array(
        'details' => array(
            'avatars' => array(
                0 => /* UploadedFileInterface instance */,
                1 => /* UploadedFileInterface instance */,
                2 => /* UploadedFileInterface instance */,
            ),
        ),
    ),
)

消费者将使用以下方法访问嵌套数组的索引1:

$request->getUploadedFiles()['my-form']['details']['avatars'][1];

 由于上传的文件数据是派生的(派生自$ _FILES或请求体),因此接口中也存在mutator方法withUploadedFiles(),允许将规范化委托给另一个进程。

在原始示例的情况下,消费类似于以下内容:

$file0 = $request->getUploadedFiles()['files'][0];
$file1 = $request->getUploadedFiles()['files'][1];

printf(
    "Received the files %s and %s",
    $file0->getClientFilename(),
    $file1->getClientFilename()
);

// "Received the files file0.txt and file1.html"

该提案还认识到实现可以在非SAPI环境中运行。因此,UploadedFileInterface提供了确保操作无论环境如何都可以工作的方法。特别是:

  • moveTo($ targetPath)作为直接在临时上载文件上调用move_uploaded_file()的安全和推荐的替代方法提供。实现将根据环境检测正确的操作。
  • getStream()将返回一个StreamInterface实例。在非SAPI环境中,一种建议的可能性是将单个上载文件解析为php:// temp streams而不是直接解析到文件;在这种情况下,不存在上传文件。因此,无论环境如何,getStream()都可以保证工作。

例如:

// Move a file to an upload directory
$filename = sprintf(
    '%s.%s',
    create_uuid(),
    pathinfo($file0->getClientFilename(), PATHINFO_EXTENSION)
);
$file0->moveTo(DATA_DIR . '/' . $filename);

// Stream a file to Amazon S3.
// Assume $s3wrapper is a PHP stream that will write to S3, and that
// Psr7StreamWrapper is a class that will decorate a StreamInterface as a PHP
// StreamWrapper.
$stream = new Psr7StreamWrapper($file1->getStream());
stream_copy_to_stream($stream, $s3wrapper);

 2 包装

所描述的接口和类作为psr / http-message包的一部分提供。

猜你喜欢

转载自blog.csdn.net/u013702678/article/details/83479981