<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Syntactic sugar &#187; PHP</title>
	<atom:link href="http://syntacticsugar.nl/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://syntacticsugar.nl</link>
	<description>Random ramblings of another web-techy</description>
	<lastBuildDate>Sat, 09 Jan 2010 21:24:13 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Finally : a new Crudder-release!</title>
		<link>http://syntacticsugar.nl/2009/11/28/finally-a-new-crudder-release/</link>
		<comments>http://syntacticsugar.nl/2009/11/28/finally-a-new-crudder-release/#comments</comments>
		<pubDate>Sat, 28 Nov 2009 12:02:06 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[CMS]]></category>
		<category><![CDATA[CRUD]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[PHP-Scaffold]]></category>
		<category><![CDATA[Scaffold]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=296</guid>
		<description><![CDATA[It took a while.. a long while actually.. but I finally made a new release of Crudder. This new release 0.50 has a lot of new features and bugfixes, to name a few;

New Field-type: HTML-editor
New Field-type: File-upload
New Field-type: Enum
Export to CSV
Adding records from the many-2-many-editor
Bugfixes; language-support works better, datefield has been fixed, the many-2-many-editor should [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_297" class="wp-caption alignright" style="width: 160px"><a href="http://syntacticsugar.nl/wp-content/uploads/2009/11/screenshot_001.png"><img class="size-thumbnail wp-image-297" title="screenshot_001" src="http://syntacticsugar.nl/wp-content/uploads/2009/11/screenshot_001-150x150.png" alt="Crudder in action" width="150" height="150" /></a><p class="wp-caption-text">Crudder in action</p></div>
<p>It took a while.. a long while actually.. but I finally made a new release of <a href="http://syntacticsugar.nl/crudder/">Crudder</a>. This new release 0.50 has a lot of new features and bugfixes, to name a few;</p>
<ul>
<li>New Field-type: HTML-editor</li>
<li>New Field-type: File-upload</li>
<li>New Field-type: Enum</li>
<li>Export to CSV</li>
<li>Adding records from the many-2-many-editor</li>
<li>Bugfixes; language-support works better, datefield has been fixed, the many-2-many-editor should work better</li>
</ul>
<p><a href="http://www.crudder.net/demo.php" target="_blank">Check out the DEMO</a> or <a href="http://syntacticsugar.nl/crudder/">read more</a></p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/11/28/finally-a-new-crudder-release/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Launched a new WPS-Demo website</title>
		<link>http://syntacticsugar.nl/2009/10/17/launched-a-new-wps-demo-website/</link>
		<comments>http://syntacticsugar.nl/2009/10/17/launched-a-new-wps-demo-website/#comments</comments>
		<pubDate>Sat, 17 Oct 2009 16:12:05 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[CMS]]></category>
		<category><![CDATA[Content Publishing]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WPS]]></category>
		<category><![CDATA[Content Management]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[Joomla]]></category>
		<category><![CDATA[Website Publication]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=266</guid>
		<description><![CDATA[For the dutchies , english folks check this page;
Op http://wps.i-v-o.nl/ staat sinds vandaag een vernieuwde demo-website van WPS.  Met deze demo-website kan je lezen over wat WPS is, wat het kan, hoe het werkt en je kan spelen met het systeem.
Wat is WPS
WPS staat voor Web Publishing System. Geen spannende afkorting, maar het dekt de [...]]]></description>
			<content:encoded><![CDATA[<p>For the dutchies , english folks check <a href="http://syntacticsugar.nl/wps/">this page</a>;</p>
<p>Op <a title="WPS demo website" href="http://demo.i-v-o.nl" target="_blank">http://wps.i-v-o.nl/</a> staat sinds vandaag een vernieuwde demo-website van WPS.  Met deze demo-website kan je lezen over wat WPS is, wat het kan, hoe het werkt en je kan spelen met het systeem.</p>
<h2>Wat is WPS</h2>
<p>WPS staat voor Web Publishing System. Geen spannende afkorting, maar het dekt de lading wel goed. WPS is namelijk geen klassiek Content Management Systeem (CMS) maar veel meer. Een klassiek CMS zorgt er eigenlijk alleen maar voor dat je de inhoud van je website kunt bewerken, dat doe je dan eigenlijk altijd op een aparte website (ook wel backend genoemd). Zo&#8217;n CMS is harstikke mooi, maar er moet ook een &#8216;voorkant&#8217;, je website, geprogrammeerd worden, en dat kan best een tijdrovende klus worden. WPS is een <em>publicatiesysteem</em> en zorgt dus niet alleen voor de CMS functie, maar ook voor het publiceren; de voorkant van je website.</p>
<p><span id="more-266"></span></p>
<h2>Publicatie-systeem</h2>
<p>Er zijn gerust wel andere publicatie-systemen, bijvoorbeeld Joomla of Wordpress, maar toch heeft I-V-O de keuze gemaakt om een heel nieuw systeem te ontwikkelen. Waarom? <strong>Flexibiliteit</strong>.<br />
De meeste andere systemen zijn nogal rigide in functionaliteit of opmaak, en dan nog maar niet te spreken over veiligheid. WPS is extreem flexibel door het gebruik van XML. Alle gegevens die in WPS rondgaan worden omgezet in XML en komen in 1 groot document samen; het WPSDoc. XML is een speciale manier van gegevens opmaak die zich uitermate goed leent tot &#8216;translaties&#8217;. Stel je voor dat je een tekst hebt ingevoerd en je wilt daar een deel van dikgedrukt maken; normaliter wordt dat in html aangegeven met de b-tag. In WPS wordt dit de bold-tag, niet &#8220;b&#8221;, en bij het publiceren van de pagina kan de programmeur dit transleren/vertalen naar de standaard &#8220;b&#8221;-tag, maar ook naar iets heel anders, bijvoorbeeld &#8220;strong&#8221; of &#8220;h1&#8243;.<br />
De taal die gebruikt wordt voor dit vertalen heet XSL-t, een soort HTML, maar dan met logica; je kan als programmeur o.a if-then-else, loops, en variabelen gebruiken. Hierdoor wordt de XML een stuk slimmer, en je site dus ook!</p>
<h2>Bewerken en beheren</h2>
<p>Wat WPS nog meer bijzonder maakt is de manier waarop je de teksten in je website kunt beheren; dit doe je namelijk gewoon <em>in</em> je site en niet op een aparte website of backend. Hierdoor zie je veel beter wat en waar je wat doet. Dit is veel gebruiksvriendelijker dan de standaard CMS&#8217;en en geeft veel beter weer wat de cohesie is tussen website en content.</p>
<h2>Plugins</h2>
<p>Een standaard WPS-site kan al heel veel;</p>
<ul>
<li>Pagina&#8217;s aanmaken, teksten maken en aanpassen</li>
<li>Meta-tags (voor zoekmachines) beheren</li>
<li>Linkjes in je teksten controleren of ze nog goed zijn</li>
<li>Bestanden (pdf&#8217;s, Word-documenten, plaatjes) uploaden</li>
<li>Een site in meerdere talen publiceren (en dat doet WPS heel slim want als je een pagina in het nederlands naar bijvoorbeeld het engels hebt vertaald dan weet WPS welke pagina&#8217;s bij elkaar horen, als een engels-sprekende op een nederlandse pagina komt kan hij/zij met 1 klik naar de engelse versie zonder de pagina opnieuw te moeten zoeken)</li>
<li>Rechten instellen per pagina</li>
</ul>
<p>Verder zijn er heel veel extra functies gemaakt in de vorm van &#8216;plugins&#8217;, een greep uit de collectie;</p>
<ul>
<li>Nieuws-plugin: zorgt voor het beheer en publicatie van nieuws-berichten (ook naar RSS!)</li>
<li>Poll-plugin: maak zelf een poll en zorg dat je bezoekers kunnen stemmen</li>
<li>Search-plugin : zorgt ervoor dat bezoekers kunnen zoeken in je website</li>
<li>Shop-plugin: volledige webwinkel, met betalingsmodule voor IDEAL en Mollie (micropayments)</li>
<li>Enquete-plugin: zeer uitgebreide plugin voor het maken van grote enquetes, compleet met uitnodigings-email-functie, toegangscodes en export-functies.</li>
<li>Catalogus/Portofolio-plugin: laat je bezoekers zien wat je verkoopt of wat je hebt gemaakt.</li>
<li>Download-beveiliging voor Audio: als je bijvoorbeeld muziek wilt laten horen vanaf je website via een zg. FlashPlayer dan zorgt WPS ervoor dat bezoekers niet stiekem de bestanden kunnen downloaden.</li>
<li>Google-Maps-plugin: zorgt ervoor dat je bezoekers op een kaartje kunnen zien waar je bedrijf is gevestigd</li>
<li>Reactie-plugin: geeft je bezoekers de mogelijkheid om korte reacties op je website te plaatsen (en ze kunnen ook op elkaar reageren)</li>
<li>PDF-output; maakt automatisch PDF-bestanden van de tekst op een pagina die bezoekers kunnen downloaden</li>
<li>Blog-Plugin: samenvoeging van de nieuws-plugin en de reactie-plugin</li>
<li>Gallery-plugin: maakt automatisch een mooie foto-gallery van foto&#8217;s</li>
<li>En in ontwikkeling; Google WAVE-integratie; een mogelijkheid om bezoekers via Google Wave te laten reageren op nieuws-berichten.</li>
</ul>
<p>Verder maakt WPS gebruik van slimme caching; je site zal altijd rete-snel blijven omdat alle veel gebruikte informatie in het geheugen van de webserver bewaard blijven, zodat ze snel weer voor handen zijn als het nodig is.</p>
<p>Voor meer informatie over WPS, <a title="WPS demo website" href="http://wps.i-v-o.nl" target="_blank">klikkerdeklik!</a></p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/10/17/launched-a-new-wps-demo-website/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Dev-Tip: Check your website from slow connections</title>
		<link>http://syntacticsugar.nl/2009/09/17/dev-tip-check-your-website-from-slow-connections/</link>
		<comments>http://syntacticsugar.nl/2009/09/17/dev-tip-check-your-website-from-slow-connections/#comments</comments>
		<pubDate>Thu, 17 Sep 2009 14:00:23 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[Apache]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=261</guid>
		<description><![CDATA[Have you ever tried one of your websites from a slow connection? Is load-time acceptable? Does the site work at all? If you use a lot of scripting and ajax-calls you might end up with a website that doesn&#8217;t work because scripts are timing out or wait for other scripts to finish (which even could [...]]]></description>
			<content:encoded><![CDATA[<p>Have you ever tried one of your websites from a slow connection? Is load-time acceptable? Does the site work at all? If you use a lot of scripting and ajax-calls you might end up with a website that doesn&#8217;t work because scripts are timing out or wait for other scripts to finish (which even could crash browsers). How do you test from a slow connection? Check out <a href="http://codee.pl/cband.html" target="_blank">mod_cband</a>; bandwidth throttling for Apache.</p>
<p>Installation is quite easy; there&#8217;s a <a href="http://www.howtoforge.com/mod_cband_apache2_bandwidth_quota_throttling" target="_blank">perfect howto available on Howtoforge</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/09/17/dev-tip-check-your-website-from-slow-connections/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Putting the Singleton-pattern to good use</title>
		<link>http://syntacticsugar.nl/2009/05/03/putting-the-singleton-pattern-to-good-use/</link>
		<comments>http://syntacticsugar.nl/2009/05/03/putting-the-singleton-pattern-to-good-use/#comments</comments>
		<pubDate>Sun, 03 May 2009 06:35:16 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[OOP]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Registry]]></category>
		<category><![CDATA[Singleton]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=167</guid>
		<description><![CDATA[I like the Singleton-pattern, it saves a lot of trouble on creating objects that you need throughout your code and need to keep their state. Though it&#8217;s very tempting, do not use the singleton too much; see here why.
There are some pretty good uses for a Singleton though; a registry and the database-connection;
Download example code [...]]]></description>
			<content:encoded><![CDATA[<p>I like the <a href="http://en.wikipedia.org/wiki/Singleton_pattern" target="_blank">Singleton-pattern</a>, it saves a lot of trouble on creating objects that you need throughout your code and need to keep their state. Though it&#8217;s very tempting, do not use the singleton too much; see <a href="http://www.sitepoint.com/blogs/2008/02/13/whats-so-bad-about-the-singleton/" target="_blank">here</a> why.</p>
<p>There are some pretty good uses for a Singleton though; a registry and the database-connection;</p>
<p><span id="more-167"></span><a href="http://syntacticsugar.nl/upload/singletons.zip">Download example code here</a></p>
<p><strong>Database-connection</strong></p>
<p>I use <a href="http://adodb.sourceforge.net/" target="_self">ADODB</a> to access databases, don&#8217;t ask me why, I just do. ADODB uses a DSN to connect to a database, which looks something like:</p>
<pre><code>mysql://dbUser:dbPass@localhost/someDb</code></pre>
<p>Normally you&#8217;d use <em>$connection = &amp;ADONewConnection($DSN)</em> to connect to the database and run your query (<em>$rs = $connection-&gt;execute($sqlQuery) </em>). The problem here; you keep creating new connections anywhere you run a query, you need to pass the value for $DSN into your objects,  you&#8217;ve made your class depend on ADODB and it take 2 lines of code <img src='http://syntacticsugar.nl/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />   You can bypass these &#8216;problems&#8217; by using a Singleton to connect to your database and run a query:</p>
<pre><code>$rs = basicDBConn::getConnection()-&gt;execute($sqlQuery);</code></pre>
<p>Now, what is wrong here? Did you see $DSN anywhere?<br />
Nope&#8230; This statement assumes you&#8217;ve passed $DSN already somewhere before, in fact the <em>first</em> time you call <em>getConnection()</em> from this singleton you must pass $DSN as argument in getConnection(). The first time you call getConnection() is the moment the singleton creates an instance of itself, and uses $DSN to connect to the database (<em>&amp;ADONewConnection($DSN)). </em>After this initialization the connection is kept within the Singleton, and $DSN isn&#8217;t needed anymore. Take a look at the files, basicDBConn.php in particular to get an idea of how the Singleton creates an instance of itself and the database-connection.</p>
<p><strong>Registry</strong></p>
<p>Another good use for Singletons is a Registry-object; A registry-object is used for configuration; global settings that you might need in your code (path&#8217;s, DB-configuration etc.). Since I&#8217;m infected by the XML-virus my configuration-files always are XML :</p>
<pre><code>&lt;config&gt;
    &lt;item index="DSN"&gt;mysql://dbUser:dbPass@localhost/singletondemo&lt;/item&gt;
    &lt;item index="someKey"&gt;someValue&lt;/item&gt;
&lt;/config&gt;
</code></pre>
<p>There is a drawback though; these XML-files are <span style="text-decoration: underline;">not</span> protected by Apache by default, anyone can open them from a browser (if they are somewhere in the public root of the website). There are 2 solutions for this; prefix the file with <em>.hta</em> (in which case Apache will protect them just as .htaccess files are protected) or add the following lines to your Apache-configuration :</p>
<pre><code>&lt;Files ~ "configuration_filename.xml"&gt;
   Order allow,deny
   Deny from all
&lt;/Files&gt;</code></pre>
<p>The registry-object works the same as the Database-object; it needs a reference to a XML-file <em>the first time</em> you call it, after that it&#8217;s fully initialized and doesn&#8217;t need that reference anymore.</p>
<p>The first call to a registry object (and get the value for &#8217;some_key&#8217;):</p>
<pre><code>$someValue = basicRegistry::getInstance('/path/to/configuration/file.xml')-&gt;get("some_key");</code></pre>
<p>Any call after that:</p>
<pre><code>$someValue = basicRegistry::getInstance()-&gt;get("some_key");</code></pre>
<p><strong>Why is a singleton a good thing?</strong><br />
Getting configuration-values or a $DSN-string from within objects should be simple, you do not want to pass the path and filename to your configuration file or $DSN to a database every time you create an instance of an object. It will create dependencies and spaghetti in your code. If you make sure the registry and/or database-object are created <em>before</em> your other objects start doing something you do not need to worry about file-names and/or $DSN&#8217;s. They have been safely put away in a singleton.</p>
<p>Another very nice side-effect ; you&#8217;ve decoupled database-connections and configuration-settings. In my examples I use XML to setup the values in the registry, if you want to use some other method of keeping key-value-pairs, just modify the inner workings of the Registry-singleton, the objects using the registry won&#8217;t notice and work just as well. I usually keep session-variables in the registry, if I can&#8217;t use PHP&#8217;s $_SESSION, I can fall back onto Cookies, my classes won&#8217;t notice.</p>
<p><a href="http://syntacticsugar.nl/upload/singletons.zip">Download example code here</a></p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/05/03/putting-the-singleton-pattern-to-good-use/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Creating your own RPC-interface in PHP</title>
		<link>http://syntacticsugar.nl/2009/04/17/creating-your-own-rpc-interface-in-php/</link>
		<comments>http://syntacticsugar.nl/2009/04/17/creating-your-own-rpc-interface-in-php/#comments</comments>
		<pubDate>Fri, 17 Apr 2009 17:53:48 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[OOP]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[DIY]]></category>
		<category><![CDATA[RPC]]></category>
		<category><![CDATA[RPC-Client]]></category>
		<category><![CDATA[RPC-server]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=142</guid>
		<description><![CDATA[Last week I got a request to create some kind of RPC-server-client in PHP.  The assignment was to create a flexible solution to allow remote updating several properties in a database. I&#8217;ve created interfaces like this before, but specifically for AJAX-calls to PHP-objects, not really for server-to-server-calls. This particular assignment also involved a PHP-client-object [...]]]></description>
			<content:encoded><![CDATA[<p>Last week I got a request to create some kind of RPC-server-client in PHP.  The assignment was to create a flexible solution to allow remote updating several properties in a database. I&#8217;ve created interfaces like this before, but specifically for AJAX-calls to PHP-objects, not really for server-to-server-calls. This particular assignment also involved a PHP-client-object for other developers to implement in their website. The RPC-interface actually doesn&#8217;t really care if it&#8217;s used for real server-to-server calls or AJAX-calls. However; in this article I&#8217;ll focus on server-to-server communication, if designed well it shouldn&#8217;t take too much effort to make it AJAX-compatible.</p>
<p><span id="more-142"></span><br />
<a href="http://syntacticsugar.nl/upload/rpc.zip">[download code]</a></p>
<p>This is how these RPC-calls are done:</p>
<p><img class="aligncenter size-full wp-image-147" title="RPC-interface structure" src="http://syntacticsugar.nl/wp-content/uploads/2009/04/diagram11.png" alt="RPC-interface structure" width="650" height="613" /></p>
<p>Since I like XML a lot, I wanted to use XML as transport mechanism for doing requests to the RPC-interface. Because I use XML I was also able to group RPC-calls into one single request. A simple method of authenticating these RPC-calls is something to consider if you&#8217;re not running a public service, but that&#8217;s beyond the scope of this article</p>
<p>A typical call would look like this:</p>
<pre><code>&lt;command&gt;
	&lt;method name=”remote_method” object=”some_object”&gt;
		&lt;arg key=”some_argument”&gt;value_of_some_argument&lt;/arg&gt;
		&lt;arg key=”another_argument”&gt;value_of_another_argument&lt;/arg&gt;
	&lt;/method&gt;
	&lt;method name=”another_remote_method” object=”another_object”&gt;
		&lt;arg key=”some_argument”&gt;value_of_some_argument&lt;/arg&gt;
		&lt;arg key=”another_argument”&gt;value_of_another_argument&lt;/arg&gt;
	&lt;/method&gt;
&lt;/command&gt;</code></pre>
<p>Now, what does this XML actually mean?</p>
<p>First of all; the methods need to be nested into a single node: &lt;command&gt;, the XML would not be valid otherwise. Each method-node has an name-attribute and object-attribute, the object-attribute defines the remote PHP-object which contains the method you&#8217;d like to call, the method is defined in the name-attribute.</p>
<p>Each method-node contains one or more arg-nodes, these nodes are passed to the method as a hashed-array and used as arguments for the method. Attribute “key” will be the key or name in the arguments-array, the node-value will become the value.</p>
<p>The example above will eventually call 2 methods,</p>
<ol>
<li>the first method, “remote_method” 	 is part of object “some_object”, and receives 2 arguments;
<ul>
<li>“some_argument” with value 	“value_of_some_argument”</li>
<li>“another_argument” with value 	“value_of_another_argument”</li>
</ul>
</li>
<li>the second method, “another_remote_method”  is part of 	object “another_object”, and also receives 2 arguments;
<ul>
<li>“some_argument” with value 	“value_of_some_argument”</li>
<li>“another_argument” with value 	“value_of_another_argument”</li>
</ul>
</li>
</ol>
<p>Now, let get some coding done, let&#8217;s focus on the RPC-server first, download <a href="http://syntacticsugar.nl/upload/rpc.zip">this</a> code-snippet and unpack it in a fresh webroot (it contains all the files described in this article, also the RPC-client).</p>
<p>First take a look at /include/classes:</p>
<ul>
<li>tools.php : contains static 	“helper” methods, I hate procedural spaghetti-code and I always 	put methods which are not part of a larger object-model into several 	helper-classes. In this particular example a single helper-class is 	enough. All methods in this class are static and can be called 	without first creating an object.</li>
<li>abstract_object.php : this is 	important and can be confusing if you&#8217;re not used to an object 	oriented approach. This class contains methods which are inherited 	into child-classes, but you cannot create an object-instance of this 	class, because it&#8217;s abstract. Why is it abstract? It&#8217;s arguable to 	just let it be a normal class in this particular example, however; 	using abstract classes in larger projects really keeps your code 	clean and separates the implementation of your object-design from the 	structure you&#8217;re using.</li>
<li>some_object.php : inherited from 	abstract_class.php; example implementation.</li>
<li>another_object.php : inherited 	from abstract_class.php; another example implementation.</li>
</ul>
<p>The actual object available for the RPC-interface are some_object and another_object. Now let&#8217;s take a look at rpc.php, which is in the root. The only really interesting part is from line 56</p>
<pre><code>

	// gather 'method' nodes and execute them;
	$methods = $dom-&gt;getElementsByTagName('method');

	for ($i=0;$i&lt;$methods-&gt;length;$i++){
		$methodNode = $methods-&gt;item($i);
		$method = $methodNode-&gt;getAttribute('name');
		$obj = $methodNode-&gt;getAttribute('object');
		$obj = new $obj();
		$arguments = tools::getArgumentsAsArray($methodNode);
		$obj-&gt;$method($arguments);
		$messages .= $obj-&gt;getMessages();
	}

	echo tools::wrapMessages($messages);
</code></pre>
<p>So, what&#8217;s really happening here?</p>
<p>The first statement; <em>$methods = $dom-&gt;getElementsByTagName(&#8216;method&#8217;);</em> will retrieve all method-nodes from the request. PHP-DOM (or any other DOM-implementation for that matter) will always return the nodes in a nodeset, even if it&#8217;s only one method-node, but not if there aren&#8217;t any nodes found.<br />
This nodeset is used to loop upon, the classic for-next statement on the next line.</p>
<p>In each loop the method-node is examined; the attribute-values for object and method are determined (<em>$method = $methodNode-&gt;getAttribute(&#8216;name&#8217;)</em> and <em>$obj = $methodNode-&gt;getAttribute(&#8216;object&#8217;)</em>) and  using a static function from the tools-class the arguments are converted into a PHP-array (<em>$arguments = tools::getArgumentsAsArray($methodNode)</em> ).</p>
<p>Now we&#8217;re ready gathering all the values needed to really create the object ( <em>$obj = new $obj()</em> ) and execute the requested method ( <em>$obj-&gt;$method($arguments) </em>).</p>
<p>The method should not return anything, instead it&#8217;s return-values are stored into a protected variable (defined in abstract_object). To get these values, use  <em>$obj-&gt;getMessages()</em>. This method doesn&#8217;t seem to be available in the class of <em>$obj</em>, it&#8217;s defined in <em>abstract_object</em>.</p>
<p>After the loop has finished the $message-string should be wrapped into a node to make sure the XML is valid, this is done with the tools&#8217; class <em>wrapMessages</em>-method.</p>
<p>Ok, the RPC-interface is ready, let&#8217;s test it! Enter the following URL into a browser (assuming you&#8217;re running PHP on localhost, in a folder called rpc:</p>
<pre><code>http://localhost/rpc.php?req=&lt;command&gt;&lt;method name="remote_method" object="some_object"&gt; &lt;arg key="some_argument"&gt;value_of_some_argument&lt;/arg&gt;&lt;arg key="another_argument"&gt;value_of_another_argument&lt;/arg&gt; &lt;/method&gt;&lt;method name="another_remote_method" object="another_object"&gt; &lt;arg key="some_argument"&gt;value_of_some_argument&lt;/arg&gt;&lt;arg key="another_argument"&gt;value_of_another_argument&lt;/arg&gt; &lt;/method&gt;&lt;/command&gt;</code></pre>
<p>You&#8217;ve just called a 2 functions from your RPC-interface! The output should look like this:</p>
<pre><code>&lt;servermessages&gt;
    &lt;message object="some_object" method="remote_method" code="200"&gt;remote_method was executed
        succesfully with arguments : value_of_some_argument, value_of_another_argument &lt;/message&gt;
    &lt;message object="another_object" method="another_remote_method" code="200"&gt;another_remote_method
        was executed succesfully with arguments : value_of_some_argument, value_of_another_argument
    &lt;/message&gt;
&lt;/servermessages&gt;</code>
</pre>
<p>Now let&#8217;s move on to the RPC-client: take a look at rpcClient.php in the root. It&#8217;s quite large, and I will not go into all the details (the comments in the file should be enough). Most of the functions in rpcClient take care of constructing the XML needed to execute the RPC-call, absolutely no rocket-science.  However; I do want to highlight the actual &#8220;executing&#8221; of the RPC-call, take a look at the private function sendToHost:</p>
<pre><code>
 private function sendToHost($data) {
	$postdata = http_build_query(
	    array(
	        'req' =&gt; $data
	    )
	);
	$opts = array('http' =&gt;
	    array(
	        'method'  =&gt; 'POST',
	        'header'  =&gt; 'Content-type: application/x-www-form-urlencoded',
	        'content' =&gt; $postdata
	    )
	);
	$context  = stream_context_create($opts);
	$this-&gt;result = file_get_contents($this-&gt;rpcURI, false, $context);
}</code></pre>
<p>This method uses <em>file_get_contents</em>, <em>http_build_query</em> and <em>stream_context_create</em> to POST the constructed XML to the RPC-server and stores the reply in a local variable ($result). I&#8217;ve tried using fopen like this:</p>
<pre>
<code>function sendToHost($host,$path,$data) {
	$fp  = @fsockopen($host,80);
	$buf = '';
	if ($fp) {
		@fputs($fp, "POST $path HTTP/1.0\n");
		@fputs($fp, "Host: $host\n");
		@fputs($fp, "Content-type: application/x-www-form-urlencoded\n");
		@fputs($fp, "Content-length: " . strlen($data) . "\n");
		@fputs($fp, "Connection: close\n\n");
		@fputs($fp, $data);
		while (!feof($fp)) {
		  $buf .= fgets($fp,128);
		}
		fclose($fp);
	}
	return $buf;
}</code>
</pre>
<p>This method failed. I kept getting weird responses from my RPC-server (command not understood etc.). And honestly;  I like the clean call using <em>file_get_contents</em>, <em>http_build_query</em> and <em>stream_context_create</em> much better.</p>
<p>Ok; we have a nice object-structure, a RPC-server and a RPC-Client. What&#8217;s left is a real world implementation of the client. Take a look at example.php:</p>
<pre><code> include('rpcClient.php');

 $hsClient = new rpcClient();
 $hsClient-&gt;createCall('some_object', 'remote_method', array('some_argument'=&gt;'some_value', 'another_argument'=&gt;'another_value'));
 $hsClient-&gt;createCall('another_object', 'another_remote_method', array('some_argument'=&gt;'some_value', 'another_argument'=&gt;'another_value'));
 $hsClient-&gt;execute();
 $str = $hsClient-&gt;getXMLResponse();
 echo "&lt;pre&gt;";
 echo $str;
 echo "&lt;/pre&gt;"; </code>
</pre>
<p>If you&#8217;d leave out the debugging-statement at the end you end up using 4 lines of code to create, execute and retrieve a RPC-call (ok; 5 lines if you consider I&#8217;ve made 2 RPC calls). First you create an instance of the RPC-client, secondly you create a call using createCall and last but not least; you execute the call and intepret the response. If you&#8217;d enter the URL to example.php into your browser you&#8217;ll get:</p>
<pre>
remote_method was executed succesfully with arguments : some_value, another_value
another_remote_method was executed succesfully with arguments : some_value, another_value
</pre>
<p>That&#8217;s it! You have a RPC-server, RPC-client and a working implementation! You should take care of authenticating your RPC-calls, and design a nice Object-structure to implement &#8216;behind&#8217;  the RPC-server, this all depends on the kind of logic you need. I&#8217;ve used this method of RPC-ing to allow 3rd-parties to update certain values in a shop-database, and with a little effort you could make your RPC-server handle AJAX-requests and return JSON to clients. But that something for the next article.</p>
<p><a href="http://syntacticsugar.nl/upload/rpc.zip">[download code]</a></p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/04/17/creating-your-own-rpc-interface-in-php/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Securing PHP&#8217;s exec() on Windows</title>
		<link>http://syntacticsugar.nl/2009/03/24/securing-phps-exec-on-windows/</link>
		<comments>http://syntacticsugar.nl/2009/03/24/securing-phps-exec-on-windows/#comments</comments>
		<pubDate>Tue, 24 Mar 2009 10:07:13 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[exec]]></category>
		<category><![CDATA[IUSR]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=128</guid>
		<description><![CDATA[Using php&#8217;s exec() function on Windows has a nasty side-effect; you&#8217;ll need to give execution-rights to IUSR on cmd.exe, this is pretty much a leak, and something you do not want to do. 
I had a problem running executables (like identify.exe and convert.exe from ImageMagick) from php under windows using the exec() funciton. The solution [...]]]></description>
			<content:encoded><![CDATA[<p>Using php&#8217;s <em>exec() </em>function on Windows has a nasty side-effect; you&#8217;ll need to give execution-rights to IUSR<em> </em>on cmd.exe, this is pretty much a leak, and something <span style="text-decoration: underline;">you do not want to do</span><em>. </em></p>
<p><em>I had a problem running executables (like identify.exe and convert.exe from ImageMagick) from php under windows using the exec() funciton. The solution provided by others was kind off stupid&#8230; Give full permisions on cmd.exe to IURS&#8230; Like you wanna do that&#8230;..<br />
I wrote a exec() replacement function using the bypass_shell option proc_open has. It works great for me</em></p>
<p>Check out <a title="Alternate PHP exec() function bypassing cmd.exe" href="http://www.bokko.nl/2009/03/23/alternate-php-exec-function-bypassing-cmdexe" target="_blank">this</a> article on <a href="http://www.bokko.nl" target="_blank">bokko.nl</a> to bypass using <em>exec()</em> on Windows</p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/03/24/securing-phps-exec-on-windows/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AJP : Authenticated JSON Proxy</title>
		<link>http://syntacticsugar.nl/2009/03/20/ajp-authenticated-json-proxy/</link>
		<comments>http://syntacticsugar.nl/2009/03/20/ajp-authenticated-json-proxy/#comments</comments>
		<pubDate>Fri, 20 Mar 2009 16:01:14 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WPS]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Cross-domain]]></category>
		<category><![CDATA[Prototype.js]]></category>
		<category><![CDATA[SOP]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=100</guid>
		<description><![CDATA[Authenticated what?
The first hurdle most developers have to take when introduced to AJAX is S.O.P.
S.O.P stands for Same Origin Policy and basically means that you can only do AJAX-requests to the (sub)-domain the client is currently on.  First thing I thought when I read about AJAX couple of years ago; &#8216;wow! cool!  I [...]]]></description>
			<content:encoded><![CDATA[<p><em>Authenticated what?</em></p>
<p>The first hurdle most developers have to take when introduced to AJAX is <a href="http://en.wikipedia.org/wiki/SOP" target="_blank">S.O.P</a>.<br />
S.O.P stands for Same Origin Policy and basically means that you can only do AJAX-requests to the (sub)-domain the client is currently on.  First thing I thought when I read about AJAX couple of years ago; &#8216;wow! cool!  I can strip content from other sites and incorporate them in my websites&#8217; .. and immediately I started hammering away on a RSS-reader which was supposed to grab news from a RSS-feed directly. My efforts failed. The RSS-feed was on another domain and S.O.P kicked in spoiling all the fun.<br />
<span id="more-100"></span><br />
There are ways to get around this, you could create a server-side script which retrieves content from another domain and proxy it to your AJAX-request. However; you will need a server that has either PHP, ASP(.net), CGI, Rails, or at least something more intelligent than a plain HTML-only environment. This is OK for most people, and proxying content from other domains to AJAX-request is used a lot. But what if you&#8217;d like to create something, like a widget, which needs to grab content from any domain, and which people can use without having a PHP/ASP/CGI or Ruby-server? You wouldn&#8217;t be able to get around S.O.P, right?<br />
How can you bypass S.O.P, while still maintain a certain level of security?</p>
<p><strong>TimeLiner</strong><br />
I ran into this problem when I created <a href="http://www.tliner.com" target="_blank">TimeLiner</a>. TimeLiner retrieves and displays messages linked to a flashmovie or mp3-file.  These messages are stored on one of my servers and TimeLiner could be running anywhere. Retrieving those messages should be done by AJAX-like requests but I didn&#8217;t want users to install all kinds of server-side scripts on their server for proxying the messages from my server to overcome SOP. I wanted TimeLiner to be able to run from HTML-only-websites, or as a widget from social networking websites. But with SOP this wouldn&#8217;t be possible.<br />
With this in mind I started experimenting. I started off by creating script-tags and insert those tags into the DOM on the fly and see if variables in the referenced js file were actually available. Expecting limitations due to security measures within browsers the results actually suprised me; the variables were globally available!  I started on designing a proof of concept and tried to take this idea to the next level. At the end of the day I got a working prototype and called it AJP; the Authenticated JSON Proxy.</p>
<p><strong>RPC, AJAX, Objects</strong><br />
First a bit of history. The past few years I&#8217;ve been busy writing WPS; a publishing system much like Joomla (only much better;). It&#8217;s written in PHP5, object orientated, follows a observer-pattern(plugins) and runs entirely on XML/XSL-translations. If you&#8217;d like a shop-plugin on your WPS-driven-website you only need to include the shop-object and you&#8217;re ready to sell your stuff.</p>
<p>Because all data within WPS is actually XML it really doesn&#8217;t matter what you want to do with with it; you could translate it to HTML by using XSL-t and write it to the browser or you can pickup this data by using AJAX-calls. <a href="http://en.wikipedia.org/wiki/KISS_principle" target="_blank">KISS</a> at work <img src='http://syntacticsugar.nl/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>WPS uses its own RPC-interface for AJAX-requests. You can create a request by defining which WPS-object you&#8217;d like to call, which method within the choosen object must be executed and the arguments that need to be passed to the method. This WPSRPC-interface consists of two parts; the server-side pickup and dispatch, and a javascript-class that simplifies the actual RPC-calls.<br />
A typical WPSRPC-request would look like this:</p>
<p>Javascript:</p>
<pre><code>var wpsRPC = new wps.rpc;
wpsRPC.createCall('shop', this.setSaved.bind(this));
wpsRPC.call('storeItemToBasket', {id:itemID, uid:someUID});</code></pre>
<p>Line 1: create a RPC-object<br />
Line 2: define the PHP-object you&#8217;d like to call (in this example; the <em>shop</em>-object) and define the callback-method (in this example; the method <em>setSaved</em> from the same object-instance, hence the <em>.bind(this)</em>-statement )<br />
Line 3 : define the method which needs to executed remotely, define the arguments that need to be passed and execute the request.</p>
<p>On the server-side (PHP) the request is handled by the WPS-controller. WPS uses <em>_autoload</em> to include the requested class, instantiates the requested object (in this case the <em>Shop</em>-object) executes the requested method (<em>storeItemToBasket</em>) and passes the arguments to that method (<em>id</em> &amp; <em>uid</em>). The output of the requested method should be XML or JSON which will be returned to the javascript-callback (<em>setSaved</em>) for further handling by the browser.<br />
This proofed to be a powerful method of remotely executing stuff; it&#8217;s clean, quick and flexible and I&#8217;ve used this structure for 5 years without any problems whatsoever.</p>
<p>Now; why this elaboration about WPS?<br />
AJP uses exactly the same structure, a Javascript object that contains methods for doing RPC-calls, and a server-side RPC-interface to handle those requests, but there is a slight difference; the calls can be done across domains!</p>
<p><strong>Back to AJP: how does it work?<br />
</strong>I&#8217;ll use the example of the WPS-shop-object and explain each step; let&#8217;s assume a user is visiting your webshop and clicks &#8220;add to basket&#8221;, what will happen?</p>
<p>By clicking an event is triggered, this event will create an AJP-object:</p>
<pre><code>var ajpObject = new ajp;
ajpObject.createCall('shop', this.setSaved.bind(this));
ajpObject.call('storeItemToBasket', {id:itemID, uid:someUID});</code></pre>
<p>Internally the AJP-object creates a random ID and prepends &#8220;jsonProxy&#8221;, let&#8217;s say this variable is called <em>jsonProxy12345</em>. This variable is used to identify the request. Next step is to create a script-tag and insert it into the head of your HTML-document. The src-attribute of the script-tag points to the URI where a RPC-interface is waiting for requests. This is not a .js file, but a .php-file; which allows you to use http-requests (<em>$_REQUEST</em> in PHP) to pass along variables.<br />
A script-tag from this example would look like this;</p>
<pre><code>&lt;script src="http://www.yourdomain.com/
rpc.php?jsonProxy=12345&amp;object=shop&amp;method=storeItemToBasket&amp;id=1&amp;uid=12" &gt;
&lt;/script&gt;</code></pre>
<p>The server-side pretty much works the same as the WPS-example above; the <em>$_REQUEST</em> is mapped to an object, a method and the arguments and is executed. There&#8217;s a small difference; the output can only be JSON, and put into a variable called <em>jsonProxy12345</em>.</p>
<p>Ok, let&#8217;s assume the RPC-call executed OK and the ID of the inserted row should be returned to javascript. How does AJP know there&#8217;s output, and how does AJP actually use the output?<br />
In &#8216;real&#8217; AJAX you can use an event to notify Javascript the RPC-call returned something, but AJP is not &#8216;real&#8217; AJAX&#8230; This is where the <em>jsonProxy12345</em> comes in: the moment the script-tag is inserted into the DOM a timer is started  which keeps checking for the variable <em>jsonProxy12345</em>. If the variable exists the value is picked up and passed to the callback-function, otherwise the timer keeps running until it encounters the variable (there&#8217;s a timeout to catch errors). This effectively mimics the events used in real AJAX, which we want.</p>
<p><strong>Now for some security</strong><br />
If you use this kind of RPC you really do not want to expose your PHP-objects to the world. If you created a populair service your server would run out of resources if anyone can do RPC-calls to it. To overcome this you need to authenticate the requests, but how?<br />
It&#8217;s really quite simple; before actually doing a request, <em>ask</em> the server if you may do a request!<br />
I&#8217;ve done this before with traditional forms; before posting a form an AJAX-call retrieves a unique ID from the server (a mysql GUID), this GUID is appended to the form in a hidden field and the form is posted. The server checks if the GUID is valid, and if that&#8217;s the case the form is handled.<br />
But how does the server know the GUID is valid? The moment the server creates the GUID it&#8217;s stored into a database, along with the originating IP-address, the browser-string and the time it&#8217;s requested. If the form is actually posted the server checks if the GUID is available, if it&#8217;s from the same IP, with the same browserstring and within 5 seconds after it&#8217;s been requested. Only if all conditions are met the form is handled and the GUID is removed from the database.</p>
<p>This principle can also be used to authenticate AJP-requests, and AJP can use itself to request a GUID by being clever in it&#8217;s design and inheritance-scheme.<br />
I realize it&#8217;s quite easy to mimic the requesting of GUID&#8217;s, but it&#8217;s a barrier, and AJP does something else to tighten up security even more; whenever AJP receives a result the script-tag will be removed immediately. It removes any evidence of requests, making it harder to find out what happened.</p>
<p>AJP is certainly not rocket-science, I&#8217;ve seen a lot of scripts using this kind of technique. I do believe the structure of AJP itself works great and allows developers to extend upon. As a bonus; this also works great on very old browsers; i&#8217;ve tested it on MSIE 4, and AJP works fine!</p>
<p>I&#8217;ve compiled a small (LGPL&#8217;ed) <a href="http://syntacticsugar.nl/upload/ajp.zip">download</a> which contains all the files for using AJP, it&#8217;s not a full implementation, but enough to get you going.</p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/03/20/ajp-authenticated-json-proxy/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Shoutcast on a Networked Media Tank (NMT)</title>
		<link>http://syntacticsugar.nl/2009/03/11/shoutcast-on-a-networked-media-tank-nmt/</link>
		<comments>http://syntacticsugar.nl/2009/03/11/shoutcast-on-a-networked-media-tank-nmt/#comments</comments>
		<pubDate>Wed, 11 Mar 2009 14:06:22 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[NMT]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[HDX-1000]]></category>
		<category><![CDATA[HDX-1080]]></category>
		<category><![CDATA[Networked Media Tank]]></category>
		<category><![CDATA[Shoutcast]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=70</guid>
		<description><![CDATA[I have a HDX-1000; a Networked Media Tank.  A kind of dedicated audio/video-player with harddisk and capable of playing about anything you throw at it (matroska, DVD-iso&#8217;s, DIVX, XVid&#8217;s, mpg&#8217;s) in Full HD (1080P).  Absolutely a toy to recommend if you&#8217;re into HD-video. If you&#8217;re looking for one I can strongly recommend buying one at [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_71" class="wp-caption alignright" style="width: 160px"><img class="size-full wp-image-71" title="hdx1000-silver-front" src="http://syntacticsugar.nl/wp-content/uploads/2009/03/hdx1000-silver-front.gif" alt="HDX-1000 Networked Media Tank" width="150" height="100" /><p class="wp-caption-text">HDX-1000 Networked Media Tank</p></div>
<p>I have a <a title="HDX-1080" href="http://www.hdx1080.com/" target="_blank">HDX-1000</a>; a Networked Media Tank.  A kind of dedicated audio/video-player with harddisk and capable of playing about anything you throw at it (matroska, DVD-iso&#8217;s, DIVX, XVid&#8217;s, mpg&#8217;s) in Full HD (1080P).  Absolutely a toy to recommend if you&#8217;re into HD-video. If you&#8217;re looking for one I can strongly recommend buying one at <a href="http://www.divxplayer.nl" target="_self">DivxPlayer.nl</a> , their service is absolutely superb.</p>
<p>Anyway; the HDX-1000 is also capable of displaying (simple) websites, aka. webservices (in fact; the GUI is HTML-based), and it&#8217;s very good at playing Shoutcast-streams. But; there were no webservices for browsing through the Shoutcast-directory.  Ok; there&#8217;s a top10 shoutcast station-list, but with over 50000 stations this does not even scratch the surface. So.. i gave up a sundaymorning and started hammering away on a Shoutcast-directory Browser for NMT&#8217;s.</p>
<p>Fortunately Shoutcast has a very nice API to request all kinds of information, and it returns XML, which I became quite an expert on the last 10 years. Because I didn&#8217;t want to put a heavy load on Shoutcast&#8217;s servers by requesting data on each visit, and to keep my directory-browser at blazing speed i&#8217;ve written a sync-script which pulls all categories and stations from the Shoutcast-API and dump them into a database.  The script runs every 2 days (if I don&#8217;t forget it:)) and refreshes the stationlist in my database.</p>
<p>If you have a NMT-player; check out <a href="http://hdx.i-v-o.nl" target="_blank">http://hdx.i-v-o.nl</a> (the stations will only play when started from a NMT, not from your browser)</p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/03/11/shoutcast-on-a-networked-media-tank-nmt/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Pending release of CRUDDER</title>
		<link>http://syntacticsugar.nl/2009/03/04/pending-release-of-crudder/</link>
		<comments>http://syntacticsugar.nl/2009/03/04/pending-release-of-crudder/#comments</comments>
		<pubDate>Wed, 04 Mar 2009 09:48:06 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[CRUD]]></category>
		<category><![CDATA[Content Publishing]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Open source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[crudder]]></category>
		<category><![CDATA[javacript]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=39</guid>
		<description><![CDATA[I&#8217;m currently still optimizing and debugging a fresh release of Crudder, e.t.a of this release will be somewhere around the midst of March.
Changes:

Updates without any affected_rows will not trigger an error anymore, SQL-errors are still caught though
MultiLinked fields _should_ work better on MSIE, but you never know; IE sucks at being any kind of predictable [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m currently still optimizing and debugging a fresh release of <a href="http://www.crudder.net" target="_self">Crudder</a>, e.t.a of this release will be somewhere around the midst of March.<br />
Changes:</p>
<ul>
<li>Updates without any affected_rows will not trigger an error anymore, SQL-errors are still caught though</li>
<li>MultiLinked fields _should_ work better on MSIE, but you never know; IE sucks at being any kind of predictable (and all other things a browser should do).</li>
<li>Language-settings of the interface-texts actually work</li>
<li>new feat: thouroughCheck; checks whether a table has non-nullable fields and will make those fields mandatory</li>
<li>new feat: exporting data to CSV (currently the output with default-settings is suitable for openOffice)</li>
<li>new feat: Advanced filtering / Querybuilder</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2009/03/04/pending-release-of-crudder/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to stuff 1000 ( or more ) images into a page, without the need for scrollbars</title>
		<link>http://syntacticsugar.nl/2007/12/26/56/</link>
		<comments>http://syntacticsugar.nl/2007/12/26/56/#comments</comments>
		<pubDate>Wed, 26 Dec 2007 17:21:41 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[Apache]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[dynamic resizing]]></category>
		<category><![CDATA[geRRit]]></category>
		<category><![CDATA[Gooseberry]]></category>
		<category><![CDATA[Homeless-songs]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=56</guid>
		<description><![CDATA[I got this call from a friend, Boris, who&#8217;s in a band called geRRIT. He asked me to help him out on an idea he had for getting geRRIT&#8217;s songs spread around the world: homeless-songs.
The idea was to send 4000 cd&#8217;s to 15 cities around the world, and ask people to copy the cd and [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.i-v-o.nl/upload/flyer.jpg" alt="" width="125" height="126" align="right" />I got this call from a friend, Boris, who&#8217;s in a band called geRRIT. He asked me to help him out on an idea he had for getting geRRIT&#8217;s songs spread around the world: homeless-songs.</p>
<p>The idea was to send 4000 cd&#8217;s to 15 cities around the world, and ask people to copy the cd and give the original to someone else, or leave it at a public spot in town. And while you&#8217;re at it, leave a message with  picture on a website. Inspired by J.A.L.D  (just another link dump) we came to a design that would fit all images that would be uploaded, whether it would be just 10 pictures, or 1000, it had to fit into one page.</p>
<p><span id="more-56"></span></p>
<p>So I started some tryouts:</p>
<p><strong> first runner up</strong>: using Javascript to resize all images. The major drawback using this technique; bandwidth. If there would be 1000 pictures, each having  an original resolution of 1024&#215;768 (which is quite small) and about 300 kb each, the client would have to download 1000&#215;300kb of pictures, a whopping 292 MB worth of data, which will eventually be displayed as tiny thumbnails. A huge waste of bandwidth! Not only bandwidth would be a problem, what about the javascript itself? I&#8217;d probably be using getElementsByTagName, or getElementsByClassName, loop through 1000 images, resizing each images. IMHO. a pretty expensive operation, even for a modern computer/browser.</p>
<p>So.. no javascript for resizing, what other options are there?</p>
<p><strong>Second runner up</strong>: using GD or Imagick to resize the images on the  fly. Verrrrry expensive, but with the (visually) best results. Each uploaded image would be dynamically resized to fit the grid of images. I&#8217;d use an Ajax call after the page is loaded, pass the size of the viewport to a PHP-script which is able to calculate the max-width &amp; height of each image, resizing a copy of the original and send something (xml/json/plain html) back to the client. Well.. I own a server which has a lot of  processing power, but 10 concurrent sessions of resizing-images-script would even bring this Xeon Quad-core to it&#8217;s knees (even if i&#8217;d cache the resized images for later use). Not an option!</p>
<p><strong>Third runner up:</strong><br />
Use GD/Imagick to resize the original into several resized copies. After a client uploads his/hers image the server would make a certain amount of resized copies. When a client opens the page, the size of the viewport is determined and sent back to the server using an Ajax-call. The server determines the total area and determines how may pixels one image may have, loops through the available sizes, and returns the size that fits the amount of pixels the image may have. I actually build this solution in Ruby on Rails. I mixed PHP and Ruby before (well.. mixed.. just called RoR-pages from a PHP-based webpage using Ajax and some smart proxy-ing), and liked the idea of spreading performance among different<br />
techniques. If the site were to be a HUGE success the actual calculation would become the bottleneck; since RoR is extremely scalable I could setup another (RoR) server to handle the load.<br />
The Proof Of Concept did hold well, even under a bit of stress, however; RoR itself uses quite a lot or resources and I&#8217;m not an expert on RoR (yet). I did not want to leave a potentially dangerous script (remember; people are uploading stuff) on a<br />
production-server while i&#8217;m not really able to assess the potential security issues.  So; <em>I rewrote the thing in PHP.</em></p>
<p>Another problem I came across was the aspect-ratio of the uploaded images. The design would be a grid-like solution, all images would have to fill up a certain amount of pixels, leaving no space between them. But; what if an image has a  spect-ratio of 16&#215;10, and the image next to it, say 10&#215;16. One of them has to be resized to match the other height, or width, leaving no space above. And how would I calculate the optimal size of each image? Not knowing the length of each side? So I dug up some old math-books to check out whether I forgot some rule or calculation.. No results.<br />
I did write some code to roughly guess the average size of one image, but the results were quite strange and unpredictive. I came to the conclusion that the only way to get those images aligned and keep a reasonable quality was to crop them to have a common aspect-ratio. Fortunately Imagick has a build-in method to accomplish this: cropThumbnailImage.</p>
<p><strong>OK, So, how did I solve this ?</strong></p>
<p>Pretty easy! I wrote one function which resized one uploaded image into 15 different sizes, store each of those images into a folder named after the size of the image and wrote a function to calculate the optimal size of those images (based on x and y from the ajax-call) to output to the client. I tried to resize the images again using javascript, to get them to fit exactly into the page, but this was too expensive and images didn&#8217;t look as crisp as they should be. PHP-Function for resizing an image:</p>
<pre><code>private function getImages($country = '', $city=''){
	if ($country){
		$sqlExtra = " AND country LIKE '" .  mysql_escape_string($country) . "' ";
	}
	if ($city){
		$sqlExtra .= " AND city LIKE '" . mysql_escape_string($city). "'";
	}
	$sql = "SELECT * FROM reactions WHERE `show` = 1 " . $sqlExtra . " ORDER BY created_at DESC";
	$rs = $this-&gt;wps-&gt;getADO()-&gt;execute($sql);
	$count = $rs-&gt;recordCount() + 2;

	$totalArea = $this-&gt;totalArea();
	$areaPerImage = ($totalArea/$count);
	// determine which image path to use:
	foreach($this-&gt;sizes as $size){
		$sizeH = $size + $this-&gt;padCorrectionH;
		$sizeW = $size + $this-&gt;padCorrectionW;
		$imgArea = ($sizeH*$sizeW);
		if ($imgArea &lt; $areaPerImage){
			$imgPath = $size;
			break;
		}
	}
	// make up the xml for the images in $imgPath;
	$xml ='';
	while (!$rs-&gt;EOF){
		if (!$rs-&gt;fields['has_image']){
			$xml .='&lt;gerrit_image id="' . $rs-&gt;fields['id'] . '" color="#' . $this-&gt;getRndColor(). '"&gt;';
		}else{
			$xml .='&lt;gerrit_image file="' . $imgPath . '/' . $rs-&gt;fields['id'] . '.jpg" id="' . $rs-&gt;fields['id'] . '"&gt;';
		}
		$xml .='&lt;name&gt;' . $rs-&gt;fields['name'] . '&lt;/name&gt;';
		$xml .='&lt;city&gt;' . $rs-&gt;fields['city'] . '&lt;/city&gt;';
		$xml .='&lt;country&gt;' . $rs-&gt;fields['country'] . '&lt;/country&gt;';
		$xml .='&lt;/gerrit_image&gt;';
		$rs-&gt;moveNext();
	}

	$xml = '&lt;gerrit_images size="' . $imgPath . '" count="' . ($count-2). '"&gt;' . $xml . '&lt;/gerrit_images&gt;';
	$this-&gt;WPS_DB_ado2dom = new DOMDocument();
	$this-&gt;WPS_DB_ado2dom-&gt;loadXML($xml);
}</code></pre>
<p>Javascript code : Ajax call to get the images from the server:</p>
<pre>
<code>
function resize(){ // executed at page-loaded-event, and on page-resize.
	var screenSize = Element.getDimensions(document.body);
	var absBottom = screenSize.height;
	var absRight = screenSize.width;
	var width = (absRight - 305 - 30);
 	$('mainContainer').style.width = width + 'px';
 	$('mainContainer').style.height = (absBottom - 60) + 'px';
 	loadDefaultContent(currCountry, currCity);
}

function loadDefaultContent(country, city){
// Use Prototype-Ajax-updater to update the image-container
	if (!country) country = '';
	if (!city) city = '';
	var screenSize = Element.getDimensions($('mainContainer'));
	try{
		new Ajax.Updater(
			'mainContainer',
			gRelRootPath + 'get_images/&amp;amp;city=' + city + '&amp;amp;c=' + country + '&amp;amp;w='+
				(screenSize.width) + '&amp;amp;h=' +(screenSize.height ),
				{
					asynchronous:true,
					evalScripts:true,
					onLoading : waiter,
					onLoaded : unWaiter,
					onComplete :function(transport){ makeSortable(); }
				}
			);
	}catch(e){
	}
	return false;
}</code></pre>
<p><strong>Checkout the result : <a href="http://www.homeless-songs.com" target="_blank">www.homeless-songs.com</a></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2007/12/26/56/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Upcoming WPS-release: WPS 2.7</title>
		<link>http://syntacticsugar.nl/2007/12/12/upcoming-wps-release-wps-27/</link>
		<comments>http://syntacticsugar.nl/2007/12/12/upcoming-wps-release-wps-27/#comments</comments>
		<pubDate>Wed, 12 Dec 2007 09:03:41 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[CMS]]></category>
		<category><![CDATA[Content Publishing]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WPS]]></category>
		<category><![CDATA[publishing]]></category>

		<guid isPermaLink="false">http://syntacticsugar.nl/?p=15</guid>
		<description><![CDATA[I&#8217;m figuring out what new features would make WPS even better. I&#8217;m planning a new release of WPS in 2008, probably around may/june for a full fresh production-release.So I summed up a list of new features that would improve WPS, if you have anything to add, please use the reactions-widget.
Other features/wannahave&#8217;s:

More/Better Help-functions, possibly integrated into [...]]]></description>
			<content:encoded><![CDATA[<div id="newsMessage_81" class="newsItem">I&#8217;m figuring out what new features would make WPS even better. I&#8217;m planning a new release of WPS in 2008, probably around may/june for a full fresh production-release.So I summed up a list of new features that would improve WPS, if you have anything to add, please use the reactions-widget.</div>
<p>Other features/wannahave&#8217;s:</p>
<ul>
<li>More/Better Help-functions, possibly integrated into WPS itself.</li>
<li> <strong>Multi-language-support</strong>, not only for editMode, but also for the publishing modules themselves</li>
<li>Sandbox-support out of the box, no need for a specific subdomain or VirtualHost</li>
<li>New <strong>site-manager</strong>; an overview of pages/contentblocks from a grid/treeview.</li>
<li>Link-checker/mover; Checks internal links to WPS-pages or renames them when a page is renamed</li>
<li>More wizards for general functions (add page/block etc).</li>
<li> <strong>Template-editting</strong> : access and edit XSL-stylesheets that make up the layout/design of your WPS-site</li>
<li>Empty-cache-button</li>
<li>Filemanager needs to be rewritten to be MUCH faster and uses less resources</li>
<li>Custom-XML-tags-support in content-editors, more editing-facilities in content-editors</li>
<li>Install/Configuration/Security-audit &#8211; scripts</li>
<li><strong>Version-control</strong></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2007/12/12/upcoming-wps-release-wps-27/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Performance Impact of loadable PHP-modules in Apache</title>
		<link>http://syntacticsugar.nl/2007/11/17/performance-impact-of-loadable-php-modules-in-apache/</link>
		<comments>http://syntacticsugar.nl/2007/11/17/performance-impact-of-loadable-php-modules-in-apache/#comments</comments>
		<pubDate>Sat, 17 Nov 2007 08:15:48 +0000</pubDate>
		<dc:creator>buTTon</dc:creator>
				<category><![CDATA[Apache]]></category>
		<category><![CDATA[FreeBSD]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Webhosting]]></category>
		<category><![CDATA[extensions]]></category>
		<category><![CDATA[Jails]]></category>

		<guid isPermaLink="false">http://blog.shared.i-v-o.nl/?p=10</guid>
		<description><![CDATA[Research on the performance impact of loadable modules in Apache/PHP.]]></description>
			<content:encoded><![CDATA[<p>What impact do modules have on a high-performance webserver? <strong>A LOT! </strong>(though that would not be a suprise)</p>
<p>I&#8217;ve been playing with a new webserver for a week now. It&#8217;s a brand new Dell PowerEdge 980, with Xeon Quadcore (2.4ghz), SAS-raid1-sata (7200rpm) and 4 GB of ECC-ram. Should be enough for a relatively large website with high concurrency.. The application I write do not cause a lot of Mysql-queries, I try to cache as much as possible, the real performance-gain for me is getting PHP5 to run faster. So I installed the machine and got to work on some stress-tests.</p>
<p><span id="more-10"></span></p>
<p><strong>FreeBSD 6.2-STABLE</strong><br />
I installed FreeBSD 6.2-stable, build the world and a fresh kernel and configured FreeBSD to run several Jails. Among these jails I have a Mysql-5.1 database-server (bound to a loopback-interface) and an Apache2.2 instance, build as MPM-worker and (eventually) fully loaded with modules. Apache and mysql each run in their own jail, and natd ipfw is forwarding web-traffic to Apache, and allows apache to connect to Mysql. Jails were build by using EZ-Jail and all compiling was done from a fresh FreeBSD-ports-collection.</p>
<p><strong>MPM-Worker VS MPM-Prefork</strong><br />
Apache can be build as worker-module of as prefork-module (<a href="http://httpd.apache.org/docs/2.0/mpm.html">see apache.org</a> ). After some research I assumed the Worker-MPM should be better for this system, because it has a Quad-Core.</p>
<p><strong>Benchmarking: AB (apacheBench)</strong></p>
<p>For benchmarking I used AB and Siege. Siege didn&#8217;t allow the amount of concurrent sessions I initially wanted (&amp;gt;300) because of memory-problems on my laptop, so; the real benchmarking was done with AB. The command I executed each test (twice): <em>ab -n 5000 -c 50 http://192.168.254.103/index.php </em>(except forr the first run, I had to use index.html &#8216;coz there was no PHP). The PHP-file did nothing more than calling phpinfo();</p>
<p><strong>MPM-WORKER tests:</strong></p>
<p><strong>1. Apache without any PHP, but build with almost all default-options/modules :</strong></p>
<p>Concurrency Level: 50<br />
Time taken for tests: 12.305248 seconds<br />
Complete requests: 5000<br />
Failed requests: 1<br />
(Connect: 0, Length: 1, Exceptions: 0)<br />
Write errors: 0<br />
<strong>Requests per second: 406.33 [#/sec] (mean)</strong><br />
Time per request: 615.262 [ms] (mean)<br />
Time per request: 2.461 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11344.35 [Kbytes/sec] received</p>
<p><strong>2. Apache Build with PHP 5.25 (default options) with mysql module:</strong><br />
* unfortunately PHP was NOT build with the suhosin-patch, which is unstable in jail-environments</p>
<p>Concurrency Level: 50<br />
Time taken for tests: 13.169914 seconds<br />
Complete requests: 5000<br />
Failed requests: 4<br />
(Connect: 0, Length: 4, Exceptions: 0)<br />
Write errors: 0<br />
Total transferred: 153016345 bytes<br />
HTML transferred: 151995475 bytes<br />
<strong>Requests per second: 379.65 [#/sec] (mean)</strong><br />
Time per request: 658.496 [ms] (mean)<br />
Time per request: 2.634 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11346.32 [Kbytes/sec] received</p>
<p><strong>3. Apache   PHP with mysql, DOM, PCRE and SimpleXML :</strong></p>
<p>Concurrency Level: 50<br />
Time taken for tests: 14.363650 seconds<br />
Complete requests: 5000<br />
Failed requests: 5<br />
(Connect: 0, Length: 5, Exceptions: 0)<br />
Write errors: 0<br />
Total transferred: 166824023 bytes<br />
HTML transferred: 165801959 bytes<br />
<strong>Requests per second: 348.10 [#/sec] (mean)</strong><br />
Time per request: 718.182 [ms] (mean)<br />
Time per request: 2.873 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11342.10 [Kbytes/sec] received</p>
<p><strong>4. Apache   PHP, with mysql, DOM, PCRE, SimpleXML, xml and XSL</strong></p>
<p>Concurrency Level: 50<br />
Time taken for tests: 14.643427 seconds<br />
Complete requests: 5000<br />
Failed requests: 6<br />
(Connect: 0, Length: 6, Exceptions: 0)<br />
Write errors: 0<br />
Total transferred: 170059685 bytes<br />
HTML transferred: 169041004 bytes<br />
<strong>Requests per second: 341.45 [#/sec] (mean)</strong><br />
Time per request: 732.171 [ms] (mean)<br />
Time per request: 2.929 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11341.13 [Kbytes/sec] received</p>
<p><strong>5. Apache   PHP, with mysql, DOM, PCRE, SimpleXML, xml, XSL and ImageMagick (PECL)</strong></p>
<p>Didn&#8217;t run well, see conclusion</p>
<p><strong>PREFORK-Tests:</strong></p>
<p><strong>1. PHP Only, No Modules</strong></p>
<p>Concurrency Level: 50<br />
Time taken for tests: 12.295282 seconds<br />
Complete requests: 5000<br />
Failed requests: 0<br />
Write errors: 0<br />
Total transferred: 141711640 bytes<br />
HTML transferred: 140707685 bytes<br />
<strong>Requests per second: 406.66 [#/sec] (mean)</strong><br />
Time per request: 122.953 [ms] (mean)<br />
Time per request: 2.459 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11255.54 [Kbytes/sec] received</p>
<p><strong>2. PHP   mysql</strong></p>
<p>Concurrency Level: 50<br />
Time taken for tests: 12.927480 seconds<br />
Complete requests: 5000<br />
Failed requests: 0<br />
Write errors: 0<br />
Total transferred: 150875864 bytes<br />
HTML transferred: 149879073 bytes<br />
<strong>Requests per second: 386.77 [#/sec] (mean)</strong><br />
Time per request: 129.275 [ms] (mean)<br />
Time per request: 2.585 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11397.35 [Kbytes/sec] received</p>
<p>3. <strong>PHP   Mysql, PCRE, SPL , DOM   SimpleXML</strong><br />
Concurrency Level: 50<br />
Time taken for tests: 14.93142 seconds<br />
Complete requests: 5000<br />
Failed requests: 0<br />
Write errors: 0<br />
Total transferred: 164663496 bytes<br />
HTML transferred: 163661133 bytes<br />
<strong>Requests per second: 354.78 [#/sec] (mean)</strong><br />
Time per request: 140.931 [ms] (mean)<br />
Time per request: 2.819 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11410.09 [Kbytes/sec] received</p>
<p><strong>4.PHP   Mysql, PCRE, SPL , DOM   SimpleXML, XML   XSL</strong><br />
Concurrency Level: 50<br />
Time taken for tests: 14.584835 seconds<br />
Complete requests: 5000<br />
Failed requests: 0<br />
Write errors: 0<br />
Total transferred: 168910632 bytes<br />
HTML transferred: 167907871 bytes<br />
<strong>Requests per second: 342.82 [#/sec] (mean)</strong><br />
Time per request: 145.848 [ms] (mean)<br />
Time per request: 2.917 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11309.76 [Kbytes/sec] received</p>
<p><strong>5. PHP   Mysql, PCRE, SPL , DOM   SimpleXML, XML   XSL, IMAGICK</strong></p>
<p>Concurrency Level: 50<br />
Time taken for tests: 15.321272 seconds<br />
Complete requests: 5000<br />
Failed requests: 0<br />
Write errors: 0<br />
Total transferred: 176969536 bytes<br />
HTML transferred: 175968367 bytes<br />
<strong>Requests per second: 326.34 [#/sec] (mean)</strong><br />
Time per request: 153.213 [ms] (mean)<br />
Time per request: 3.064 [ms] (mean, across all concurrent requests)<br />
Transfer rate: 11279.81 [Kbytes/sec] received</p>
<p><strong> Conclusion</strong></p>
<p>During the testing I rebuild the whole thing to run Apache Preforked. I noticed that adding ImageMagick to a Apache Worker-instance caused segmentation faults, leading to dropped clients. I didn&#8217;t find anything conclusive at Google on this. I also noticed the distribution of clients among the httpd-processes. Worker caused 8 http-processes to run at 40% processor (out of 400%), the prefork-httpd-version had 50 httpd-processes spawned, each taking about 5% at init, and got down to 1% soon after init. Using Imagick on worker caused each httpd-process to use every cycle it could (350% on 1 client). Preforked httpd&#8217;s with ImageMagick also used a lot more cycles; 8-10% at init!</p>
<p>So: if you want to get every cycle worth of PHP from your box you&#8217;re better of keeping PHP as slim as possible, and try to keep away from loading Imagick. I&#8217;m considering building a special Jail for imagick-php which will not be used that much. This way the-real-everyone-sees-me-PHP5-instance is ready for a lot more concurrent session.</p>
<p><strong>Still want more performance?</strong><br />
Consider serving static content (images, css, javascript and HTML-files) from lighttpd. Lighttpd is a LOT faster, doesn&#8217;t need a lot of memory. and uses less cycles compared to Apache (remember to build lighttpd without support for php, fcgi). I&#8217;m still figuring out whether I can use mod_rewrite for redirection to lighttpd, I guess not, because Apache still has to handle those requests. Maybe mod_proxy will work.</p>
<p><strong>Update</strong></p>
<p>Session-support in Worker-threads seems to break too, so I finally build a debug-build of PHP and the extensions, trying to figure out why Imagick made the Worker-thread barf on me. No results; no clue anywhere in the logs. At this time I have a stable Prefork-apache running with both session and imagick-support, including some other extensions. This means giving up on 100 req/s based on the tests. In still want to build a production environment capable of handling heavy load (without imagick), a script/jail for manipulation of images and lighttpd for static content (which is already up, but cannot be tested yet).</p>
]]></content:encoded>
			<wfw:commentRss>http://syntacticsugar.nl/2007/11/17/performance-impact-of-loadable-php-modules-in-apache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
