Some of posts from this blog has been moved to dywicki.pl. You will be automatically redirected to new blog if you would submit comment.
New posts are published on dywicki.pl, this blog contains old content and it is not continued.

Niektóre posty z tego bloga zostały przeniesione do dywicki.pl. Zostaniesz automatycznie przekierowany jeśli bedzięsz chciał dodać komentarz.
Nowe posty sa publikowane na dywicki.pl, ten blog zawiera stare treści i nie jest kontynuowany.

Weź do Javy to co chcesz z PHP!

Filed under Java,JAXB,PHP,XML by

Integracja między językami czy też platformami to kwestia poruszana nie od dzisiaj. Na poziomie platform funkcjonuje od dłuższego czasu CORBA i Web Services z trio SOAP + WSDL + XML Schema na czele. Integracja systemów napisanych w tym samym języku sprowadza się zwykle do wykorzystania serializacji, która jest najszybsza i najwygodniejsza. Gorzej jeśli idzie o połączenie dwóch języków – w moim przypadku PHP i Javy.
Zend ma swój mostek, który umożliwia na zintegrowanie Javy i PHP, jest też dodatkowe rozszerzenie do PHP, które pozwala na wykorzystanie Javy w PHP, jednakże moje oczekiwania nie był aż tak wielkie. Potrzebowałem po prostu odczytać dane specyficzne dla PHP – powiedzmy informacje o jakiejś klasie. Standardowo taka operacja wymagała stworzenia parsera, co jest zadaniem powiedzmy, nie na moje siły i umiejętności.. stąd też postanowiłem sobie nieco uprościć pracę. :)

Wspólny, najwygodniejszy format (zarówno w odczycie i zapisie danych) z jednej i drugiej strony to oczywiście XML. Problem w tym, jaki format ma być wykorzystany. Nie da się przecież bezpośrednio odwzorować obiektu z PHP do Javy głównie z racji na dynamikę. Jeśli w PHP ktoś dorzuci pole do obiektu, poprzez proste $someUser->city = ‘Białystok’ to Java bazująca tylko na statycznych, zadeklarowanych polach w klasie nie odczyta tej informacji. Serializacja w postaci specyficznej dla PHP również wiąże się ze stworzeniem parsera po stronie Javy by to wszystko obsługiwać i dodatkowo coś co by później mapowało obiekty z Javy do XMLa w postaci przyjaznej dla PHP. Wyjściem z całej sytuacji okazały się funkcje wddx_*. Po prostu strzał w dziesiątkę. WDDX to standard może nie najnowszy, ale dosyć spójny, i co najważniejsze umożliwiający przesyłanie złożonych obiektów bez zbytniej walki. Po chwili poszukiwań znalazłem DTD, zatem ze strony Javy wystarczy odpalić JAXB i jesteśmy na miejscu.

Przykładowy skrypt PHP, który uzyskuje informacje o konfiguracji Agavi:

< ?php
include_once 'E:/htdocs/shop/agavi/agavi.php';
$value = wddx_serialize_value(AgaviConfig::export());

$value = "<?xml version='1.0' encoding='utf-8' ?>\n< !DOCTYPE wddxPacket SYSTEM 'wddx.dtd'>\n" . $value;

echo $value;

A teraz część wyniku, który PHP wyświetla w konsoli:

< ?xml version="1.0" encoding="utf-8" ?>
< !DOCTYPE wddxPacket SYSTEM 'wddx.dtd'>
<wddxpacket version='1.0'>
	<header />
	<data>
		<struct>
			<var name='core.minimum_php_version'>
				<string>5.1.0</string>
			</var>
			<var name='core.agavi_dir'>
				<string>E:\htdocs\shop\agavi</string>
			</var>
			<!-- i tak dalej -->
		</struct>
	</data>
</wddxpacket>

Teraz kod Javy, który odczytuje sobie informacje.. (nawiasy kwadratowe przy listach podyktowane błędami w skrypcie, który koloruje składnię)


		// odpalamy interpreter PHP
		Runtime runtime = Runtime.getRuntime();
		Process exec = runtime.exec("php -q E:/agavi-ide/org.codehouse.bridge/src/org/codehouse/bridge/test2.php");

		// podnosimy kontekst JAXB
		JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
		// deserializujemy XML wygenerowany przez PHP
		WddxPacket object = (WddxPacket) context.createUnmarshaller().unmarshal(exec.getInputStream());

		// odczytujemy informacje
		for (Object stc : object.getData().getWDDXData()) {
			// spodziewamy się informacji o typie złożonym
			if (stc instanceof Struct) {
				List[generated.Var] vara = ((Struct) stc).getVar();
				for (Var value : vara) {
					// pozostaje nam tylko odczytanie zserializowanej wartości
					List[Object] configurationValue = value.getWDDXData();
					System.out.println(value.getName() + ": " + ((generated.String) configurationValue.get(0)).getvalue());
				}
			}
		}

Wynik działania poniższego kodu to:

core.minimum_php_version: 5.1.0
core.agavi_dir: E:\htdocs\shop\agavi
exception.default_template: E:\htdocs\shop\agavi/exception/templates/shiny.php
agavi.name: Agavi
agavi.major_version: 0
agavi.minor_version: 11
agavi.micro_version: 0
agavi.status: DEV
agavi.branch: trunk
agavi.version: 0.11.0-DEV
agavi.release: Agavi 0.11.0-DEV
agavi.url: http://www.agavi.org
agavi_info: Agavi 0.11.0-DEV (http://www.agavi.org)

Czyli wszystko czego trzeba było nam do szczęścia! :)

Pora na przykład bardziej złożony, wyciągnięcie informacji o jakiejś klasie widoku użytej w aplikacji opartej o Agavi.

< ?php

include_once 'E:/htdocs/shop/agavi/agavi.php';

AgaviConfig::set('core.app_dir', 'E:/htdocs/shop/project/');
AgaviConfig::set('core.system_config_dir', 'E:/htdocs/shop/agavi/config/defaults/');

// metoda dopisana przeze mnie, konfiguruje Agavi bez wyrzucania wyjątków.
Agavi::bootstrap2('');

include_once 'E:/htdocs/shop/project/modules/Cart/lib/BaseInputView.class.php';

$obj = new ReflectionClass('Cart_BaseInputView');
$orig = $obj->getFileName();

$myMethods = $obj->getMethods();

$methods = array();

foreach ($myMethods as $method) {
	// sprawdzenie czy plik w którym jest zadeklarowana metoda
	// pokrywa się z plikiem w którym jest zadeklarowana klasa
	// w ten sposób pozbywam się niepotrzebnych metod z klas nadrzędnych
	if ($method->getFileName() == $orig) {
		$methods[] = $method;
	}
}


$value = wddx_serialize_value($methods);
$value = "< ?xml version='1.0' encoding='utf-8' ?>< !DOCTYPE wddxPacket SYSTEM 'wddx.dtd'>\n" . $value;
echo $value;

?>

Drobna przeróbka kodu Javy, który był użyty wcześniej i wynikiem jest

php_class_name: ReflectionMethod
name: execute
class: Cart_BaseInputView

Tym, którzy dobrnęli do końca tej noty dziękuję za wytrwałość. :) Kolejna nota będzie być może o tym jak wywoływać metody statyczne klas Javy z poziomu XSLT (wierzcie mi, da się!).

2 responses so far

2 Responses to “Weź do Javy to co chcesz z PHP!”

  1. Elf says:

    Zastanawiałem się nad efektywną i przenośną metodą serializacji obiektów, dokładniej rzecz ujmując atrybutów tychże. SDO oraz DIY Framework (http://www.xml.lt/Resources/Framework) skłoniły mnie do zastanowienia się nad wykorzystaniem XML. Jednak nie chciało mi się “wymyślać” XML Schema do tego. WDDX wydaje się tu całkiem niezłym wyjściem, ale jakoś nie podoba mi się jego składnia.

    Myślałem także nad serializacją do RDF/XML. Model danych RDF całkiem nieźle pasuje do zapisu obiektowego, standard jest znany i dobrze udokumentowany. Konsultowałem się nawet z twórcami wspomnianego DIY FW nad wykorzystaniem jako domyślny format serializacji, ale stwierdzili że RDF/XML jest zbyt skomplikowany dla przetwrzania XSLT. Cóż, chyba mają rację.

    Rozważałeś może wykorzystanie JSON? Wydaje się lżejszym i efektywniejszym formatem. Chyba nie tylko ja tak myślę: http://www.bloginblack.de/archives/000766.cfm

  2. Składnia WDDX rzeczywiście jest nieco pokręcona. Nie ma na przykład rozróżnienia czy coś jest tablicą asocjacyjną czy obiektem – w WDDX jest to po prostu “struct”. O wyborze tego a nie innego sposobu zadecydowała prostota. By zapiąć wddx do pracy po stronie PHP nie muszę nic robić. W przypadku bardziej złożonych technik takich jak RDF potrzebne są biblioteki zarówno po stronie PHP jak i Javy.
    Fakt faktem, że RDF jest potężniejszy niż zwykły XML.
    JSON – podobny problem jak w przypadku RDF – dodatkowe biblioteki. Chociaż faktycznie ze wszystkich trzech technik czy też technologii jest to rozwiązanie najlżejsze.
    Myślę, że w pierwszej kolejności przyjrzę się RDF i temu co można z niego wykrzesać. :)

Leave a Reply

You must be logged in to post a comment.