PuSH deel 3
De PuSH subscriber software zou op dezelfde machine kunnen draaien als de OBA website, maar omdat het shared hosting is en de memory-limiet regelmatig overschreden werd, hebben we besloten het op een andere machine te laten draaien en de verzamelde berichten door te sturen naar OBA. ( een andere reden was, dat de host waar OBA nu op draait niet alle software beschikbaar stelt die nodig was. ) Ik heb dus een onafhankelijk van wordpress draaiende client gebouwd, die de berichten vervolgens doorstuurt met XML-RPC. RPC staat voor Remote-Procedure-Call, en het is een manier om dingen die je normaal binnen 1 computer doet te distribueren over meerdere computers. XML betekent dat er XML gebruikt wordt voor het uitwisselen van berichten tussen de ene en de andere computer, waar als het allemaal binnen 1 computer zou plaatsvinden geen codering nodig zou zijn. De XML-RPC interface van wordpress maakt het mogelijk WordPress beheertaken vanaf een andere computer uit te voeren. En je kan dus ook een stukje schrijven op een andere computer, en dat via XML-RPC publiceren op je blog. De eerste stap was dus, om m.b.v. een bestaande client te proberen of ik iets kon publiceren op mijn eigen blog. ( ik heb een kopie van de OBA software op een testblog )
Die client was niet meer dan een veredeld kladblok met een “settings” menu waarop ik mijn blognaam, userid en password kon invullen. Dat werkte, dus XML-RPC functioneert. Vervolgens ben ik op zoek gegaan naar wat er al was aan open-source PuSH client software. Ik vond een aantal interessante tools, geschreven in Python, waarmee je zelf een hub kon maken en uitproberen. Interessant, maar niet erg nuttig voor mijn doel. Maar ik vond ook een client library in PHP. Zelf toevoegen : storage-backend, logging, etc., de intelligentie die nodig is voor de afhandeling van het PuSH protocol zat er al in. Hij was geschreven als een paar PHP classes, waarbij je zelf 2 interfaces moest implementeren. Dat klonk niet al te ingewikkeld. Het PuSH protocol verwacht per subscription een unieke URL, waar de fat-pings d.m.v. een POSt request naartoe geschreven konden worden. Woeps.
Hoe zorg je dat 185 URL’s allemaal bij hetzelfde PHP-script uitkomen ? Door een .htaccess file te maken die de mod-rewrite extension van Apache aanspreekt. De URL is bijvoorbeeld : test.voorbeeld.nl/ping/12345 waarbij 12345 het variabele subscription nummer is. Het subdomein test.voorbeeld.nl wordt door Apache gemapped op de directory /var/www, dus zoekt Apache het php script op /var/www/ping/12345 Apache kijkt op elk directoryniveau of er een .htaccess file bestaat, en in /var/www/ping vindt hij er inderdaad een. De rewrite-rule in deze .htaccess zorgt er voor dat de url wordt herschreven tot test.voorbeeld.nl/push/pingme.php/?req=12345 Nu is het subscription-id dus een parameter van het script geworden en kan ik zien van wie de publicatie afkomstig is. Het enige moeilijke hieraan was, dat de gebruiksaanwijzing van Apache dikker is dan de bijbel, maar het werkt fluitend. Logging was niet zo moeilijk : Linux heeft een syslog-proces, dat zelf verschillende soorten logs open houdt en zorgt dat er een nieuw log geopend wordt als de vorige vol is. De “storage-backend” is 1 tabel in een database. De functie van de storage-backend is : zorgen dat een PHP object persistent wordt ( = opgeslagen en weer teruggelezen kunnen worden ). Dat is niet moeilijk : PHP kent de functie “serialize” , waarmee je een object serialiseert ( = omzetten in een string ). Je kan in MySQL geen objecten opslaan, maar strings natuurlijk wel. Het enige probleem dat ik ben tegengekomen was, dat de documentatie van de functiebibliotheek nogal vaag was met betrekking tot de eisen die aan storage gesteld werden. Het PuSH protocol werkt met vrij lange “secret keys”. Omdat de databasevelden die ik ervoor bedacht had niet lang genoeg bleken te zijn, ging het abonneren op een feed steeds fout. De hub krijgt dan het verkeerde antwoord, en uiteindelijk zie je “subscription failed” staan. Het protocol kent geen reeksen foutcodes, maar “ik zal wel iets fout gedaan hebben” is
het begin van de oplossing. Wat je natuurlijk ook zelf moest maken, was een scherm om je subscriptions te kunnen administreren. Er moest wat HTML-code komen voor een formulier, waarmee je je subscritions kon toevoegen en beeindigen, en een lijstje dat alle subscriptions laat zien, met hun status. Dat lijstje is een HTML tabel die door PHP gegenereerd wordt vanuit de MySQL tabel die de PuSH subscriber functie gebruikt voor storage. Hiervoor is PHP ideaal. Ik kan geen programmeertaal bedenken waarmee het makkelijker is om zulke lijstjes in een browserwindow te krijgen. Ten opzichte van gecompileerde talen is PHP vaak dik en traag, maar het is een prima taal om dingen aan elkaar te lijmen. Als je tegen de onmogelijkheden van PHP aanloopt, is het altijd nog mogelijk een stored procedure in de database te stoppen, of een module aan Apache toe te voegen. Je kan ook PHP zelf uitbreiden met modules, of een PHP compiler gebruiken. Geen wonder dus, dat de taal waanzinnig populair is, hoewel het nadeel daarvan lijkt, dat het een minder professioneel imago heeft dan Java.
Toen de subscriptions het eenmaal deden, begonnen de feeds binnen te stromen. Het was de bedoeling dat de feeds uiteindelijk zouden eindigen als posts op de OBA website, maar daarvoor moest nog wel e.e.a. gebeuren. Eerst proberen of ik vanuit een PHP-script m.b.v. XML-RPC iets kon posten. Dat lukte wel, maar bij nadere beschouwinge bleek, dat het “date-published” veld door WordPress niet goed geïnterpreteerd werd. Ik zag het staan in de code, en de Apache errorlog bevestigde het : “trying to call a member function on a non-object”. Nogal logisch. Je kan van alles opsturen met XML-RPC, maar toch geen object ? Wat ik verder nodig had, was een soort “permalink”, de link naar het oorspronkelijke bericht, zoals OBA die nu ook toont. Dat was een absolute vereiste, maar XML-RPC negeert dat veld. Documentatie, zoals bij wordpress, beschrijft meestal wat een product wél kan. Je kan dagenlang zoeken naar features die er niet in zitten. Daarom is het fijn dat WordPress in PHP geschreven is. Een blik in de sourcecode leerde me dat permalinks of alles wat er voor door kon gaan niet aan het product zijn toegevoegd.
Fortunately, hangt WordPress aan elkaar van de “hooks”. XML-RPC bestaat uit 2 delen : Het deel dat XML en communicatie afhandelt en het deel dat de aangevraagde functies vertaalt naar WordPress internals. Die vertaling vindt plaats in een class, en de naam van die class kun je vervangen met een hook. De hele class zat in 1 file, dus ik kopieerde de file, veranderde er 3 regels aan, en schreef een basale plugin die niets anders deed dan mijn alternatieve class aan te roepen i.p.v. de originele. Hij deed het : ik mocht de publish-date invullen, en de guid/permalink. Beide zijn normaal onderdeel van een post, en het is me een raadsel waarom ze niet via XML-RPC aangepast konden worden. Nu nog zien dat ik die feeds kon inlezen. Er bestaan in PHP een aantal alternatieven voor het verwerken van XML. Ik kende ze al, want voor de VKblog importer had ik ze al bekeken. En ik vond op internet een blog van iemand die een keer een RSS feed had ingelezen, en daarvoor een aantal problemen had moeten oplossen. Het stukje las als mijn eigen aantekeningen : verrassingen, ongedocumenteerde features, onbegrijpelijke ontwerpbeslissingen uit het verleden. Hij gebruikt SimpleXML. Daar had ik ook weleens naar gekeken, maar voor het importeren van een webpagina was het niet zo geschikt. Zo, even knippen en plakken, dat scheelt weer een dag werk. Bingo. Je stopt er een RSS-feed in, en je krijgt als output een titel, timestamp, permalink en natuurlijk content. Tags en categories worden door RSS helaas op 1 hoop gegeooid. Alles wordt “category” Aan die content moest nog wel wat gesleuteld worden. Ik wilde de OBA webserver niet opzadelen met onnodig werk, dus die content wordt gestript. Comments, javascript, dat hebben we niet nodig. Een
,
of <span> omhulsel moet weg, want we willen allen de inhoud. Ik kap het rigoureus af bij 350 tekens, dus wil je aandacht, dan zul je in de eerste 3 regels iets interessants moeten vertellen. Wordpress en Blogspot bleken totaal verschillende feeds aan te leveren. WordPress levert RSS, waar mijn tool de weg in kende. Blogspot levert het meer gecompliceerde Atom formaat. Het bleek teveel gepruts te zijn om de 2 formaten door dezelfde functie te persen. In Atom heeft alles een andere naam of een ander formaat, en het staat op een andere plek. Het is makkelijker om aan de hele feed in te lezen in SimpleXML, en aan de hand van 1 kenmerk te beslissen welke functie het resulterende object mag verwerken. Beter leesbare code, ten koste van iets meer regels.Volgende stap : in plaats van schrijven naar het scherm, alles verpakken in een mooi XML-RPC request, en dat posten, was daarna niet zo moeilijk meer. callback Als je wat achtergrondinformatie leest over het PuSH protocol, kom je regelmatig het woord “callback” tegen. Een callback is niets anders dan een functienaam, die gebruikt wordt als argument voor een andere functie. Neem als voorbeeld een sorteerfunctie, die zou gebruik maken van een andere functie “vergelijk” om dingen te sorteren. Zo kun je met dezelfde sorteerfunctie uiteenlopende zaken als namen, bedragen of elk ander ding waarvan je de kenmerken kan vergelijken sorteren. Wordt vervolgd… Er volgen nog 1 of 2 delen over de praktische uitwerking van PuSH voor OBA.