What John has said is a very good explanation for the core of the Java
implementation -- unfortunately for you, what he covered isn't going to help
you write PHP code at all, because all of that fancy parallel processing
(with the exception of the dependency graph, which I'll get to) simply won't
work in the PHP world.

In a nutshell, this is how you process gadgets:

Step 1: Fetch the gadget spec XML. This *should* consult a cache first, to
minimize load on the gadget author's HTTP server. The spec does not say how
long to cache for (suggest 24 hours or less), but we do require that you
support bypassing / clearing the cache on your server somehow. The PHP
implementation should support the "nocache=1" query parameter to be
compatible with Java. Relevant Java classes: GadgetServer,
RemoteContentFetcher, GadgetDataCache

Step 2: Process the gadget spec. This is hard, especially since the spec
hasn't been published yet (it will be later this week, I'm told). I would
strongly recommend using SimpleXML and follow the same processing pattern as
you'll see in GadgetSpecParser. SimpleXML should make this pretty easy, and
you won't have to go through the pain of all that DOM nastiness that our
Java implementation had to deal with. Relevant Java classes:
GadgetSpecParser, GadgetSpec

Step 3: Fetch message bundles. These are fetched by inspecting the <Locale>
tags in the spec and making http requests for the bundle files. You should
get your locale from the "lang" and "country" query parameters. Processing
rules for locales have a few caveats, so examine MessageBundleSubstituter
carefully and emulate that behavior. The actual messagebundle spec is
extremely simple. Message bundles should be cached using the same caching
policy as Step #1. Relevant classes: MessageBundle, MessageBundleParser,
MessageBundleSubstituter

Step 4: Perform hangman variable substitutions. This is done by replacing
__UP, __MSG, __MODULE, and other placeholders with other values. You should
perform these substitutions in the following order:

__MSG
__BIDI
__MODULE
__UP

MSG and UP need to be html escaped to prevent XSS attacks. BIDI is a set of
constants (see BidiSubstituter or the spec, whenever it gets published, for
details). __MODULE only has one possible value right now: __MODULE_ID__,
which is the "mid" query parameter (an UNSIGNED integer). Only do a single
pass for each substitution type (you can use either a regex, or implement a
basic parser like we did), and do not revist types already processed (i.e.
don't substitute __MSG after you've already done __UP). Relevant classes:
Substitutions, MessageBundleSubstituter, BidiSubstituter, ModuleSubstituter,
UserPrefsSubstituter

Step 5: Process <Require> / <Optional> features. This is going to be hard to
do right now without the spec. We will be publishing this shortly, but what
you see in shindig right now is, for the most part, the complete set of what
will be in the initial "mandatory" support list. The good news is that most
of this work is already done for you. You need to implement support for
feature.xml loading and proper handling of <dependency>. See
trunk/features/README for details on the file format, and look at
JsFeatureLoader for an implementation. Combine this with the dependency
graph resolution in GagdetFeatureRegistry.getIncludedFeatures and you should
be good to go. Relevant classes: GadgetFeatureRegistry, JsFeature,
JsFeatureLoader

Step 6: Output gadget content. The order is as follows:

- standard html header (an ugly inline version can be seen in
GadgetRenderingServlet. This will be a lot cleaner in a PHP template).
Replicate this exactly for now (even the lack of a DOCTYPE).
- feature libraries (separate scripts, or ideally rolled up into a single
file or script block like we do in GadgetRenderingServlet)
- Initialization of feature libraries (currently only gadgets.ifpc_,
gadgets.Prefs, and gadgets.io need this)
- processed gadget code from step 4.
- a single call to gadgets.util.runOnLoadHandlers
- close your html

Ideally, all of the output from step 6 (except the html body wrapper) should
be passed through Caja, but right now that's going to be difficult to do in
PHP. Don't worry about it for now, I'll have a solution for you shortly.

This implements what we have on gmodules.com. Ideally, everything up to and
including step 5 should be able to be done directly from some library
similar to GadgetProcessor, and the wrapper of Step 6 should happen in the
code that specifically handles outputting iframe content. I *strongly*
recommend using curl_multi in implementing containers to fetch all of the
gadget data in parallel, then caching that so that when the iframes load,
they will be loading entirely from cache (which will be fast). This is
really the only part of the whole pipeline that needs to be run in parallel,
due to the fact that http fetching is slow.

I expect that the steps I have just described will more or less become the
companion document to the XML schema for the gadget spec when it gets
published later this week. John is responsible for making that happen, so
please send him feedback if you feel that there is any ambiguity in the
processing rules.

Let me know if I left anything out.

~Kevin

Reply via email to