Download as pdf or txt
Download as pdf or txt
You are on page 1of 37

Service Component

Architecture for PHP


Reusable components and effort-free web services
Matthew Peters, IBM Hursley Park
matthew_peters@uk.ibm.com
Background
• IBM Hursley Park, Winchester, UK
• http://www5.ibm.com/uk/locations/hursley_explore.html
• 2-3K people
• Services, outsourcing
• Product development: CICS, MQSeries, Java
• Incubator group:
• Porting, experimenting with technologies from
Java world, implementing and simplifying in PHP

1
How to find us
• Google for OSOA
• (Open Service Oriented Architecture)

2
Agenda
• Two slide-overview
• Some simple SCA components (hello world–style)
• Components calling each other locally
• Make them all run as web services
• (Slides and Zend Studio)
• More SCA:
• Interoperability with other web services
• Exceptions
• Data Structures
• Work in progress: DOJO, JSON-RPC and SCA
• Summary, Futures and Links

3
Service Component Architecture
SWG AB Incubators
for PHP
• SCA for PHP allows a PHP programmer to write reusable
components (classes) in PHP, which can be called
either locally, or remotely via Web Services, with an
identical interface.

• Components use PHP annotations both to declare their


dependencies on other components, and to define the
interface which they expose as a service. Business logic
is kept separate from interface and dependencies.

• Deploying a PHP component as a web service can be as


simple as copying it into a web server’s document root.

4
Making your component reusable
• Do not entangle the business logic with the “wiring”

1. Be flexible about how you are called


• Expose as many ‘bindings’ as needed – make sure your business logic does not
need to know how it was called
2. Be flexible about your dependencies
• Declare the dependencies – but make sure your business logic does not need to
know how to resolve these
• Ideally get something else to “wire up” the components (Inversion of Control;
Dependency Injection patterns)

A local component,
Same call stack
Local binding

2. Be flexible about
A component,
1. Be flexible about Web service binding containing
your dependencies
how you are called business logic

JSON-RPC binding A web service

5
Four scenarios
1. One component called locally
2. One component calling two others
3. Make the single component expose a
Web service binding
4. Make them all use web services

• Same interface, minimal effort

6
Scenario 1. Simplest
• A client script calling one local
component
• What does the simplest SCA component
look like?

GreetingComponent
client

7
Our first simple SCA component
<?php

• A PHP class include 'SCA/SCA.php';

/**
• @service annotation * @service
*/
• include for SCA.php class GreetingComponent
{
public function greet($name)
{
return 'hello ' . $name;
}
}
?>

GreetingComponent

8
Calling an SCA component from a
client script
• Client script
• Includes SCA.php
• But is not itself a <?php
component
include 'SCA/SCA.php';
• Uses SCA::getService()
• getService takes a $service = SCA::getService('./GreetingComponent.php');
path
• Absolute or relative echo $service->greet('PHP');
• Relative paths are
resolved against the ?>
location of the script
"hello PHP"
• getService returns a
‘proxy’ object:
• Enforces pass-by-
value client GreetingComponent

9
Scenario 2. Multiple
• A local component calling other local
components
• How are the dependencies wired up?

GreetingComponent

client ReversedGreeting
Component ReversingComponent

"PHP olleh"

10
Add a second component
• Like GreetingComponent: <?php

include 'SCA/SCA.php';
• @service
• Include for SCA.php /**
* @service
*/

class ReversingComponent
{
function reverse ($in)
{
return strrev ($in);
}
ReversingComponent }

?>

11
Dependencies
<?php

include 'SCA/SCA.php';
• Dependencies are declared
• annotated with @reference /**
• The instance variable * @service
*/
following will be assigned a class ReversedGreetingComponent
proxy {
• Hence needs to be public /**
• Initialised before any * @reference
business logic * @binding.php GreetingComponent.php
*/
• @binding.php public $greeting_component;
• indicates how to find the
component /**
* @reference
• and that it is local * @binding.php ReversingComponent.php
• same rules as getService */
public $reversing_component;

public function greet($name)


ReversedGreeting {
Component $greeting = $this-> greeting_component->greet($name);
return $this->reversing_component->reverse($greeting);
}
}

?>
12
What have we got so far?
• Sample call stack:
reverse( $in ) C:\Program Files\Apache Group\Apache2\htdocs\Konferenz\ReversingComponent.php line 13
__call( $method_name, $arguments ) c:\php\PEAR\SCA\SCA_LocalProxy.php line 109
greet( $name ) C:\Program Files\Apache Group\Apache2\htdocs\Konferenz\ReversedGreetingComponent.php line 24
__call( $method_name, $arguments ) c:\php\PEAR\SCA\SCA_LocalProxy.php line 109
main( ) C:\Program Files\Apache Group\Apache2\htdocs\Konferenz\client2.php line 7

GreetingComponent
breakpoint

SCA_Local client ReversedGreeting


Component ReversingComponent
proxy

"PHP olleh"

13
Scenario 3. Web Service
• A client script calling one remote
component
• How to expose a web service binding

client GreetingComponent

= SOAP Web service request/response

14
Exposing a web service binding
<?php
• @binding include 'SCA/SCA.php';
• Expose a web service
binding /**
• Public methods are in the * @service
interface * @binding.ws
*/
• @param/@return
• Need more information class GreetingComponent
about each method {
/**
* @param string $name
* @return string
*/
public function greet($name)
{
GreetingComponent
return 'hello ' . $name;
}
}
?>

15
= SOAP Web service request/response
Generating the WSDL
• Generated in response to HTTP GET with ?wsdl
• http://www.example.com/GreetingComponent.php?wsdl
• Do it in a browser
• file_get_contents('http://www.example.com/GreetingCom
ponent.php?wsdl');
• Currently cached in the same directory as the
component
• http://www.example.com/GreetingComponent.wsdl
• (in future need to do something different to avoid
need for write access into htdocs)

16
Generated WSDL
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xsi:type="tDefinitions«
xmlns:tns2="http:// GreetingComponent"
xmlns:tns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns3="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
targetNamespace="http://GreetingComponent">
<types>
• Document/literal <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://GreetingComponent">
wrapped style <xs:element name="greet">
• Message <xs:complexType>
<xs:sequence>
formats are <xs:element name=“name" type="xs:string" nillable="true"/>
explicit within </xs:sequence>
the schema </xs:complexType>
</xs:element>
<xs:element name="greetResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="greetReturn" type="xs:string" nillable="true"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</types>
...
17
Generated WSDL
...
<message name="greetRequest">
<part name="greetRequest" element="tns2:greet"/>
• message … </message>
<message name="greetResponse">
<part name="return" element="tns2:greetResponse"/>
• post … </message>
<portType name="GreetingComponentPortType">
<operation name="greet">

• binding … <input message="tns2:greetRequest"/>


<output message="tns2:greetResponse"/>
</operation>
</portType>
<binding name="GreetingComponentBinding" type="tns2:GreetingComponentPortType">
<operation name="greet">
<input>
<tns3:body xsi:type="tBody" use="literal"/>
</input>
<output>
<tns3:body xsi:type="tBody" use="literal"/>
</output>
<tns3:operation xsi:type="tOperation" soapAction=""/>
</operation>
<tns3:binding xsi:type="tBinding" transport="http://schemas.xmlsoap.org/soap/http"
style="document"/>
</binding>
...

18
Generated WSDL
• Location attribute is decided once the file is in place
• Currently using the URL to determine the location with respect to the
document root
• (in future need to do something different to cope with proxies,
rewriting, firewalls)
• Ends with a distinctive comment
• Special handling of exceptions when one component talks to another

...
<service name="GreetingComponentService">
<port name="GreetingComponentPort" binding="tns2:GreetingComponentBinding">
<tns3:address xsi:type="tAddress" location="http://www.example.com/GreetingComponent.php"/>
</port>
</service>
</definitions>
<!-- this line identifies this file as WSDL generated by SCA for PHP. Do not remove -->

19
Calling a remote SCA component from
a script
• SCA::getService() takes the <?php
location of the WSDL include 'SCA/SCA.php';
• Once again, $service is a
$service = SCA::getService(
proxy: SCA_SoapProxy 'http://www.example.com/GreetingComponent.wsdl');
• Proxy contains within it an
echo $service->greet();
instance of the ext/SOAP
client ?>

• Location can be a URL…


• In which case the soap = SCA_SoapProxy
extension will probably
cache it
• Location can be a path client Greeting
Component
• Relative paths resolved
against location of script

20
Sample Soap request
• Document/literal wrapped, so <greet>
element enclosing <name> element
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<tns:greet xmlns= "http://GreetingComponent"
xmlns:tns= "http://GreetingComponent"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="greet">
<name>PHP</name>
</tns:greet>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

21
Scenario 4. Multiple web service
• Everything separated

GreetingComponent

ReversedGreeting
client Component ReversingComponent

= SOAP Web service request/response

22
A second remote component
<?php
• Annotations include 'SCA/SCA.php';
• @binding
/**
• @param * @service
• @return * @binding.ws
*/
• And generate WSDL as
before class ReverseComponent
{
/**
* @param string $in
* @return string
*/
function reverse ($in)
ReversingComponent {
return strrev ($in);
}
}
?>

23
Remote dependencies
<?php
include 'SCA/SCA.php';

• @binding.ws /**
* @service

for remote * @binding.ws


*/
class ReversedGreetingComponent
component {
/**
* @reference
• locates wsdl * @binding.ws GreetingComponent.wsdl
*/
public $greeting_component;

/**
* @reference
* @binding.ws ReversingComponent.wsdl
*/
public $reversing_component;
/**
* @param string $name
ReversedGreeting
Component * @return string
*/
public function greet($name)
{
$greeting = $this-> greeting_component->greet($name);
return $this->reversing_component->reverse($greeting);
}
}
24
?>
What have we got now?
• What have we achieved?
• Client <–> local <-> local
• Client <-> remote <-> remote
• What had to change?
• Arguments to getService(), or @binding.php to @binding.ws
• Annotatations to describe the interface in more detail
• Generating WSDL on demand; otherwise deployment is just copying the component
• But the business logic remains unchanged

SOAP Web service request/response

GreetingComponent

ReversedGreeting
client Component ReversingComponent

25
Interlude

• “But, you have to change the files


themselves…”
• True, but changing an annotation in an interpreted
file – is that different from a line in a config file?
• Essential point is that “wiring” and business
logic are separated
• Same file, but in different worlds
• Wiring is declarative, in annotations
• Business logic is imperative, in code

26
Futures
• Annotation overriding
• Changing service targets, bindings,
properties from outside
• PHP classes rather than xsds for data
structures
• Simple database services
• Other bindings
• Atompub, REST (XML and JSON), RSS

38
DOJO, JSON-RPC and SCA
• DOJO is a user interface widget set written in JavaScript
• Can talk back to the server asynchronously - AJAX style
• Can use JSON-RPC to do so
• JSON = JavaScript Object Notation
• Like a simplified XML
• Name/value pairs
• { for structure
• [ array
• A JSON-RPC interface can be defined in SMD = Service Method Description
• Like a simplified WSDL
• Also written in JSON

• SCA components can expose a JSON-RPC binding too

39
40
A component exposing a JSON
binding
• @binding.jsonrpc <?php
include 'SCA/SCA.php';
/**
• Generates .smd * @service
* @binding.jsonrpc
• <url>?smd */
class HelloService
{
• smd = service method /**
* @param string $name The name to say hello to
description * @return string The string hello <name>
*/
public function sayHello ($name)
{
return ‘hello ‘ . $name;
}
}
?>

41
HelloService.php?smd
• Defines a service that has:
• One method sayHello(), with …
• One parameter, name
{
"SMDVersion":".1",
"serviceType":"JSON-RPC",
"serviceURL":"http://localhost/Samples/JsonRpc/hello/HelloService.php",
"methods": [ {
"name":"sayHello",
"parameters": [ {
"name":"name",
"type":"string“
} ],
"return": {"type":"string"}
} ]
}

42
A DOJO function to call sayHello
• Obtain .smd
• Issue the call

function sayHello()
{
var SCA = new dojo.rpc.JsonService({smdUrl: "HelloService.php?smd"});
var inputfield = document.getElementById("hellotext").value;
SCA.sayHello(inputfield).addCallback(handleResponse);
}

43
JSON-RPC - POST
• POST Style - Request
POST /json-rpc/HelloService.php HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/json-rpc
Content-Length: 48
Pragma: no-cache
Cache-Control: no-cache

{"params":["Hello!"],"method":"sayHello","id":1}

• POST Style - Response


HTTP/1.1 200 OK
Date: Tue, 03 Oct 2006 18:14:35 GMT
Server: Apache/2.0.55 (Win32) PHP/5.2.0RC5-dev
X-Powered-By: PHP/5.2.0RC5-dev
Content-Length: 27
Keep-Alive: timeout=15, max=89
Connection: Keep-Alive
Content-Type: application/json-rpc

{“return":"Hello "}

44
Links
• SCA for PHP homepage
• http://osoa.org/display/PHP/SOA+PHP+Homepage
• Discussion Group
• http://groups.google.com/group/phpsoa/
• Blog
• http://www.ibm.com/developerworks/blogs/page/phpblog

• SDO for PHP


• http://www.php.net/sdo

45
Acknowledgements
• Other members of the SCA for PHP team
• Graham Charters, Megan Beynon, Chris
Miller, Caroline Maynard, Simon Laws
• Special thanks to Dmitry Stogov for help
with the SOAP extension, serialising and
de-serialising SDOs

46
The end

47

You might also like