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

The State of SOAP in PHP (IPC2009 2009-11-18)

Avatar for David Zuelke David Zuelke
November 18, 2009

The State of SOAP in PHP (IPC2009 2009-11-18)

Presentation given at International PHP Conference 2009 in Karlsruhe, Germany.

Avatar for David Zuelke

David Zuelke

November 18, 2009
Tweet

More Decks by David Zuelke

Other Decks in Programming

Transcript

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

    Most important ones: • HTTP/HTTPS • SMTP
  2. MESSAGES • Wrapped in <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>
  3. <?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"/>
  4. $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
  5. 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
  6. 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)
  7. 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)
  8. 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 :) }
  9. 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();
  10. 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 XML 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
  11. TYPEMAPS • Used for custom serialization and unserialization in rare

    cases • Example: • xsd:long is mapped to PHP int, overflows on 32bit archs • Needs two callbacks: • one for XML->PHP conversion • one for PHP->XML conversion
  12. TYPEMAP EXAMPLE: XS:LONG function to_long_xml($longVal) { return '<long>' . $longVal

    . '</long>'; } function from_long_xml($xmlFragmentString) { return (string)strip_tags($xmlFragmentString); } $client = new SoapClient('http://acme.com/products.wsdl', array( 'typemap' => array( array( 'type_ns' => 'http://www.w3.org/2001/XMLSchema', 'type_name' => 'long', 'to_xml' => 'to_long_xml', 'from_xml' => 'from_long_xml', ), ), )); you can use any name for this wrapper tag
  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($product1, $product2); } } MULTI-PART RETURN VALUES <wsdl:message name="getTwoThingsRequest"> <wsdl:part name="id1" type="xsd:int"/> <wsdl:part name="id2" type="xsd:int"/> </wsdl:message> <wsdl:message name="getTwoThingsResponse"> <wsdl:part name="firstThing" type="tns:Product"/> <wsdl:part name="secondThing" type="tns:Product"/> </wsdl:message> list($p1, $p2) = $client‐>getTwoThings($id1, $id2));
  17. <complexType name="ArrayOfProducts"> <element name="products" type="foo:Product" maxOccurs="unbounded" /> </complexType> XML SCHEMA

    ARRAYS <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> =
  18. HASHMAPS (W/ STRING KEYS) <xsd:schema xmlns:apache‐enc="http:// xml.apache.org/xml‐soap"> <xsd:complexType name="MyHashmap"> <xsd:element

    name="parameters" type="apache‐enc:Map" /> </xsd:complexType> </xsd:schema> • Custom Apache Axis encoding style • be aware that it won’t offer good interoperability • Use any type for values, scalars for keys • Keys and values will use RPC encoding in the message
  19. ONE-WAY CALLS • SOAP Operations may have only a request,

    without a response declared • Both SoapClient and SoapServer will close the connection as soon as they can when calling such a method http://flic.kr/99996581@N00/1122331674/
  20. XSD:DATETIME TYPE MAP function to_datetime_xml(DateTime $dateTime) { return '<dateTime>'.$dateTime‐>format('Y‐m‐d\TH:i:sP').'</dateTime>'; }

    function from_datetime_xml($xmlFragmentString) { return new DateTime(strip_tags($xmlFragmentString)); } $client = new SoapClient('http://acme.com/products.wsdl', array( 'typemap' => array( array( 'type_ns' => 'http://www.w3.org/2001/XMLSchema', 'type_name' => 'dateTime', 'to_xml' => 'to_datetime_xml', 'from_xml' => 'from_datetime_xml', ), ), ));
  21. DOCUMENT/LITERAL WRAPPED • Document style services yield messages that can

    be validated against the WSDL’s XML Schema • Unlike with RPC style services, such messages won’t contain the name of the called method anymore • Solution: wrap the message payload in an element that has the same name as the procedure you want to call • Not a problem in PHP, but you need to wrap/unwrap yourself
  22. 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 auto WSDL generation
  23. 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(); // only dumps a WSDL, does not start a server!
  24. 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
  25. THANK YOU! • Shoot me an E-Mail: [email protected] • Follow

    @dzuelke on Twitter • Slides will be on SlideShare