introduction to zend framework web services
TRANSCRIPT
Zend Framework “at your service”
Michelangelo van DamMacq Electronique 2010, Brussels
WARNING
Used EnvironmentPHP 5.3.2
Zend Framework 1.10.4MySQL 5.1.32
Examples might not work on another environment
Michelangelo van Dam
• Independent Consultant
• Zend Certified Engineer (ZCE)
- PHP 4 & PHP 5
- Zend Framework• Co-Founder of PHPBenelux• Shepherd of “elephpant” herds
What is Zend Framework
A loosly-coupled framework with a flexible architecture that lets you easily build modern web applications and web servicesMatthew Weier O’Phinney - Chief Architect Zend Framework
Why a service ?
• opening up existing API- internal applications- external applications- additional functionality• support to more devices• portability and flexibility
Important components
• Zend_Soap- Zend_Soap_Server- Zend_Soap_Client- Zend_Soap_Wsdl• Zend_Rest- Zend_Rest_Server- Zend_Rest_Client• Zend_XmlRpc- Zend_XmlRpc_Server- Zend_XmlRpc_Client- Zend_XmlRpc_Request- Zend_XmlRpc_Response
Example service
• Time registration- list time sheets- add a new task- edit an existing task- delete an existing task
MVC approach
• time module- controllers (for displaying listing and forms)- actions (for listing, adding, editing and deleting)- models (for access to database)- forms (for filtering, validation and rendering forms)
Common behavior
• all “logic” is put in the controller• actions = API interface• downside- not flexible towards other clients- difficult to provide maintenance- not reusable design
Time_IndexController
Time ModuleindexAction
- lists registered tasks- links to the form - for adding task - for editing task - for deleting task
editAction- displays a form - for adding task - for editing task
registerAction- processes form data - validating data - storing data in db
deleteAction- deletes task
API Design
• moving logic- out the controller- in an API class• structures the application• is better testable• is better maintainable
My_Api_Timesheet
Time APIlistTasks
- lists registered tasksregisterNewTask
- for adding task
editExistingTask- for modifying task
deleteExistingTask- deletes task
Simple ?
API output channels
API
Web
XML
SOAP
XMLRPC
Internal
JSON … REST
Timesheet API
DocBlocks are important!
• DocBlocks are very important !- provide quality API documentation- can be used as reference for the service !!!
Automated API docs
My_Api_Timesheet<?phpclass My_Api_Timesheet{}
listTasks/** * List all tasks for a given user * * @param int $user * @param int $limit * @return array */public function listTasks($user, $limit = null){ $array = array (); $timesheet = new Time_Model_Timesheet(); if (null !== ($result = $timesheet->fetchAll(array ( 'user_id = ?' => $user, ), array ('date DESC', 'start_time ASC'), $limit))) { foreach ($result as $entry) { $array[] = $entry->toArray(); } } return $array;}
registerNewTask/** * Register a new task * * @param int $user The ID of the user * @param int $customer The ID of the customer * @param int $task The ID of the task * @param string $date A date formatted as YYYY-mm-dd * @param string $start The starting time as HH:mm:ss * @param string $end The ending time as HH:mm:ss * @param string $description A short description * @return bool TRUE if registration succeeded * @throws My_Api_Timesheet_Exception */
registerNewTaskpublic function registerNewTask( $user, $customer, $task, $date, $start, $end, $description){ $timesheet = new Time_Model_Timesheet(); $timesheet->setUserId($user) ->setCustomerId($customer) ->setTaskId($task) ->setDate($date) ->setStartTime($start) ->setEndTime($end) ->setDescription($description); $validator = $this->_validate($timesheet); if (false === $validator) { require_once 'My/Api/Timesheet/Exception.php'; throw new My_Api_Timesheet_Exception('Invalid data provided'); } $timesheet->save(); return true;}
editExistingTask/** * Modify an existing task * * @param int $id The ID of an existing Task * @param int $user The ID of the user * @param int $customer The ID of the customer * @param int $task The ID of the task * @param string $date A date formatted as YYYY-mm-dd * @param string $start The starting time as HH:mm:ss * @param string $end The ending time as HH:mm:ss * @param string $description A short description * @return bool TRUE if registration succeeded * @throws My_Api_Timesheet_Exception */
editExistingTaskpublic function editExistingTask( $id, $user, $customer, $task, $date, $start, $end, $description){ $timesheet = new Time_Model_Timesheet(); $timesheet->setId($id) ->setUserId($user) ->setCustomerId($customer) ->setTaskId($task) ->setDate($date) ->setStartTime($start) ->setEndTime($end) ->setDescription($description); $validator = $this->_validate($timesheet); if (false === $validator) { require_once 'My/Api/Timesheet/Exception.php'; throw new My_Api_Timesheet_Exception('Invalid data provided'); } $timesheet->save(); return true;}
deleteExistingTask/** * Removes an existing task * * @param int $id The ID of an existing Task * @param int $user The ID of the user * @return bool TRUE if registration succeeded */public function deleteExistingTask($id, $user){ $timesheet = new Time_Model_Timesheet(); $timesheet->setId($id) ->setUserId($user); $validator = $this->_validate($timesheet); if (false === $validator) { require_once 'My/Api/Timesheet/Exception.php'; throw new My_Api_Timesheet_Exception('Invalid data provided'); } $timesheet->delete(array ( 'id = ?' => $timesheet->getId(), 'user_id = ?' => $timesheet->getUserId(), )); return true;}
_validate/** * Private validation method * * @param Time_Model_Timesheet $timesheet * @return bool TRUE if validated, FALSE if invalid */private function _validate(Time_Model_Timesheet $timesheet){ $result = true; $validator = new Time_Form_Register(); $customer = new Time_Model_Customer(); $task = new Time_Model_Task(); $validator->getElement('customer_id')->setMultiOptions($customer->toSelect()); $validator->getElement('task_id')->setMultiOptions($task->toSelect()); if (!$validator->isValid($timesheet->toArray())) { $result = false; } return $result;}
Zend_XmlRpc
XmlRpc Server<?phpclass Time_XmlRpcController extends Zend_Controller_Action{ public function indexAction() { $this->_helper->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); require_once 'My/Api/Timesheet.php'; $server = new Zend_XmlRpc_Server(); $server->setClass('My_Api_Timesheet'); echo $server->handle(); }}
XmlRpc Client<?phpclass Time_XmlRpcClientController extends Zend_Controller_Action{ protected $_client; public function init() { $this->_helper->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); $this->_client = new Zend_XmlRpc_Client( 'http://www.demo.local/Time/XmlRpc'); }…}
XmlRpc Clientpublic function newAction(){ $client = $this->_client; $request = new Zend_XmlRpc_Request(); $request->setMethod('registerNewTask'); $request->setParams(array ( 2, // user ID 3, // customer ID 3, // task ID '2010-03-28', // date '18:00:00', // start time '23:30:00', // end time 'Demo XmlRpc add', // description )); $result = null; $errors = array (); try { $client->doRequest($request); $result = $client->getLastResponse()->getReturnValue(); } catch (Zend_XmlRpc_Client_FaultException $e) { $errors['fault'] = "[{$e->getCode()}]: {$e->getMessage()}"; } catch (Zend_XmlRpc_Client_HttpException $e) { $errors['http'] = "[{$e->getCode()}]: {$e->getMessage()}"; } catch (My_Api_Timesheet_Exception $e) { $errors['api'] = "[{$e->getCode()}]: {$e->getMessage()}"; }}
XmlRpc Clientpublic function editAction(){ $client = $this->_client; $request = new Zend_XmlRpc_Request(); $request->setMethod('editExistingTask'); $request->setParams(array ( 5, // ID 2, // user ID 3, // customer ID 4, // task ID '2010-03-28', // date '18:00:00', // start time '23:30:00', // end time 'Demo XmlRpc add', // description )); $result = null; $errors = array (); try { $client->doRequest($request); $result = $client->getLastResponse()->getReturnValue(); } catch (Zend_XmlRpc_Client_FaultException $e) { $errors['fault'] = "[{$e->getCode()}]: {$e->getMessage()}"; } catch (Zend_XmlRpc_Client_HttpException $e) { $errors['http'] = "[{$e->getCode()}]: {$e->getMessage()}"; } catch (My_Api_Timesheet_Exception $e) { $errors['api'] = "[{$e->getCode()}]: {$e->getMessage()}"; }}
XmlRpc Clientpublic function deleteAction(){ $client = $this->_client; $request = new Zend_XmlRpc_Request(); $request->setMethod('deleteExistingTask'); $request->setParams(array ( 6, // ID 2, // user ID )); $result = null; $errors = array (); try { $client->doRequest($request); $result = $client->getLastResponse()->getReturnValue(); if ($client->getLastResponse()->isFault()) { $result = $client->getLastResponse()->getFault(); } } catch (Zend_XmlRpc_Client_FaultException $e) { $errors['fault'] = "[{$e->getCode()}]: {$e->getMessage()}"; } catch (Zend_XmlRpc_Client_HttpException $e) { $errors['http'] = "[{$e->getCode()}]: {$e->getMessage()}"; } catch (My_Api_Timesheet_Exception $e) { $errors['api'] = "[{$e->getCode()}]: {$e->getMessage()}"; }}
Zend_Soap
SOAP Server<?phpclass Time_SoapController extends Zend_Controller_Action{ public function indexAction() { $this->_helper->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); $options = array (); $server = new Zend_Soap_AutoDiscover(); $server->setClass('My_Api_Timesheet'); $server->handle(); }}
SOAP Client<?phpclass Time_SoapClientController extends Zend_Controller_Action{ protected $_client; public function init() { $this->_helper->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); $this->_client = new Zend_Soap_Client( 'http://www.demo.local/Time/soap?wsdl', array ()); }
public function indexAction() { $result = $this->_client->listTasks(2, 2); $this->getResponse()->setHeader('Content-Type', 'text/xml; Charset=UTF-8'); echo $this->_client->getLastResponse(); }…
SOAP Clientpublic function addAction() { Zend_Debug::dump($this->_client->registerNewTask( 2, 2, 3, '2010-03-30', '18:00:00', '23:30:00','Setting up SOAP')); } public function editAction() { Zend_Debug::dump($this->_client->editExistingTask( 7, 2, 2, 3, '2010-03-30', '18:00:00', '23:30:00','Testing SOAP')); } public function deleteAction() { Zend_Debug::dump($this->_client->deleteExistingTask(7, 2)); }}
Zend_Rest
REST Server<?phpclass Time_RestController extends Zend_Controller_Action{ public function indexAction() { $this->_helper->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); $server = new Zend_Rest_Server(); $server->setClass('My_Api_Timesheet'); $server->handle(); }}
REST Client<?php
class Time_RestClientController extends Zend_Controller_Action{ protected $_client; public function init() { $this->_helper->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); $this->_client = new Zend_Rest_Client('http://www.demo.local/Time/rest'); }
public function indexAction() { $this->_client->listTasks(2); Zend_Debug::dump($this->_client->get()); }}
Patterns ???
Conclusion
moving functionality out the controllerinto a library API
=saves time
1 api = XmlRpc + SOAP + REST + …
Recommended readingWeb Services EssentialsEthan Cerami
O’Reilly
Programming Web Services with XML-RPCSimon St. Laurent, Joe Johnston, Edd Dumbill, Dave Winer
O’Reilly
Recommended readingRESTful Web ServicesLeonard Richardson, Sam Ruby
O’Reilly
Programming Web Services with XML-RPCJames Snell, Doug Tidwell, Pavel Kulchenko
O’Reilly
We need you help !!!
Feedback is important
• find a bug ?- test it- report it- send a patch/fix• need a non-existing component- submit proposal• like Zend Framework- blog about it- talk about it• translations
ZF Bug hunt days
Zend Framework Bughuntdaysevery 3rd Thursday and Friday of the month
http://framework.zend.com/issuesIRC (irc.freenode.net) #zftalk.dev
prizes:recognition and appreciation of the community
Free subscription for 1 year on php|Architect magazineZend Framework t-shirt
ZF issue tracker
• When: June 10 - 12 2010• Where: Amsterdam RAI• Tickets: http://phpconference.nl• Early bird: ends April 30, 2010 (Queen’s Day)
• annual event• improve code coverage• test php core source code• improve quality of PHP
PHPB ENELUX
• When: May 29, 2010• Where: <to-be-defined>• Updates: • web: http://www.phpbenelux.eu• twitter: @phpbenelux
PHPB ENELUX
Thank you !
Slides on Slidesharehttp://www.slideshare.net/group/macqel
Give feedback on Joind.inhttp://joind.in/1263