tag:blogger.com,1999:blog-14696189163733242012024-02-20T09:52:34.929-08:00The LAMPpostTips and tricks for LAMP (Linux Apache MySQL PHP) developers.Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-1469618916373324201.post-31965407400015121752014-11-20T11:29:00.001-08:002014-11-20T11:34:02.916-08:00SimpleXML and mixed, nested namespaces<p>I work with several third party marketplace APIs, one of which is Amazon's MWS API.<br />
When processing their <em>GetMatchingProductForId</em> response I discovered that they sometimes namespace the XML nodes (with <em>ns2</em>).</p>
<p>PHP's SimpleXML doesn't allow access to namespaced nodes via the usual interface of <code>$simpleXml->node</code> but I found you can specify a namespace to the <code>children()</code> method like so:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New';">$simpleXml
->children('ns2', true)
->namespacedNode</pre>
<p>That works great but then I hit a problem. As I mentioned, Amazon namespace <em>some</em> of the nodes but not all and the node I was really after was not namespaced itself but was a child of a namespaced node. After I applied the code above I found that I couldn't access the child node:</p>
<pre style="background: #FF9999; font-family: 'Courier New'">$simpleXml
->children('ns2', true)
->namespacedNode
->nonNamespacedNode</pre>
<p>I thought all was lost until I discovered you can reset SimpleXML's namespace filter back to none again with another call to <code>children()</code> so you can get at the non-namespaced child node:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New';">$simpleXml
->children('ns2', true)
->namespacedNode
->children()
->nonNamespacedNode</pre>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-20960437385567447912014-06-29T08:31:00.001-07:002014-06-29T08:31:43.831-07:00Jasmine and Jasq<p>Recently I've started using <a href="http://jasmine.github.io/" target="_blank">Jasmine</a> - a BDD framework - to test some pretty complex JavaScript I've been writing for work. We're using <a href="http://requirejs.org/" target="_blank">RequireJS</a> for our JavaScript and we found a great plugin for Jasmine to make it play nicely with AMD modules called <a href="https://github.com/biril/jasq" target="_blank">Jasq</a>.</p>
<p>All works beautifully except for one thing: the Jasq docs say to wrap your specifications in a <em>require</em> block but that didn't seem to work for me. Once I changed them to <em>define</em> blocks (see below) everything started working. I don't know why this is and it doesn't seem anyone else had the same issue so perhaps I've done something wrong but I just thought I'd put this out there in case anyone else runs into the same issue.</p>
<p>
<pre style="background: #EEEEEE; font-family: 'Courier New';">
define(['jasq'], function ()
{
describe('My Tests', 'thing/to/test', function()
{
it('should do something', function(thingToTest)
{
//...
});
});
});
</pre>
</p>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-1537601173300256532013-09-02T12:30:00.001-07:002013-09-02T12:30:47.910-07:00Elasticsearch, Chef and Vagrant<p>I've been tasked at work with setting up an <a href="http://www.elasticsearch.org/" target="_blank">Elasticsearch</a> cluster.
We use <a href="http://www.opscode.com/chef/" target="_blank">Chef</a> for provisioning and there's an <a href="https://github.com/elasticsearch/cookbook-elasticsearch" target="_blank">official cookbook</a> available with some instructions but they pressume you are using <a href="http://aws.amazon.com/ec2/" target="_blank">Amazon EC2</a> which we are not - we're using our own servers and <a href="http://www.vagrantup.com/" target="_blank">Vagrant</a> VMs for testing - so I had to figure a few things out myself.</p>
<p>When I first added the recipe to the node's run list it all installed fine but then I found that Elasticsearch was not running. When I tried running it manually it just said "Killed" and exited. This had me scratching my head for quite a while but I finally found the solution.</p>
<p>In some of the official examples they include the following in the Chef node:</p>
<pre style="background: #FFCCFF; font-family: 'Courier New'">
"elasticsearch": {
"bootstrap.mlockall": true
}
</pre>
<p>It's not explained what this does but in the template config <a href="http://en.wikipedia.org/wiki/Yaml" target="_blank">YAML</a> file it says it prevents the <a href="http://en.wikipedia.org/wiki/Java_virtual_machine" target="_blank">JVM</a> from using swap which causes Elasticsearch to perform badly. Fair enough, however, on a virtual machine that has very little memory it can mean that the JVM doesn't have enough memory to run so it crashes. True is the default value so it's not enough to simply not specify this config, you have to set it to false.</p>
<p>Once I got that working my first node had Elasticsearch running and all was well. Then I started up my second node but I couldn't get it to form a cluster with the first.</p>
<p>As per the documentation I had given them both the same cluster_name. Our servers are spread across different networks so I couldn't use the default multicast option for discovery so I added the <a href="http://en.wikipedia.org/wiki/Fully_qualified_domain_name" target="_blank">FQDN</a>'s of each node to the unicast list:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New'">
"elasticsearch": {
"discovery.zen.ping.multicast.enabled": false,
"discovery.zen.ping.unicast.hosts": "[\"node1.example.com\", \"node2.example.com\"]"
}
</pre>
<p>Each node has a host entry for each other node and they could telnet to each other on the Elasticsearch discovery port (9300) just fine but when the second node started up I got an error like:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New'">
[node2[inet[/
10.0.2.2:9300]] failed to send join request to master [node1], reason
[org.elasticsearch.transport.RemoteTransportException: [node2[inet[/
10.0.2.2:9300]][discovery/zen/join];
org.elasticsearch.ElasticSearchIllegalStateException: Node [node2[inet[/
10.0.2.2:9300]] not
master for join request from [node2[inet[/
10.0.2.2:9300]]
</pre>
<p>Huh? Why was node2 trying to connect to node2? It was my colleague that noticed the references to the <em>10.0.2.*</em> IPs where we would've expected <em>192.168.33.*</em> IPs. Turns out that Vagrant always sets the NAT adapter on eth0 and it was the IP of that that Elasticsearch was binding to by default. You can override with the network.host config:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New'">
"elasticsearch": {
"network.host": "192.168.33.1"
}
</pre>
<p>Once I'd done that for each node (with their respective IPs) the cluster started working.</p>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com2tag:blogger.com,1999:blog-1469618916373324201.post-71534350049253448912013-07-15T05:56:00.000-07:002013-07-15T05:56:49.401-07:00MongoDB, replica sets and authentication<p>I've just recently had to set up a <a href="http://www.mongodb.org/" target="_blank">MongoDB</a> <a href="http://docs.mongodb.org/manual/replication/" target="_blank">replica set</a> using <a href="http://www.opscode.com/chef/" target="_blank">Chef</a> at work. It was also required that authentication be set up in MongoDB.</p>
<p>I had very little experience of either technology (but I would now recommend both) so it took me quite a while to figure it all out so I thought I'd offer some advice here for anyone else who may need it. Although I used Chef most of this stuff still applies even if you're doing it manually. I'll be presuming you know what replica sets are, if you don't but are interested please <a href="http://docs.mongodb.org/manual/core/replication-introduction/" target="_blank">read up on them</a> first.</p>
<p>Many set-ups are possible with replica sets but we opted for a three node set: primary, secondary and an arbiter.<br />
It's also worth noting that I used <a href="http://www.vagrantup.com/" target="_blank">Vagrant</a> to test all this which I'd highly recommend. It did mean having three VM's running simultaneously which chewed up my RAM but it was worth it.</p>
<p>We used the <a href="https://github.com/edelight/chef-mongodb" target="_blank">edelight chef-mongodb cookbook</a> as the starting point. I added <span style="background: #EEEEEE; font-family: 'Courier New'">recipe[mongodb::replicaset]</span> to all three nodes' run lists. Then came my first stumbling block: by default this recipe will try to initiate the replica set from all three nodes but only the primary can do that so, for the other two nodes, you need to add the following attribute:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New'">
{
"mongodb": {
"auto_configure": {
"replicaset": false
}
}
}
</pre>
<p>Then on all three nodes you need to add the following (this can be combined with the other mongodb attributes, it's just shown separately here for convenience):</p>
<pre style="background: #EEEEEE; font-family: 'Courier New'">
{
"mongodb": {
"cluster_name": "ClusterName",
"replicaset_name": "ReplicasetName"
}
}
</pre>
<p>And finally on the arbiter node you need to add:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New'">
{
"mongodb": {
"arbiter": true
}
}
</pre>
<p>The next problem was that, at the time of writing, this cookbook does not support authentication. There is a <a href="https://github.com/edelight/chef-mongodb/pull/48" target="_blank">pull request</a> to add <a href="http://docs.mongodb.org/manual/tutorial/enable-authentication/" target="_blank">keyFile</a> support (which is how you do authentication for replica sets) but it has not been actioned yet. We forked the repository and pulled in these changes ourselves. Once done you then need to generate <a href="http://docs.mongodb.org/manual/tutorial/generate-key-file/" target="_blank">suitable keyFile contents</a> then specify the following attribute on all three nodes:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New'">
{
"mongodb": {
"key_file": "KeyFileContentGoesHere"
}
}
</pre>
<div style="background-color: #F5F6CE;">
<p>
<strong>Non-Chef note:</strong><br />
If you're doing this manually you'll need to make sure you start mongo with the following arguments (Chef does this for you):
<p>
<pre style="font-family: 'Courier New'">
--replSet ReplicasetName --keyFile /path/to/keyFile
</pre>
</div>
<p>The next issue was that this cookbook has been written for <a href="http://docs.opscode.com/chef_overview_server.html" target="_blank">Chef-Server</a> and tries to search for the nodes of the replica set to initiate. <a href="http://docs.opscode.com/chef_solo.html" target="_blank">Chef-Solo</a>, however, does not support searching. Edelight offer a workaround for this with <a href="https://github.com/edelight/chef-solo-search" target="_blank">chef-solo-search</a>. I didn't actually make use of this, we just hard-coded our list of nodes into some attributes but that's a bit hacky.</p>
<p>On to the next problem: the set members contact each other via their domain names. Not all of our servers had domain names set up so we needed to add hosts entries to each server in the set. In Chef we did this with <a href="https://github.com/customink-webops/hostsfile" target="_blank">CustomInk's Hostsfile cookbook</a>.</p>
<p>The sequence in which you bring up the replica set nodes is somewhat important in that they must all be operational before you can initiate the set, therefore the primary has to come up last.<br />
I provisioned the arbiter first (which we were already using for something else) and ensured that mongodb was running and that I hadn't broken anything else. I then provisioned our secondary and checked that mongo was running and that it could ping the domain name of the arbiter (which was in the hosts file) and vice-versa. I then provisioned the primary which initiated the replica set. After about a minute it was up and running.</p>
<p>Well... there was a little more to it than that.<br />
We already had the 'primary' MongoDB server live but just as a standalone, we'd later decided to make it a replica set. We'd already set up authentication on the standalone and that's where things got complicated. In order to initiate a replica set on a MongoDB instance with authentication you must be authenticated as a user with the <strong><a href="http://docs.mongodb.org/manual/reference/user-privileges/#clusterAdmin" target="_blank">clusterAdmin</a></strong> role. If you don't you will simply get an “unauthenticated” error which, if you're like me, will make you tear your hair out as you're sure you're entering the right credentials.<br />
The other issue is that the edelight cookbook does not handle authenticating before trying to initiate the replica set so we had to add this in. It's very simple, in libraries/mongodb.rb, in the configure_replicaset define, just before the command to initiate the replica set add:</p>
<pre style="background: #EEEEEE; font-family: 'Courier New'">
admin.authenticate("username", "password")
</pre>
<p>As long as that user has the aforementioned clusterAdmin role you should be set.</p>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com2tag:blogger.com,1999:blog-1469618916373324201.post-36066242982778054422013-02-25T12:17:00.000-08:002013-02-25T12:17:42.561-08:00SimpleXML and large files<p>
I encountered an issue earlier today whilst trying to process a fairly large (~37MB) XML file through <a href="http://www.php.net/manual/en/book.simplexml.php" target="_blank">SimpleXML</a>.
</p>
<p>
It worked just fine on mine and a colleague's development systems but failed with weird errors on the live server, for instance reporting there was "Extra content at the end of the document".
</p>
<p>
After about an hour of trying to figure it out we realised our dev systems were using <a href="http://www.xmlsoft.org/" target="_blank">libxml</a> v2.6.x and the live server was using 2.7.6. We then read that somewhere between those versions some hard-coded limits were added in that can cause the problem we were seeing.
</p>
<p>
To get around it you need to specify the <a href="http://php.net/manual/en/libxml.constants.php" target="_blank">LIBXML_PARSEHUGE</a> flag:
</p>
<div style="background: #EEEEEE; font-family: 'Courier New'">
$simplexml = simplexml_load_file($file_path, 'SimpleXMLElement', LIBXML_PARSEHUGE);<br />
//OR<br />
$simplexml = new SimpleXMLElement($xml_string, LIBXML_PARSEHUGE);
</div>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-1862651701584881532013-02-09T06:35:00.000-08:002013-02-09T06:35:21.511-08:00Documentation using ApiGen and Swagger UI<p>At work we're writing an API and an SDK that'll talk to that API. We hope to one day make this SDK available to third parties and so we wanted to ensure there was good documentation for it.</p>
<p>I suggested early on that the code itself should be where we write the documentation (in DocBlocks) and then generate external documentation from there. That way both the code and the documentation are complete and consistent with each other.</p>
<p>This suggested the use of something like <a href="http://www.phpdoc.org/" target="_blank">PHPDocumentor</a> but that's a little long-in-the-tooth and produces rather dry-looking documentation (in our opinion). <a href="http://apigen.org/" target="_blank">ApiGen</a> is similar to PHPDoc but is a bit more modern with support for namespaces, traits, etc but still just produces boring HTML by default.</p>
<p>My boss had encountered <a href="http://developers.helloreverb.com/swagger/" target="_blank">Swagger UI</a> which is a collection of JavaScript and CSS files that render nice looking docs. The problem is that Swagger is designed specifically for <a href="http://en.wikipedia.org/wiki/Representational_state_transfer" target="_blank">RESTful</a> APIs, not PHP SDKs.</p>
<p>I looked into it though and found at least a partial solution:<br />
ApiGen allow you to write your own templates (PHPDoc also has this) and the input to Swagger UI is just a collection of <a href="http://en.wikipedia.org/wiki/JSON" target="_blank">JSON</a> files and so we decided to write ApiGen templates that produced Swagger UI JSON.</p>
<p>Swagger UI needs an 'index' JSON file that defines what APIs are available and then a JSON file for each of those APIs. In our case we had a file per Class in the SDK. In each of the Class JSON files you then define the (public) methods.</p>
<p>The spec for Swagger UI JSON files is here:
<ul>
<li>index file or Resource Listing: <a href="https://github.com/wordnik/swagger-core/wiki/Resource-Listing" target="_blank">https://github.com/wordnik/swagger-core/wiki/Resource-Listing</a></li>
<li>API files or API Declaration: <a href="https://github.com/wordnik/swagger-core/wiki/API-Declaration" target="_blank">https://github.com/wordnik/swagger-core/wiki/API-Declaration</a></li>
</ul>
</p>
<p>ApiGen templates use <a href="http://nette.org/" target="_blank">Nette Framework</a> Latte Templates: <a href="http://doc.nette.org/en/templating" target="_blank">http://doc.nette.org/en/templating</a></p>
<p>We wrote an <em>overview.latte</em> file that loops all the classes and enumerates them and a <em>class.latte</em> file that loops all the methods of a class and details those.</p>
<p>I said this was a partial solution - we have got all this working nicely but Swagger UI still <em>looks</em> like it's describing a RESTful interface. For example: for every method we defined we had to say whether it was GET, PUT or POST which obviously isn't relevant. That said you can edit the Swagger JavaScript to do whatever you want so you can change things like the above.</p>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-67566567480161206082011-11-12T04:44:00.000-08:002011-11-12T04:58:57.622-08:00Rename a MySQL databaseRecently I needed to rename a MySQL database I was working with to make way for another with the same name. When I looked around for how to do this it seemed that the only option was to create the new database and then copy each table to it, via INSERT...SELECTs or a dump and import. It was quite a large database and this would have been very time consuming.<div><br /></div><div>After discussing it with a colleague we came up with another way to do it by using bash to iterate over each table in the schema and then use the RENAME TABLE command:</div><br /><div style="background: #EEEEEE; font-family: 'Courier New'">for TABLE in `mysql -u[USERNAME] -p[PASSWORD] [OLD DB] -e "SHOW TABLES" -B -N -s`; do mysql -u[USERNAME] -p[PASSWORD] [OLD DB] -e "RENAME TABLE [OLD DB].$TABLE TO [NEW DB].$TABLE"; done;<br /></div><div><br /></div><div>This worked a treat (I've been using my new database reliably for a while now) and was fast too!</div>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com1tag:blogger.com,1999:blog-1469618916373324201.post-9929504671108284222010-09-26T04:36:00.001-07:002010-09-26T04:45:34.897-07:00Symlinks in WindowsNot strictly a <i>L</i>AMP tip this as it's for Windows but I found it useful recently.<br /><br />I'm using the rather awesome <a href="http://www.apachefriends.org/en/xampp.html" target="_blank">XAMPP</a> to do some development for one of my personal sites on my Windows machine and I wanted to symlink my codebase into XAMPP's equivalent of a 'public_html' folder (they call it 'htdocs'). I tried simply creating a shortcut but this doesn't work as a shortcut isn't a direct pointer but is a file in itself so Apache just tries to read that file.<br /><br />After much Googling I discovered that, since Vista, Microsoft have introduced a symlinking function, MKLINK:<br /><br /><div style="font-family: 'Courier New';background:#eeeeee"><br />MKLINK /D C:\path\to\link C:\path\to\target<br /></div><br />The <i>/D</i> specifies a link to a directory as the default is to a file.<br /><br />This worked a treat on my Windows 7 machine! Who knew Vista introduced something useful?! ;)Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-30343417729635327762010-04-19T11:32:00.000-07:002010-04-19T11:40:14.001-07:00Colour Schemes<div>This is more for designers than developers but some of us have to do both :)</div><div><br /></div><div>When coming up with a colour scheme for a site or UI it's a good idea to use colours that <a href="http://en.wikipedia.org/wiki/Complimentary_colors">compliment</a> each other. This isn't just a design thing, it's partly mathematical and there are some sites out there that can calculate compatible colours for you.</div><div>This is a good example: <a href="http://colorschemedesigner.com/">http://colorschemedesigner.com/</a></div><div><br /></div><div>Another thing that can be useful is the ability to blend two colours together. <a href="http://meyerweb.com/eric/tools/color-blend/">http://meyerweb.com/eric/tools/color-blend/</a> allows you to input two hex colour values and it will give you the resulting hex colour value.</div><div><br /></div><div><br /></div>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-78982607625748982812010-04-09T10:50:00.000-07:002010-04-09T11:10:37.942-07:00PHP-GTK TipsI've been developing a PHP-GTK application over the past few months and have found that there are very few resources out there to help (the entire project seems a bit dead tbh..), however I have come across a few invaluable things that I thought I'd share:<div><br /></div><div><a href="http://www.kksou.com/php-gtk2">php-gtk2 Cookbook</a>: This is by far the best resource I have found. It has loads of examples and you can even look them up by widget type. It also has a help forum full of useful answers.</div><div>You have to register to see all of the code but it's free and I haven't had any spam as a result so I highly recommend it.</div><div><br /></div><div><a href="http://gtk.php.net/manual/en/reference.php">Official API documentation</a>: This is a bit feeble but contains all the classes and methods (even if it doesn't explain them very well).</div><div>One thing to watch out for with this is that the class pages don't show you inherited methods (even from interfaces) so you have to read through the entire hierarchy to get the full picture.</div><div><br /></div><div><a href="http://glade.gnome.org/">Glade UI Designer</a>: A handy tool for constructing interfaces for GTK. These produce an XML file which you can then read into your PHP code with the GTKBuilder class. Much nicer than having to create all of your interfaces in code.</div><div>Be wary though that it is a bit flakey (crashes sometimes) and it can only do so much for you, some of it will still need to be done in code.</div><div><br /></div><div>When looking for other resources remember that GTK isn't specific to PHP and so you may find more results for just searching for GTK stuff.</div><div><br /></div><div>Hope that helped!</div>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-7849551363917923332009-10-10T04:32:00.000-07:002009-10-10T04:44:04.288-07:00No disk space causes random errorsI'm sure most of you are aware of this but it's something that always takes me a long time to remember.<br /><br />Whenever you have a seemingly random error on your Linux box - strange MySQL or Apache errors, being unable to write to a file that you could write to a moment ago, etc - always do a quick <span style="background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;font-family:'Courier New';" >df -h</span> to check how much disk space you've got left.<br /><br />This problem is especially evident for those of us developing on virtual machines that often have limited disk space and so can run out easily. A quick recce of your <span style="background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;font-family:'Courier New';" >/var/log/</span> (or equivalent) directory is a good starting point for freeing up space but you may also want to try <span style="background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;font-family:'Courier New';" >find / -type f -size +50000k</span>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-48771234682591749472009-08-12T11:16:00.000-07:002009-08-12T11:32:24.792-07:00ExtJS Store ResetI recently discovered something rather annoying about <a href="http://www.extjs.com">ExtJS</a> Stores.<br />You may know that you can request all of the modified Records from a Store with something like:<br /><br /><div style="background: #EEEEEE; font-family: 'Courier New'">var arr_modified = obj_store.getModifiedRecords();<br /></div><br />This can be very useful if you have a lot of Records but only want to perform actions on the ones that have been edited by the user.<br />However, what I didn't realise is that the modified Records are held separately so that if you empty the Store (by calling <i>removeAll()</i>) and repopulate it then call <i>getModifedRecords()</i> again not only do you get any modified Records from the new batch but from the old batch also.<br /><br />The remedy to this is quite simple, upon emptying the Store you must also call <i>rejectChanges()</i> which clears the cache of changed Records (this is complimentary to <i>commitChanges()</i> which performs basically the same action but for a different reason).Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-78722903204077966612009-02-06T11:06:00.000-08:002009-02-06T11:17:52.783-08:00"shell_exec(): Unable to execute"I spent ages trying to get round this PHP warning today:<br /><br /><div style="font-family: 'Courier New'; background: #eeeeee;">"PHP Warning: shell_exec(): Unable to execute '[any bash command]'"</div><br /><br />I hadn't changed anything and it was working 5mins before so what was wrong?<br /><br />A colleague of mine suggested restarting Apache... and it worked!<br />I don't fully understand why, something to do with Apache running out of resources or something. Anywho, thought it might be useful for someone.Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com1tag:blogger.com,1999:blog-1469618916373324201.post-68885917647068157852008-06-10T11:03:00.000-07:002008-06-15T02:27:06.268-07:00Comet server-push frameworkI've just been asked to look into a way to 'stream' information from the server to any listening clients. Basically it's a <a href="http://en.wikipedia.org/wiki/Push_technology" target="_blank">server-push</a> rather than <a href="http://en.wikipedia.org/wiki/Pull_technology" target="_blank">client-pull</a> system where changes made on one client are (almost) immediately reflected on all other clients. This is similar to AJAX in that there is no page refresh but it is more autonomous.<br /><br />I've been reading up on <a href="http://en.wikipedia.org/wiki/Comet_%28programming%29" target="_blank">Comet</a>, which is basically just a term for a kind of AJAX solution to this problem, but it doesn't specify any implementation suggestions.<br /><br /><a href="http://svn.xantus.org/shortbus/trunk/bayeux/bayeux.html" target="_blank">Bayeux</a> takes this a step further by actually outlining a protocol for message transfer between client and server.<br /><br />Most implementations on the server-side are in Java but we need PHP. I've tracked down a PHP implementation called <a href="http://sourceforge.net/projects/comet/" target="_blank">Comep</a> that works quite well on the server-side (although its procedural rather than OO based) and I'm going to look into replacing its vanilla-javascript client-side with this <a href="http://plugins.jquery.com/project/Comet" target="_blank">jQuery Comet plugin</a>.<br /><br />For any other LAMP developers interested in Comet a good place to start is the <a href="http://cometdaily.com/" target="_blank">Comet Daily Blog</a>.<br /><br /><b>Update, 2008-06-15:</b> Although PERL rather than PHP based it is also worth mentioning <a href="http://meteorserver.org/">Meteor</a>, which is a highly polished comet server implementation.Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-58717891635991431222008-05-01T11:52:00.000-07:002008-05-01T12:24:46.847-07:00Custom ExtJS header buttons<a href="http://www.extjs.com">ExtJS</a> Panels can have a limited range of buttons on the header bar but what if you want a custom button? These can be added to a toolbar just underneath the header but if space is at a premium or perhaps you only want one button it's not always ideal.<br /><br />One solution is to have no header and just make the toolbar look like a header:<br /><br /><div style="font-family: 'Courier New'; background: #eeeeee;">var obj_panel = new Ext.Panel({<br /> tbar: new Ext.Toolbar({<br />// make the toolbar background look like a header<br /> cls: 'x-panel-header',<br /> height: 25,<br /> items: [<br />// give the toolbar a title which looks like a Panel title<br /> '<span style="color:#15428B; font-weight:bold">Title Here</span>',<br /> '->', //fill element<br /> {<br /> text: 'Button Text',<br /> iconCls: 'add', // optional icon class<br /> handler: function() {<br /> // do stuff here<br /> }<br /> },' ']<br /> }),<br />// ...<br />});<br /></div><br />(The styles used above fit the standard ExtJS theme)<br /><br />This produces something like this:<br /><img src="http://images.lilchef.co.uk/header_toolbar.jpg" width="409" height="87" style="border: 0px; padding: 0px;" />Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com6tag:blogger.com,1999:blog-1469618916373324201.post-4029242807739114992008-04-19T03:57:00.000-07:002015-01-03T08:34:05.986-08:00Extending classes in JavaScript<p><strong>Note:</strong> This post was originally written in 2008. There's a <a href="#update-2015">2015 update at the bottom</a>.</p>
There are many different ways to achieve Object Orientation in JavaScript and the Internet is littered with examples.<br />
<br />
<a href="https://www.blogger.com/jquery.com">jQuery</a> Uses an extend method within the super-class that allows you to add functionality onto an <span style="font-weight: bold;">instance</span> of that class, creating a kind of sub-class (although it is an object):<br />
<br />
<div style="background: #FFCCFF; font-family: 'Courier New';">
function SuperClass() {}<br />
SuperClass.prototype = {<br />
// ...<br />
extend: function() {<br />
var int_count = 0;<br />
var arr_prop;<br />
while ( (arr_prop = arguments[int_count++]) != null ) {<br />
for ( var i in arr_prop ) {<br />
this[i] = arr_prop[i];<br />
}<br />
}<br />
// Return the modified object<br />
return this;<br />
} <br />
}<br />
<br />
// ...<br />
<br />
var obj_subclass = new SuperClass();<br />
obj_subclass.extend({<br />
foo: 'bar',<br />
// ...<br />
});</div>
<br />
This is quick and dirty but not proper OO and if you want several instances of the 'sub-class' you need to call extend on each one.<br />
<br />
You probably already know that to add member variables/methods to a class you assign them to its <span style="font-style: italic;">prototype</span> property. This can be one value at a time:<br />
<br />
<div style="background: #FFCCFF; font-family: 'Courier New';">
function SuperClass() {}<br />
SuperClass.prototype.foo = 'bar';</div>
<br />
Or a simple object can be assigned:<br />
<br />
<div style="background: #FFCCFF; font-family: 'Courier New';">
function SuperClass() {}<br />
SuperClass.prototype = {<br />
foo: 'bar',<br />
x: 'y',<br />
super_func: function() {<br />
//...<br />
}<br />
}</div>
<br />
A good way to simulate inheritance is to assign an instance of the super-class to the sub-class' prototype <a href="http://www.devarticles.com/c/a/JavaScript/Object-Oriented-JavaScript-Using-the-Prototype-Property/2/">example here</a>:<br />
<br />
<div style="background: #FFCCFF; font-family: 'Courier New';">
var obj_super = new SuperClass();<br />
function SubClass() {}<br />
SubClass.prototype = obj_super;</div>
<br />
This means that all member variables/functions of SuperClass are now contained within SubClass' prototype.<br />
<br />
In order to now extend SuperClass' functionality in SubClass we revert back to adding one new thing at a time with <span style="font-style: italic;">SubClass.prototype.sub_func = function() { //... }</span>. This is fine but if you plan on adding a lot more stuff it's not ideal.<br />
<br />
The solution I have come up with <span style="font-style: italic;">(I say "I", maybe other people do this but I can't find any mention of it on the net)</span> is to combine the two methods above:<br />
<br />
<div style="background: #EEEEEE; font-family: 'Courier New';">
var obj_super = new SuperClass();<br />
function SubClass() {}<br />
SubClass.prototype = obj_super.extend({<br />
foo: 'bar',<br />
sub_func: function() { //... },<br />
// ...<br />
});</div>
<br />
This way we are still creating a proper sub-class and inheriting the super-classes functionality whilst conveniently adding our own.<br />
<span style="font-style: italic;"><br />Note: You can call super-class methods directly from within the sub-class with SuperClass.prototype.super_func.call(this, arg1, arg2, argN);</span>
<br />
<h3 id="update-2015">Update (03/01/2015)</h3>
<p>The above used to be how I extended 'classes' in JavaScript but these days I use a different method. Again, there are many ways to achieve JS extension, what follows is just my preference.</p>
<p>This method has two main requirements:
<ol>
<li>You call the constructor of the parent inside the constructor of the child using the child's context</li>
<li>You copy the parents prototype onto the child's prototype via the Object.create method
</ol>
For example:</p>
<div style="background: #EEEEEE; font-family: 'Courier New';"><pre>function SuperClass()
{
var a = 1;
this.getA = function()
{
return a;
};
}
SuperClass.prototype.functionA = function()
{
alert('SuperClass functionA');
};
function SubClass()
{
SuperClass.call(this);
var b = 2;
this.getB = function()
{
return b;
};
}
SubClass.prototype = Object.create(SuperClass.prototype);
SubClass.prototype.functionB = function()
{
var aPlusB = this.getA() + this.getB();
alert('SubClass functionB. a+b == '+aplusB);
};
var eg = new SubClass();
eg.functionA(); // alerts "SuperClass functionA"
eg.functionB(); // alerts "SubClass functionB. a+b == 3"
</pre></div>
<p>Now SubClass extends SuperClass. So why not use <code>SubClass.prototype = new SuperClass();</code> you ask? Well there's a subtle difference between <code>new</code> and <code>Object.create()</code> in that the latter doesn't actually call SuperClass's constructor, it just copies the prototype so the SuperClass constructor will only get called at the time of creation of the SubClass (as we are calling it explicitly with <code>SuperClass.call(this)</code>).</p>
<p>Note that, as with the old method from this post, the way to call SuperClass function directly is with <code>SuperClass.prototype.functionName.call(this, arg1, argN)</code></p>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-61927856551894850102008-04-09T12:20:00.000-07:002008-04-09T13:04:16.073-07:00Creating a basic select box in ExtJS<style>.comment { color: #999999; font-size: 0.8em; }</style><br />I'm currently working on a project that involves generating an entirely <a href="http://www.extjs.com">ExtJS</a>-based interface from PHP code.<br /><br />I needed to be able to produce in ExtJS the equivelent of a standard HTML <span style="font-style:italic;"><select></span> box. This can be achieved with a <span style="font-style:italic;">ComboBox</span>, however, it seems that ComboBoxes are heavily geared towards doing lots of clever things like loading in options over AJAX, allowing the user to type in their own options, auto-completing typed in options, etc. That's all great but if all you want is a standard drop-down with a predefined set of options it takes a bit of work.<br /><br />I incorrectly assumed that something like the following would work:<br /><div style="font-family: 'Courier New'; background: #ffccff;">obj_combo = new Ext.form.ComboBox({<br /> name: 'countries',<br /> items: [<br /> {value: '1', text: 'UK'},<br /> {value: '2', text: 'US'},<br /><span style="font-style:italic;" class="comment">// ...</span><br />]<br />});<br /></div><br />Unfortunaltey, it's not that simple.<br /><br />Here's an example of the actual code required:<br /><div style="font-family: 'Courier New'; background: #eeeeee;">obj_combo = new Ext.form.ComboBox({<br /> name: 'countries',<br /><span style="font-style:italic;" class="comment">// Stop users being able to type in the combobox</span><br /> editable: false,<br /><span style="font-style:italic;" class="comment">// Even though the user cant type any more<br />// once they select one option it'll remove any<br />// others that don't start with the same letters<br />// unless we turn off filtering</span><br /> disableKeyFilter: true,<br /><span style="font-style:italic;" class="comment">// Only allow users to pick an option that exists<br />// in the list of options (not one of their own)</span><br /> forceSelection: true,<br /><span style="font-style:italic;" class="comment">// This isn't entirely necessary but the combox<br />// will start off blank otherwise</span><br /> emptyText: '--select one--',<br /><span style="font-style:italic;" class="comment">// This one's vital: when the user clicks on the<br />// drop-down show ALL options</span><br /> triggerAction: 'all',<br /><span style="font-style:italic;" class="comment">// By default it retrieves remote data,<br />// we're using local data</span><br /> mode: 'local',<br /><span style="font-style:italic;" class="comment">// ComboBox will only accept data from a Store<br />// so we have to create a basic one</span><br /> store: new Ext.data.SimpleStore({<br /> id: 0,<br /> fields: ['value', 'text'],<br /> data : [['1', 'UK'], ['2', 'US']]<br /> }),<br /><span style="font-style:italic;" class="comment">// Specify which fields in the store hold<br />// the value and the display text</span><br /> valueField: 'value',<br /> displayField: 'text',<br /><span style="font-style:italic;" class="comment">// Important: by default the POST/GET data<br />// for this item will contain the <b>display text</b><br />// not the value. This option creates a hidden field<br />// with the same name as the dropdown containing the<br />// selected value so it is that which gets returned</span><br /> hiddenName: 'countries'<br />});<br /></div><br />With all those set (plus any of your own) you should have the equivalent of an HTML select box in ExtJS.<br /><br />For good examples of different ComboBoxes (but unfortunately little explanation of them) check this link:<br /><a href="http://extjs.com/deploy/ext/examples/form/combos.html">http://extjs.com/deploy/ext/examples/form/combos.html</a>Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com7tag:blogger.com,1999:blog-1469618916373324201.post-14259918328946605652008-04-04T12:46:00.000-07:002008-04-04T13:18:18.687-07:00Traversing XML in ExtJSWe've just started using the JavaScript <a href="http://www.extjs.com">ExtJS</a> framework for a new project and whilst it is very good at presentational stuff we have found it to be lacking in the DOM manipulation and traversal department.<br /><br />We use XML a lot to send data back and forth and so we needed to use that instead of ExtJS' preferred JSON. That in itself is fine, ExtJS provides an XmlReader class that can interpret XML. However, this is geared towards reading in a result set for use in a grid, etc. We wanted to simply return some messages and get ExtJS to read them.<br />Not impossible I'm sure but we we're struggling with it.<br /><br />Long story short: we decided to use <a href="http://jquery.com">jQuery</a> for that bit :)<br />We were already very familiar with jQuery and where it is not as good at presentation as ExtJS it makes up for it in it's superior (imo) DOM traversal and manipulation.<br /><br />ExtJS and jQuery can co-exist quite peacefully so this was no problem, here's a sample:<br /><br /><div style="background:#eeeeee;font-family:'Courier New';">Ext.Ajax.request({<br /> // ..<br /> // success and failure functions are passed<br /> // the XMLHTTPRequest object<br /> success: function(obj_response) {<br /> var str_val=$('mynode',obj_response.responseXML).text();<br /> alert(str_val);<br /> }<br /> // ...<br />});<br /></div><span style="font-size:80%">(This assumes that, somewhere, you're including both the ExtJS and jQuery library js files)</span><br /><br />This would work for:<br /><br /><div style="background:#eeeeee;font-family:'Courier New';"><?xml version='1.0'?><br /><mynode>This would be the output</mynode><br /></div><br />Again, I'm sure ExtJS can do something similar but I couldn't get it to work.<br /><br />Bear in mind that other ExtJS classes that can make AJAX calls sometimes pass the success and fail functions another object which contains the XMLHTTPRequest object, so the call is something like <span style="font-style:italic;">obj_response.response.responseXML</span>. Check the <a href="http://extjs.com/deploy/dev/docs/">ExtJS API Docs</a> for more.<br /><br />If you are familiar with ExtJS and not jQuery, or vice-versa, I'd highly recommend reading up on both :)Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com0tag:blogger.com,1999:blog-1469618916373324201.post-65675990716624204612008-04-04T12:11:00.000-07:002008-04-04T12:31:02.517-07:00Exclude certain files from a cp commandI needed to be able to exclude files with a certain extension from a <span style="font-style: italic;">cp</span> command that was going to be executed automatically as part of a much larger script.<br /><br />There doesn't seem to be any direct way to do this (e.g. <span style="font-style: italic;">cp --exclude</span>...) and although there are several suggestions on the net on how to overcome this I found the neatest way was to use the <span style="font-style: italic;">tar</span> command instead:<br /><div style="font-family: 'Courier New';background:#eeeeee">cd /copy/from/path<br />tar -czf /path/to/archive.tar.gz ./ --exclude=*.txt<br />cd /copy/to/path<br />tar -xzvf /path/to/archive.tar.gz<br /></div><br />Of course this isn't ideal, creating a tar archive when it isn't necessary but the files I needed to copy weren't large and so it was nice and quick and dirty.Lilchefhttp://www.blogger.com/profile/05309198718613169438noreply@blogger.com1