Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The State of SOAP in PHP (ZendCon2009 2009-10-24)

The State of SOAP in PHP (ZendCon2009 2009-10-24)

Presentation given at ZendCon 2009 in San Jose, CA, United States.

David Zuelke

October 24, 2009
Tweet

More Decks by David Zuelke

Other Decks in Programming

Transcript

  1. or

  2. SOAP TRANSPORTS • Transports are used for message transmission •

    Most important ones: • HTTP/HTTPS • SMTP
  3. 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>
  4. <?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"/>
  5. $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
  6. 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
  7. 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)
  8. 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)
  9. 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 :) }
  10. 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();
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. class ProductService { public function getTwoThings() { // rocket science

    here return array($foo, $bar); } } MULTI-PART RETURN VALUES
  17. 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
  18. 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
  19. 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
  20. THANK YOU! • Shoot me an E-Mail: [email protected] • Follow

    @dzuelke on Twitter • Slides will be online at http://talks.wombert.de/