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

The State of SOAP in PHP (PHPBNL2010 2010-01-30)

The State of SOAP in PHP (PHPBNL2010 2010-01-30)

David Zuelke

January 30, 2010
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