[HttFoundation] extracted FileBag, ServerBag, fixed HeaderBag::add(), updated Request test

This commit is contained in:
Bulat Shakirzyanov 2011-01-22 12:34:18 -05:00 committed by Fabien Potencier
parent 183c8c6f6c
commit 271e757f27
7 changed files with 333 additions and 186 deletions

View File

@ -0,0 +1,127 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* FileBag is a container for HTTP headers.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
*/
class FileBag extends ParameterBag
{
private $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
/**
* (non-PHPdoc)
* @see Symfony\Component\HttpFoundation\ParameterBag::replace()
*/
public function replace(array $files = array())
{
$this->parameters = array();
$this->add($files);
}
/**
* (non-PHPdoc)
* @see Symfony\Component\HttpFoundation\ParameterBag::set()
*/
public function set($key, $value)
{
if (is_array($value)) {
parent::set($key, $this->convertFileInformation($value));
}
}
/**
* (non-PHPdoc)
* @see Symfony\Component\HttpFoundation\ParameterBag::add()
*/
public function add(array $files = array())
{
foreach ($files as $key => $file) {
$this->set($key, $file);
}
}
/**
* Converts uploaded files to UploadedFile instances.
*
* @param array $file A (multi-dimensional) array of uploaded file information
*
* @return array A (multi-dimensional) array of UploadedFile instances
*/
protected function convertFileInformation(array $file)
{
$file = $this->fixPhpFilesArray($file);
if (is_array($file)) {
$keys = array_keys($file);
sort($keys);
if ($keys == $this->fileKeys) {
$file['error'] = (int) $file['error'];
}
if ($keys != $this->fileKeys) {
$file = array_map(array($this, 'convertFileInformation'), $file);
} else
if ($file['error'] === UPLOAD_ERR_NO_FILE) {
$file = null;
} else {
$file = new UploadedFile($file['tmp_name'], $file['name'],
$file['type'], $file['size'], $file['error']);
}
}
return $file;
}
/**
* Fixes a malformed PHP $_FILES array.
*
* PHP has a bug that the format of the $_FILES array differs, depending on
* whether the uploaded file fields had normal field names or array-like
* field names ("normal" vs. "parent[child]").
*
* This method fixes the array to look like the "normal" $_FILES array.
*
* It's safe to pass an already converted array, in which case this method
* just returns the original array unmodified.
*
* @param array $data
* @return array
*/
protected function fixPhpFilesArray($data)
{
if (! is_array($data)) {
return $data;
}
$keys = array_keys($data);
sort($keys);
if ($this->fileKeys != $keys || ! isset($data['name']) ||
! is_array($data['name'])) {
return $data;
}
$files = $data;
foreach ($this->fileKeys as $k) {
unset($files[$k]);
}
foreach (array_keys($data['name']) as $key) {
$files[$key] = $this->fixPhpFilesArray(array(
'error' => $data['error'][$key],
'name' => $data['name'][$key], 'type' => $data['type'][$key],
'tmp_name' => $data['tmp_name'][$key],
'size' => $data['size'][$key]
));
}
return $files;
}
}

View File

@ -62,6 +62,16 @@ class HeaderBag
public function replace(array $headers = array())
{
$this->headers = array();
$this->add($headers);
}
/**
* Adds new headers the current HTTP headers set.
*
* @param array $headers An array of HTTP headers
*/
public function add(array $headers)
{
foreach ($headers as $key => $values) {
$this->set($key, $values);
}

View File

@ -103,9 +103,9 @@ class Request
$this->query = new ParameterBag(null !== $query ? $query : $_GET);
$this->attributes = new ParameterBag(null !== $attributes ? $attributes : array());
$this->cookies = new ParameterBag(null !== $cookies ? $cookies : $_COOKIE);
$this->files = new ParameterBag($this->convertFileInformation(null !== $files ? $files : $_FILES));
$this->server = new ParameterBag(null !== $server ? $server : $_SERVER);
$this->headers = new HeaderBag($this->initializeHeaders());
$this->files = new FileBag(null !== $files ? $files : $_FILES);
$this->server = new ServerBag(null !== $server ? $server : $_SERVER);
$this->headers = new HeaderBag($this->server->getHeaders());
$this->content = null;
$this->languages = null;
@ -930,102 +930,6 @@ class Request
return (string) $pathInfo;
}
/**
* Converts uploaded files to UploadedFile instances.
*
* @param array $files A (multi-dimensional) array of uploaded file information
*
* @return array A (multi-dimensional) array of UploadedFile instances
*/
protected function convertFileInformation(array $files)
{
$fixedFiles = array();
foreach ($files as $key => $data) {
$fixedFiles[$key] = $this->fixPhpFilesArray($data);
}
$fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
foreach ($fixedFiles as $key => $data) {
if (is_array($data)) {
$keys = array_keys($data);
sort($keys);
if ($keys == $fileKeys) {
$data['error'] = (int) $data['error'];
}
if ($keys != $fileKeys) {
$fixedFiles[$key] = $this->convertFileInformation($data);
} else if ($data['error'] === UPLOAD_ERR_NO_FILE) {
$fixedFiles[$key] = null;
} else {
$fixedFiles[$key] = new UploadedFile($data['tmp_name'], $data['name'], $data['type'], $data['size'], $data['error']);
}
}
}
return $fixedFiles;
}
/**
* Fixes a malformed PHP $_FILES array.
*
* PHP has a bug that the format of the $_FILES array differs, depending on
* whether the uploaded file fields had normal field names or array-like
* field names ("normal" vs. "parent[child]").
*
* This method fixes the array to look like the "normal" $_FILES array.
*
* It's safe to pass an already converted array, in which case this method
* just returns the original array unmodified.
*
* @param array $data
* @return array
*/
protected function fixPhpFilesArray($data)
{
if (!is_array($data)) {
return $data;
}
$fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
$keys = array_keys($data);
sort($keys);
if ($fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) {
return $data;
}
$files = $data;
foreach ($fileKeys as $k) {
unset($files[$k]);
}
foreach (array_keys($data['name']) as $key) {
$files[$key] = $this->fixPhpFilesArray(array(
'error' => $data['error'][$key],
'name' => $data['name'][$key],
'type' => $data['type'][$key],
'tmp_name' => $data['tmp_name'][$key],
'size' => $data['size'][$key],
));
}
return $files;
}
protected function initializeHeaders()
{
$headers = array();
foreach ($this->server->all() as $key => $value) {
if ('http_' === strtolower(substr($key, 0, 5))) {
$headers[substr($key, 5)] = $value;
}
}
return $headers;
}
static protected function initializeFormats()
{
static::$formats = array(

View File

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation;
/**
* HeaderBag is a container for HTTP headers.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
*/
class ServerBag extends ParameterBag
{
public function getHeaders()
{
$headers = array();
foreach ($this->parameters as $key => $value) {
if ('http_' === strtolower(substr($key, 0, 5))) {
$headers[substr($key, 5)] = $value;
}
}
return $headers;
}
}

View File

@ -0,0 +1,117 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\FileBag;
/**
* FileBagTest.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.org>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
*/
class FileBagTest extends \PHPUnit_Framework_TestCase
{
public function testShouldConvertsUploadedFiles()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array('file' => array(
'name' => basename($tmpFile),
'type' => 'text/plain',
'tmp_name' => $tmpFile,
'error' => 0,
'size' => 100
)));
$this->assertEquals($file, $bag->get('file'));
}
public function testShouldSetEmptyUploadedFilesToNull()
{
$bag = new FileBag(array('file' => array(
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => UPLOAD_ERR_NO_FILE,
'size' => 0
)));
$this->assertEquals(null, $bag->get('file'));
}
public function testShouldConvertUploadedFilesWithPhpBug()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array(
'child' => array(
'name' => array(
'file' => basename($tmpFile),
),
'type' => array(
'file' => 'text/plain',
),
'tmp_name' => array(
'file' => $tmpFile,
),
'error' => array(
'file' => 0,
),
'size' => array(
'file' => 100,
),
)
));
$files = $bag->all();
$this->assertEquals($file, $files['child']['file']);
}
public function testShouldConvertNestedUploadedFilesWithPhpBug()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array(
'child' => array(
'name' => array(
'sub' => array('file' => basename($tmpFile))
),
'type' => array(
'sub' => array('file' => 'text/plain')
),
'tmp_name' => array(
'sub' => array('file' => $tmpFile)
),
'error' => array(
'sub' => array('file' => 0)
),
'size' => array(
'sub' => array('file' => 100)
),
)
));
$files = $bag->all();
$this->assertEquals($file, $files['child']['sub']['file']);
}
protected function createTempFile()
{
return tempnam(sys_get_temp_dir(), 'FormTest');
}
}

View File

@ -392,93 +392,6 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST');
}
public function testInitializeConvertsUploadedFiles()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$request = Request::create('', 'get', array(), array(), array('file' => array(
'name' => basename($tmpFile),
'type' => 'text/plain',
'tmp_name' => $tmpFile,
'error' => 0,
'size' => 100
)));
$this->assertEquals($file, $request->files->get('file'));
}
public function testInitializeDoesNotConvertEmptyUploadedFiles()
{
$request = Request::create('', 'get', array(), array(), array('file' => array(
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => UPLOAD_ERR_NO_FILE,
'size' => 0
)));
$this->assertEquals(null, $request->files->get('file'));
}
public function testInitializeConvertsUploadedFilesWithPhpBug()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$request = Request::create('', 'get', array(), array(), array(
'child' => array(
'name' => array(
'file' => basename($tmpFile),
),
'type' => array(
'file' => 'text/plain',
),
'tmp_name' => array(
'file' => $tmpFile,
),
'error' => array(
'file' => 0,
),
'size' => array(
'file' => 100,
),
)
));
$files = $request->files->all();
$this->assertEquals($file, $files['child']['file']);
}
public function testInitializeConvertsNestedUploadedFilesWithPhpBug()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$request = Request::create('', 'get', array(), array(), array(
'child' => array(
'name' => array(
'sub' => array('file' => basename($tmpFile))
),
'type' => array(
'sub' => array('file' => 'text/plain')
),
'tmp_name' => array(
'sub' => array('file' => $tmpFile)
),
'error' => array(
'sub' => array('file' => 0)
),
'size' => array(
'sub' => array('file' => 100)
),
)
));
$files = $request->files->all();
$this->assertEquals($file, $files['child']['sub']['file']);
}
public function testGetContentWorksTwiceInDefaultMode()
{
$req = new Request;

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\ServerBag;
/**
* ServerBagTest
*
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
*/
class ServerBagTest extends \PHPUnit_Framework_TestCase
{
public function testShouldExtractHeadersFromServerArray()
{
$server = array(
'SOME_SERVER_VARIABLE' => 'value',
'SOME_SERVER_VARIABLE2' => 'value',
'ROOT' => 'value',
'HTTP_CONTENT_TYPE' => 'text/html',
'HTTP_CONTENT_LENGTH' => '0',
'HTTP_ETAG' => 'asdf',
);
$bag = new ServerBag($server);
$this->assertEquals(array(
'CONTENT_TYPE' => 'text/html',
'CONTENT_LENGTH' => '0',
'ETAG' => 'asdf'
), $bag->getHeaders());
}
}