the state of soap in php
DESCRIPTION
Presentation at PHP Barcelona 2009TRANSCRIPT
THE STATE OF SOAP IN PHP
David Zülke
David Zuelke
apologies
http://flic.kr/chrismetcalf/43098186/
http://flic.kr/waltjabsco/3598975570/
back from
ZendCon
✈
✈
✈
http://en.wikipedia.org/wiki/File:München_Panorama.JPG
Founder
Lead Developer
@dzuelke
WHAT IS SOAP?And How Did It All Start?
original plan
talk
dis is srs bsns
make
http://flic.kr/kevinsteele/230997861/
KittY NotIncluded
however
http://en.wikipedia.org/wiki/File:Flughafenkontrolle.jpg
risk
SHOOT MEIN THE FACE
WHAT IS SOAP?And How Did It All Start?
Data Exchange Protocol
XML-based
language independent
platform independent
typically used for RPC-style Web Services
zomg lol
ORIGINSA Brief (and Wildly Inaccurate) History Lesson
< 1998
XML-RPC
XML-RPC sucks
1998
Simple Object Access Protocol 1.0
2003
not really simple
renamed
Simple Object Access Protocol
SOAP
SOAP 1.2
GLOSSARYTransports, Messages and WSDL
SOAP TRANSPORTS
• Transports are used for message transmission
• Most important ones:
• HTTP/HTTPS
• SMTP
Amazon
100.000.000.000 SOAP requests
(per second)
Sharks
(with friggin’ laser beams attached to their heads)
Custom Socket Transport
MESSAGES
• Wrapped in an <Envelope>
• <Header>s and a <Body>
• Structure is identical for Request and Response
<?xml version="1.0" encoding="UTF‐8"?><SOAP‐ENV:Envelope xmlns:SOAP‐ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://agavi.org/sampleapp"> <SOAP‐ENV:Body> <ns1:getProductResponse> <product> <id>123456</id> <name>Red Stapler</name> <price>3.14</price> </product> </ns1:getProductResponse> </SOAP‐ENV:Body></SOAP‐ENV:Envelope>
but worry not
that’s the entire point of SOAP
WSDL document
describes
• the service
• the operations
• the data types
<?xml version="1.0" encoding="utf‐8"?><wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://agavi.org/sampleapp/types" xmlns:asa="http://agavi.org/sampleapp" name="AgaviSampleApplication" targetNamespace="http://agavi.org/sampleapp"> <wsdl:types> <xsd:schema xmlns:soap‐enc="http://schemas.xmlsoap.org/soap/encoding/" targetNamespace="http://agavi.org/sampleapp/types"> <xsd:complexType name="Product"> <xsd:sequence> <xsd:element name="id" type="xsd:int"/> <xsd:element name="name" type="xsd:string"/> <xsd:element name="price" type="xsd:float"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="ArrayOfProducts"> <xsd:complexContent> <xsd:extension base="soap‐enc:Array"> <xsd:attribute ref="soap‐enc:arrayType" wsdl:arrayType="tns:Product[]"/> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:schema> </wsdl:types> <wsdl:portType name="AgaviSampleApplicationPortType"> <wsdl:operation name="getProduct"> <wsdl:input message="asa:getProductRequest"/> <wsdl:output message="asa:getProductResponse"/> </wsdl:operation> <wsdl:operation name="listProducts"> <wsdl:output message="asa:listProductsResponse"/> </wsdl:operation> </wsdl:portType> <binding name="AgaviSampleApplicationBinding" type="asa:AgaviSampleApplicationPortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getProduct"> <soap:operation soapAction="http://agavi.org/sampleapp#getProduct"/> <wsdl:input> <soap:body namespace="http://agavi.org/sampleapp" use="literal"/> </wsdl:input> <wsdl:output> <soap:body namespace="http://agavi.org/sampleapp" use="literal"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="listProducts"> <soap:operation soapAction="http://agavi.org/sampleapp#listProducts"/> <wsdl:output> <soap:body namespace="http://agavi.org/sampleapp" use="literal"/> </wsdl:output> </wsdl:operation> </binding> <service name="AgaviSampleApplicationService"> <port name="AgaviSampleApplicationPort" binding="asa:AgaviSampleApplicationBinding"> <soap:address location="http://services.acme.com/soap.php"/> </port> </service> <wsdl:message name="getProductRequest"> <wsdl:part name="id" type="xsd:int"/> </wsdl:message> <wsdl:message name="getProductResponse"> <wsdl:part name="product" type="tns:Product"/> </wsdl:message> <wsdl:message name="listProductsResponse"> <wsdl:part name="products" type="tns:ArrayOfProducts"/> </wsdl:message></wsdl:definitions>
AN EXAMPLESo We Are All on the Same Page
$client = new SoapClient('http://acme.com/product.wsdl', array( 'exceptions' => true, 'trace' => true,));
try { var_dump($client‐>listProducts());} catch(SoapFault $e) { // here be dragons}
array 0 => object(stdClass)[3] public 'id' => int 8172401 public 'name' => string 'TPS Report Cover Sheet' (length=22) public 'price' => float 0.89 1 => object(stdClass)[4] public 'id' => int 917246 public 'name' => string 'Weighted Companion Cube' (length=23) public 'price' => float 129.99
by the way
I will not talk about non-WSDL modes
because using SOAP without a WSDL is stupid
SOAP CLIENTSIf You Want To Consume Services
OMNOMNOM SERVICE
BASICS
$client = new SoapClient( 'http://acme.com/product.wsdl', // URL to WSDL describing the service array( // array of additional options for the client 'exceptions' => true, // throw SoapFault exceptions on errors 'trace' => true, // allow use of SoapClient::__getLast…() ));
$allProducts = $client‐>listProducts(); // $allProducts contains return value
GETTING AVAILABLE FUNCS
$client = new SoapClient('http://acme.com/product.wsdl', array( 'exceptions' => true, 'trace' => true,));
var_dump($client‐>__getFunctions());
array 0 => string 'Product getProduct(int $id)' (length=27) 1 => string 'ArrayOfProducts listProducts()' (length=30)
GETTING AVAILABLE TYPES
$client = new SoapClient('http://acme.com/product.wsdl', array( 'exceptions' => true, 'trace' => true,));
var_dump($client‐>__getTypes());
array 0 => string 'struct Product { int id; string name; float price;}' (length=55) 1 => string 'Product ArrayOfProducts[]' (length=25)
ADVANCED CONCEPTSFaults, Headers and Mappings
FAULT HANDLING
$client = new SoapClient('http://acme.com/product.wsdl', array( 'exceptions' => true, 'trace' => true,));
try { $newThing = $client‐>createProduct(new stdClass());} catch(SoapFault $e) { // could be a client‐side fault e.g. if fields are missing // or a server‐side fault if the server had any objections :)}
SOAP HEADERS
$client = new SoapClient('http://acme.com/product.wsdl', array( 'exceptions' => true, 'trace' => true,));
$client‐>setSoapHeader( new SoapHeader('http://acme.com/soap/products', 'username', 'Chuck Norris'));$client‐>setSoapHeader( new SoapHeader('http://acme.com/soap/products', 'password', 'r0undh0usek!ck'));
// headers will be sent along with the request$allProducts = $client‐>listProducts();
CLASSMAPS
class Product { protected $id, $name, $price; // imagine getters and setters here}
$client = new SoapClient('http://acme.com/product.wsdl', array( 'exceptions' => true, 'trace' => true, 'classmap' => array( 'Product' => 'Product', // no namespace here, which can be problematic… ),));
var_dump($client‐>getProduct(123456));
object(Product)[2] protected 'id' => int 123456 protected 'name' => string 'Red Stapler' (length=11) protected 'price' => float 3.14
TYPEMAPS
• Used for custom serialization and deserialization
• Needs two callbacks:
• one for XML->PHP conversion
• one for PHP->XML conversion
• Only necessary in very, very rare cases
SOAP SERVERSSlightly More Complicated
class ProductService { public function getProduct($id) { // witchcraft here return $product; } public function listProducts() { // more witchcraft here return $products; }}
$server = new SoapServer('/path/to/local/products.wsdl', array(/* options… */));
// register a class to instantiate that has all the methods$server‐>setClass('ProductService');// alternative: use an existing instance// $server‐>setObject(new ProductService());
// rock and roll$server‐>handle();
A BASIC SERVER
you can also register functions instead of class methods
wanna know how?
RTFM :X
class ProductService { public function getProduct($id) { // witchcraft here return $product; } public function listProducts() { // more witchcraft here return $products; } public function username($value) { // check if it's really chuck norris } public function password($value) { // check if he did a roundhouse kick }}
$server = new SoapServer('/path/to/local/products.wsdl', array(/* options… */));// register a class to instantiate that has all the methods$server‐>setClass('ProductService');// rock and roll$server‐>handle();
DEALING WITH HEADERS
class ProductService { public function getProduct($id) { if($product = ProductFinder::retrieveById($id)) { return $product; } else { return new SoapFault('Server', 'No such product'); } } public function listProducts() { // more witchcraft here return $products; }}
PRODUCING FAULTS
class ProductService { public function getTwoThings() { // rocket science here return array($foo, $bar); }}
MULTI-PART RETURN VALUES
LITTLE SECRETSDid You Know That ext/soap Supports...
<complexType name="ArrayOfProducts"> <element name="products" type="foo:Product" maxOccurs="unbounded" /></complexType>
{http://xml.apache.org/xml-soap}Map
LITTLE DISAPPOINTMENTSThings That ext/soap Does Not Support...
DateTime objects
document/literal wrapped
DOS AND DON’TSKeep This in Mind
enable the SOAP_SINGLE_ELEMENT_ARRAYS feature
don’t use SoapServer::fault()
use the exceptions option
double-check soap_use_error_handler()
don’t use cookies or other forms of state, ever
FRAMEWORK HIGHLIGHTSZend Framework & Agavi
ZEND FRAMEWORK
• Zend_Soap_Client as a wrapper for SOAPClient
• Zend_Soap_Server as a wrapper for SOAPServer
• Zend_Soap_Wsdl for constructing WSDL documents
• Zend_Soap_Autodiscover for automatic WSDL generation
Zend_Soap_Autodiscover generates WSDLs for you!
using PHPDoc comments
class AcmeProductService { /** * @param int The ID of the product. * * @return Product The product object. * * @deprecated Call Joe from sales if you want to know details about a product… */ public function getProduct($id) { // witchcraft goes here return $product; }}
$autodiscover = new Zend_Soap_AutoDiscover();$autodiscover‐>setClass('AcmeProductService');$autodiscover‐>handle(); // dumps a WSDL
also very nice for prototyping
but might get difficult with complex stuff
AGAVI
• Re-use existing Actions for SOAP Services
• Needs some information about the service in WSDL format
• WSDL auto-generated by the Routing
• Requires basic knowledge of XML Schema and WSDL
• Supports Document/Literal Wrapped for Servers
Demo
SOAP VERSUS RESTYour Thoughts Please
!e End
Questions?
THANK YOU!
• Shoot me an E-Mail: [email protected]
• Follow @dzuelke on Twitter
• Slides will be online at http://talks.wombert.de/