service component architecture for php · • components use php annotations both to declare their...

Post on 11-Jul-2020

12 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Service Component Architecture for PHP

Reusable components and effort-free web servicesMatthew Peters, IBM Hursley Park

matthew_peters@uk.ibm.com

1

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

2

How to find us

• Google for OSOA

• (Open Service Oriented Architecture)

3

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

4

SWG AB IncubatorsService Component Architecture

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.

5

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)

JSON-RPC binding

A component,

containing

business logic

A local component,

Same call stack

A web service

2. Be flexible about your dependencies1. Be flexible about

how you are called

Local binding

Web service binding

6

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

7

Scenario 1. Simplest

• A client script calling one local component

• What does the simplest SCA component look like?

GreetingComponentclient

8

Our first simple SCA component

• A PHP class

• @service annotation

• include for SCA.php

<?php

include 'SCA/SCA.php';

/*** @service*/class GreetingComponent{

public function greet($name) {

return 'hello ' . $name;}

}?>

GreetingComponent

9

Calling an SCA component from a client script

• Client script • Includes SCA.php• But is not itself a

component

• Uses SCA::getService()• getService takes a

path• Absolute or relative• Relative paths are

resolved against the location of the script

• getService returns a ‘proxy’ object:

• Enforces pass-by-value

<?php

include 'SCA/SCA.php';

$service = SCA::getService('./GreetingComponent.php');

echo $service->greet('PHP');

?>

GreetingComponentclient

"hello PHP"

10

Scenario 2. Multiple

• A local component calling other local components• How are the dependencies wired up?

ReversingComponent

ReversedGreeting

Component

GreetingComponent

client

"PHP olleh"

11

Add a second component

• Like GreetingComponent:• @service

• Include for SCA.php

<?php

include 'SCA/SCA.php';

/*** @service*/

class ReversingComponent{

function reverse ($in){

return strrev ($in);}

}

?>

ReversingComponent

12

Dependencies• Dependencies are declared• annotated with @reference

• The instance variable following will be assigned a proxy

• Hence needs to be public• Initialised before any

business logic

• @binding.php• indicates how to find the

component• and that it is local• same rules as getService

<?php

include 'SCA/SCA.php';

/*** @service*/class ReversedGreetingComponent{/**

* @reference* @binding.php GreetingComponent.php*/public $greeting_component;

/*** @reference* @binding.php ReversingComponent.php*/public $reversing_component;

public function greet($name){

$greeting = $this-> greeting_component->greet($name);return $this->reversing_component->reverse($greeting);

}}

?>

ReversedGreeting

Component

13

What have we got so far?

• Sample call stack:

ReversingComponent

ReversedGreeting

Component

GreetingComponent

client

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

SCA_Localproxy

breakpoint

"PHP olleh"

14

Scenario 3. Web Service

• A client script calling one remote component

• How to expose a web service binding

GreetingComponentclient

= SOAP Web service request/response

15

Exposing a web service binding• @binding

• Expose a web service binding

• Public methods are in the interface

• @param/@return• Need more information

about each method

<?php

include 'SCA/SCA.php';

/*** @service* @binding.ws*/

class GreetingComponent{

/*** @param string $name* @return string*/

public function greet($name) {

return 'hello ' . $name;}

}?>

GreetingComponent

= SOAP Web service request/response

16

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/GreetingComponent.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)

17

Generated WSDL

• Document/literal wrapped style• Message

formats are explicit within the schema

<?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><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://GreetingComponent"><xs:element name="greet">

<xs:complexType><xs:sequence>

<xs:element name=“name" type="xs:string" nillable="true"/></xs:sequence>

</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>

...

18

Generated WSDL

• message …

• post …

• binding …

...<message name="greetRequest"><part name="greetRequest" element="tns2:greet"/>

</message><message name="greetResponse"><part name="return" element="tns2:greetResponse"/>

</message><portType name="GreetingComponentPortType"><operation name="greet"><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>...

19

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 -->

20

Calling a remote SCA component from a script

• SCA::getService() takes the location of the WSDL• Once again, $service is a

proxy: SCA_SoapProxy

• Proxy contains within it an instance of the ext/SOAP client

• Location can be a URL…• In which case the soap

extension will probably cache it

• Location can be a path• Relative paths resolved

against location of script

<?php

include 'SCA/SCA.php';

$service = SCA::getService('http://www.example.com/GreetingComponent.wsdl');

echo $service->greet();

?>

Greeting

Componentclient

= SCA_SoapProxy

21

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>

22

Scenario 4. Multiple web service

• Everything separated

ReversingComponent

ReversedGreeting

Component

GreetingComponent

client

= SOAP Web service request/response

23

A second remote component

• Annotations• @binding• @param• @return

• And generate WSDL as before

<?php

include 'SCA/SCA.php';

/*** @service* @binding.ws*/

class ReverseComponent{

/*** @param string $in* @return string*/function reverse ($in){

return strrev ($in);}

}?>

ReversingComponent

24

Remote dependencies

• @binding.wsfor remote component

• locates wsdl

<?phpinclude 'SCA/SCA.php';

/*** @service* @binding.ws*/class ReversedGreetingComponent{/**

* @reference* @binding.ws GreetingComponent.wsdl*/public $greeting_component;

/*** @reference* @binding.ws ReversingComponent.wsdl*/public $reversing_component;/*** @param string $name* @return string*/

public function greet($name){

$greeting = $this-> greeting_component->greet($name);return $this->reversing_component->reverse($greeting);

}}?>

ReversedGreeting

Component

25

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

ReversingComponent

ReversedGreeting

Component

GreetingComponent

client

SOAP Web service request/response

26

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

38

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

39

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

40

41

A component exposing a JSON binding

• @binding.jsonrpc

• Generates .smd

• <url>?smd

• smd = service method description

<?phpinclude 'SCA/SCA.php';/*** @service* @binding.jsonrpc*/class HelloService{

/*** @param string $name The name to say hello to* @return string The string hello <name>*/public function sayHello ($name){

return ‘hello ‘ . $name;}

}?>

42

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"}

} ]}

43

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);

}

44

JSON-RPC - POST• POST Style - Request

• POST Style - Response

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}

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 "}

45

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

46

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

47

The end

top related