Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="note"><ac:rich-text-body>
<p>This proposal is being split into two: one for Zend\Rest\Client and another for Zend\Rest\Server</p></ac:rich-text-body></ac:macro>
<ac:macro ac:name="toc"><ac:parameter ac:name="maxLevel">3</ac:parameter></ac:macro>
<h2>Overview</h2>

<p>The REST client in ZF 1.x only supports XML content types. Since REST does not dictate which content type to respond with, some REST services opt to use other formats such as JSON. Additionally, the REST client can benefit from supporting hypermedia to help make services self-discoverable.</p>

<h3>Changes to Zend_Rest_Client</h3>
<p>Zend\Rest\Client will be updated to support various content return types, including custom ones that were not anticipated during this proposal. Optionally a hypermedia parser can help to discover hypermedia within the returned content body.</p>

<h2>References</h2>
<ul>
<li><a href="http://martinfowler.com/articles/richardsonMaturityModel.html">Richardson Maturity Model</a></li>
</ul>

<h2>Component Requirements, Constraints, and Acceptance Criteria</h2>
<ul>
<li>This component <strong>will</strong> introduce BC breaks with ZF 1.x.</li>
<li>This component <strong>will</strong> support content type negotiation with built in support for XML, JSON and ATOM.</li>
<li>This component <strong>will</strong> use a LIFO stack for negotiation prioritization.</li>
<li>This component <strong>will</strong> support custom content types.</li>
<li>This component <strong>may</strong> parse responses for HyperMedia.</li>
<li>This component <strong>will</strong> support custom HyperMedia parsers.</li>
<li>This component <strong>will</strong> understand ETags.</li>
<li>This component <strong>may</strong> support client-side caching (e.g. to support conditional GET).</li>
<li>This component <strong>may</strong> use CRUD as aliases for HTTP verbs.</li>
</ul>

<h2>Theory of Operation</h2>
<p>The client should be easy to use. In its simplest form:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
use Zend\Rest\Client;

$client = new RestClient('http://rest.example.com');
$orders = $client->get('orders');
assert($orders instanceof \Zend\Rest\Response); // true
echo $orders->price; // outputs "$5.25"
]]></ac:plain-text-body></ac:macro>

<h2>Dependencies on Other Framework Components</h2>
<ul>
<li>Zend\Service\AbstractService</li>
<li>Zend\Http\Client</li>
<li>Zend\Uri</li>
</ul>

<h2>Milestones / Tasks</h2>
<ul>
<li>Milestone 1: Acquire community acceptance of proposal</li>
<li>Milestone 2: Working prototype checked in to github fork</li>
<li>Milestone 3: Unit tests checked in to github fork</li>
<li>Milestone 4: Acquire community acceptance of github fork</li>
<li>Milestone 5: Submit pull request to zf2</li>
</ul>

<h2>Class Index</h2>
<ul>
<li>Zend\Rest\Client\RestClient</li>
<li>Zend\Rest\Client\Response\ResponseInterface</li>
<li>Zend\Rest\Client\Response\Response</li>
<li>Zend\Rest\Client\Negotiator\NegotiatorInterface</li>
<li>Zend\Rest\Client\Negotiator\Xml</li>
<li>Zend\Rest\Client\Negotiator\Json</li>
<li>Zend\Rest\Client\Negotiator\Atom</li>
</ul>

<h2>Use Cases</h2>
<h3>UC-01 Simple GET request</h3>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$client = new RestClient('http://rest.example.com');
$order = $client->get('order/123'); // or $client->read('order/123')
assert($order instanceof \Zend\Rest\Response); // true
echo $order->price; // outputs "$5.25"
]]></ac:plain-text-body></ac:macro>

<h3>UC-02 Creating a new resource using PUT</h3>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$client = new RestClient('http://rest.example.com');
$order = array(
'product' => 'milk',
'flavor' => 'chocolate'
);
$response = $client->put($order); // or $client->create('order/123');
if ($response->isSuccess()) {
// ok!
} else {
throw new Exception\RestException("Received error response {$response->getCode()}");
}
]]></ac:plain-text-body></ac:macro>

<h3>UC-03 Updating an existing resource</h3>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$order = $client->get('order/123');
$order->flavor = 'vanilla';
$response = $client->update($order); // No need to remember the URI
if ($response->isSuccess()) {
// ok!
} else {
throw new Exception\RestException("Received error response {$response->getCode()}");
}
]]></ac:plain-text-body></ac:macro>

<h3>UC-04 Deleting an existing resource</h3>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$client->delete('order/123');
// or
$order = $client->get('order/123');
$client->delete($order);
]]></ac:plain-text-body></ac:macro>

<h3>UC-05 Detecting hypermedia</h3>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$orders = $client->read('orders');
assert($orders instanceof \Zend\Rest\Response); // true

foreach ($orders->order as $order) {
assert($orders instanceof \Zend\Rest\Response); // true
// Attempt to detect hypermedia
$orderUri = $order->getUri(); // returns first hypermedia URI within resource e.g. "order/123";
$orderResource = $client->read($orderUri);
}
]]></ac:plain-text-body></ac:macro>

<h3>UC-06 Providing a custom content type negotiator for parsing custom responses (e.g. YAML).</h3>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
use \Zend\Rest\Client\Negotiator
class MyNegotiator implements NegotiatorInterface
{
public function getContentType();
public function parse($body);
}

$myNegotiator = new MyNegotiator();
$client->registerContentTypeNegotiator($myNegotiator); // LIFO stack of negotiators
$response = $client->read('order/123');
]]></ac:plain-text-body></ac:macro>

<h3>UC-07 Conditional GET with Zend\Cache</h3>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
assert($cache instanceof \Zend\Cache\Cache); // true
$cache->clean(); // empty cache
$client->setCache($cache);
$response1 = $client->read('order/123'); // cache miss
$response2 = $client->read('order/123'); // cache hit
sleep(60); // somehow the resource is updated during this sleep
$response3 = $client->read('order/123'); // cache hit replaced with updated response
]]></ac:plain-text-body></ac:macro>

<h2>Class Skeletons</h2>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Feb 06, 2012

    <p>(duplicate)</p>

  2. Feb 06, 2012

    <p>Any changes to the way Zend_Rest_Client handles params (specifically multiple GET params)? I know this has caused some issues (see the warning in the manual and #ZF-9307 as examples). Personally I'd prefer to see a syntax more along the lines of:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $client->get('/path/to/method', array(
    'api_key' => 'something',
    'fruit' => 'apple'
    ));
    ]]></ac:plain-text-body></ac:macro>