Skip to content

Commit

Permalink
Add new MultipartRequest class to handle extra parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
samwilson authored and addshore committed Oct 10, 2017
1 parent 6fb0381 commit 2eb6350
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 8 deletions.
2 changes: 1 addition & 1 deletion RELEASENOTES.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
These are the release notes for the [mediawiki-api-base](README.md) library.

## Version 2.4.0 (work in progress)
* TBA
* New MultipartRequest class added. PR [#38](https://github.com/addwiki/mediawiki-api-base/pull/38).

## Version 2.3.1 (3 May 2017)
* Don't fail on libxml errors if the RSD URL can still be found. PR [#35](https://github.com/addwiki/mediawiki-api-base/pull/35), Fixes [T163527](https://phabricator.wikimedia.org/T163527).
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Welcome to mediawiki-api-base's documentation!

overview
quickstart
multipart

Indices and tables
==================
Expand Down
28 changes: 28 additions & 0 deletions docs/multipart.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
==================
Multipart requests
==================

The MultipartRequest class can be used if you need a FluentRequest that has more parameters to be set on individual parts of a multipart request.

The name is a slight misnomer, because either of the other two Request classes (SimpleRequest and FluentRequest)
will also end up being multipart requests if you pass any parameters of type Resource_.

.. _Resource: http://php.net/manual/en/resource.php

To use a MultipartRequest you must first set the main parameters, and then you can add additional "multipart parameters" to any of the parameters you've set.
(You will get an Exception if you try to set a multipart parameter for a main parameter that doesn't exist yet.)

For example, to add a ``Content-Disposition`` header to a parameter named ``param1``::

$contentDisposition = 'form-data; name="param1"; filename="a_filename.png"';
$request = MultipartRequest::factory()
->setParams( [ 'param1' => 'Lorem ipsum' ] )
->setAction( 'actionname' )
->setMultipartParams( [
'param1' => [
'headers' => [ 'Content-Disposition' => $contentDisposition ],
],
] );
$response = $api->postRequest( $request );

(For details of creating the ``$api`` object in this example, see :ref:`quickstart`.)
2 changes: 2 additions & 0 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _quickstart:

==========
Quickstart
==========
Expand Down
4 changes: 2 additions & 2 deletions src/FluentRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ public function getHeaders() {
/**
* @since 1.0
*
* @return self
* @return static
*/
public static function factory() {
return new self();
return new static();
}

/**
Expand Down
27 changes: 22 additions & 5 deletions src/MediawikiApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ private function decodeResponse( ResponseInterface $response ) {
* @return string
*/
private function getPostRequestEncoding( Request $request ) {
if ( $request instanceof MultipartRequest ) {
return 'multipart';
}
foreach ( $request->getParams() as $value ) {
if ( is_resource( $value ) ) {
return 'multipart';
Expand All @@ -286,7 +289,7 @@ private function getPostRequestEncoding( Request $request ) {
private function getClientRequestOptions( Request $request, $paramsKey ) {
$params = array_merge( $request->getParams(), [ 'format' => 'json' ] );
if ( $paramsKey === 'multipart' ) {
$params = $this->encodeMultipartParams( $params );
$params = $this->encodeMultipartParams( $request, $params );
}

return [
Expand All @@ -296,17 +299,31 @@ private function getClientRequestOptions( Request $request, $paramsKey ) {
}

/**
* @param array $params
* Turn the normal key-value array of request parameters into a multipart array where each
* parameter is a new array with a 'name' and 'contents' elements (and optionally more, if the
* request is a MultipartRequest).
*
* @param Request $request The request to which the parameters belong.
* @param string[] $params The existing parameters. Not the same as $request->getParams().
*
* @return array
*/
private function encodeMultipartParams( $params ) {
private function encodeMultipartParams( Request $request, $params ) {
// See if there are any multipart parameters in this request.
$multipartParams = ( $request instanceof MultipartRequest )
? $request->getMultipartParams()
: [];
return array_map(
function ( $name, $value ) {
return [
function ( $name, $value ) use ( $multipartParams ) {
$partParams = [
'name' => $name,
'contents' => $value,
];
if ( isset( $multipartParams[ $name ] ) ) {
// If extra parameters have been set for this part, use them.
$partParams = array_merge( $multipartParams[ $name ], $partParams );
}
return $partParams;
},
array_keys( $params ),
$params
Expand Down
77 changes: 77 additions & 0 deletions src/MultipartRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Mediawiki\Api;

use Exception;

/**
* A MultipartRequest is the same as a FluentRequest with additional support for setting request
* parameters (both normal parameters and headers) on multipart requests.
*
* @link http://docs.guzzlephp.org/en/stable/request-options.html#multipart
*
* @since 2.4.0
*/
class MultipartRequest extends FluentRequest {

/** @var mixed[] */
protected $multipartParams = [];

/**
* Check the structure of a multipart parameter array.
*
* @param mixed[] $params The multipart parameters to check.
*
* @throws Exception
*/
protected function checkMultipartParams( $params ) {
foreach ( $params as $key => $val ) {
if ( !is_array( $val ) ) {
throw new Exception( "Parameter '$key' must be an array." );
}
if ( !in_array( $key, array_keys( $this->getParams() ) ) ) {
throw new Exception( "Parameter '$key' is not already set on this request." );
}
}
}

/**
* Set all multipart parameters, replacing all existing ones.
*
* Each key of the array passed in here must be the name of a parameter already set on this
* request object.
*
* @param mixed[] $params The multipart parameters to use.
* @return $this
*/
public function setMultipartParams( $params ) {
$this->checkMultipartParams( $params );
$this->multipartParams = $params;
return $this;
}

/**
* Add extra multipart parameters.
*
* Each key of the array passed in here must be the name of a parameter already set on this
* request object.
*
* @param mixed[] $params The multipart parameters to add to any already present.
*
* @return $this
*/
public function addMultipartParams( $params ) {
$this->checkMultipartParams( $params );
$this->multipartParams = array_merge( $this->multipartParams, $params );
return $this;
}

/**
* Get all multipart request parameters.
*
* @return mixed[]
*/
public function getMultipartParams() {
return $this->multipartParams;
}
}
44 changes: 44 additions & 0 deletions tests/Unit/MultipartRequestTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Mediawiki\Api\Test\Unit;

use Exception;
use Mediawiki\Api\MultipartRequest;
use PHPUnit_Framework_TestCase;

class MultipartRequestTest extends PHPUnit_Framework_TestCase {

public function testBasics() {
$request = new MultipartRequest();
$this->assertEquals( [], $request->getMultipartParams() );

// One parameter.
$request->setParam( 'testparam', 'value' );
$request->addMultipartParams( [ 'testparam' => [ 'lorem' => 'ipsum' ] ] );
$this->assertEquals(
[ 'testparam' => [ 'lorem' => 'ipsum' ] ],
$request->getMultipartParams()
);

// Another parameter.
$request->setParam( 'testparam2', 'value' );
$request->addMultipartParams( [ 'testparam2' => [ 'lorem2' => 'ipsum2' ] ] );
$this->assertEquals(
[
'testparam' => [ 'lorem' => 'ipsum' ],
'testparam2' => [ 'lorem2' => 'ipsum2' ],
],
$request->getMultipartParams()
);
}

/**
* You are not allowed to set multipart parameters on a parameter that doesn't exist.
* @expectedException Exception
* @expectedExceptionMessage Parameter 'testparam' is not already set on this request.
*/
public function testParamNotYetSet() {
$request = new MultipartRequest();
$request->addMultipartParams( [ 'testparam' => [] ] );
}
}

0 comments on commit 2eb6350

Please sign in to comment.