<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: php</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/php.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-07-01T19:57:46+00:00</updated><author><name>Simon Willison</name></author><entry><title>A custom template system from the mid-2000s era</title><link href="https://simonwillison.net/2025/Jul/1/mid-2000s/#atom-tag" rel="alternate"/><published>2025-07-01T19:57:46+00:00</published><updated>2025-07-01T19:57:46+00:00</updated><id>https://simonwillison.net/2025/Jul/1/mid-2000s/#atom-tag</id><summary type="html">
    &lt;p&gt;Using LLMs for &lt;strong&gt;code archaeology&lt;/strong&gt; is pretty fun.&lt;/p&gt;
&lt;p&gt;I stumbled across &lt;a href="https://simonwillison.net/2003/Jul/17/phpAndColdFusion/"&gt;this blog entry from 2003&lt;/a&gt; today, in which I had gotten briefly excited about ColdFusion and implemented an experimental PHP template engine that used XML tags to achieve a similar effect:&lt;/p&gt;
&lt;pre&gt;&amp;lt;&lt;span class="pl-ent"&gt;h1&lt;/span&gt;&amp;gt;%title%&amp;lt;/&lt;span class="pl-ent"&gt;h1&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span class="pl-ent"&gt;sql&lt;/span&gt; &lt;span class="pl-e"&gt;id&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;recent&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
select title
from entries 
order by added desc
limit 0, %limit%
&amp;lt;/&lt;span class="pl-ent"&gt;sql&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span class="pl-ent"&gt;ul&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span class="pl-ent"&gt;output&lt;/span&gt; &lt;span class="pl-e"&gt;sql&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;recent&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;li&lt;/span&gt;&amp;gt;%title%&amp;lt;/&lt;span class="pl-ent"&gt;li&lt;/span&gt;&amp;gt;
  &amp;lt;/&lt;span class="pl-ent"&gt;output&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span class="pl-ent"&gt;ul&lt;/span&gt;&amp;gt;&lt;/pre&gt;

&lt;p&gt;I'd completely forgotten about this, and in scanning through the PHP it looked like it had extra features that I hadn't described in the post.&lt;/p&gt;
&lt;p&gt;So... I fed my 22 year old &lt;code&gt;TemplateParser.class.php&lt;/code&gt; file into Claude and prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Write detailed markdown documentation for this template language&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here's &lt;a href="https://static.simonwillison.net/static/2003/template-docs.html"&gt;the resulting documentation&lt;/a&gt;. It's pretty good, but the highlight was the &lt;a href="https://claude.ai/share/1627f1f3-4b07-4eb3-af24-5ac2da96b712"&gt;Claude transcript&lt;/a&gt; which concluded:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This appears to be a custom template system from the mid-2000s era, designed to separate presentation logic from PHP code while maintaining database connectivity for dynamic content generation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Mid-2000s era indeed!&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/coldfusion"&gt;coldfusion&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;&lt;/p&gt;



</summary><category term="coldfusion"/><category term="php"/><category term="projects"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="claude"/></entry><entry><title>ActivityPub Server in a Single PHP File</title><link href="https://simonwillison.net/2024/Feb/19/activitypub-server-in-a-single-php-file/#atom-tag" rel="alternate"/><published>2024-02-19T00:20:13+00:00</published><updated>2024-02-19T00:20:13+00:00</updated><id>https://simonwillison.net/2024/Feb/19/activitypub-server-in-a-single-php-file/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://shkspr.mobi/blog/2024/02/activitypub-server-in-a-single-file/"&gt;ActivityPub Server in a Single PHP File&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Terence Eden: “Any computer program can be designed to run from a single file if you architect it wrong enough!”&lt;/p&gt;

&lt;p&gt;I love this as a clear, easy-to-follow example of the core implementation details of the ActivityPub protocol—and a reminder that often a single PHP file is all you need.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/jynnm3/activitypub_server_single_php_file"&gt;lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mastodon"&gt;mastodon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/activitypub"&gt;activitypub&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/terence-eden"&gt;terence-eden&lt;/a&gt;&lt;/p&gt;



</summary><category term="php"/><category term="mastodon"/><category term="activitypub"/><category term="terence-eden"/></entry><entry><title>Making a Discord bot with PHP</title><link href="https://simonwillison.net/2024/Jan/14/making-a-discord-bot-with-php/#atom-tag" rel="alternate"/><published>2024-01-14T22:56:08+00:00</published><updated>2024-01-14T22:56:08+00:00</updated><id>https://simonwillison.net/2024/Jan/14/making-a-discord-bot-with-php/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.kryogenix.org/days/2024/01/14/making-a-discord-bot-with-php/"&gt;Making a Discord bot with PHP&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Building bots for Discord used to require a long-running process that stayed connected, but a more recent change introduced slash commands via webhooks, making it much easier to write a bot that is backed by a simple request/response HTTP endpoint. Stuart Langridge explores how to build these in PHP here, but the same pattern in Python should be quite straight-forward.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://mastodon.social/@sil/111756707673740628"&gt;@sil&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/stuart-langridge"&gt;stuart-langridge&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webhooks"&gt;webhooks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/discord"&gt;discord&lt;/a&gt;&lt;/p&gt;



</summary><category term="php"/><category term="stuart-langridge"/><category term="webhooks"/><category term="discord"/></entry><entry><title>hubcap.php</title><link href="https://simonwillison.net/2023/Sep/6/hubcap/#atom-tag" rel="alternate"/><published>2023-09-06T15:45:11+00:00</published><updated>2023-09-06T15:45:11+00:00</updated><id>https://simonwillison.net/2023/Sep/6/hubcap/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/dave1010/hubcap/blob/6c03365f7f4b68e9a60505cc53e45f201b63fc11/hubcap.php"&gt;hubcap.php&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This PHP script by Dave Hulbert delights me. It’s 24 lines of code that takes a specified goal, then calls my LLM utility on a loop to request the next shell command to execute in order to reach that goal... and pipes the output straight into `exec()` after a 3s wait so the user can panic and hit Ctrl+C if it’s about to do something dangerous!

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://medium.com/@dave1010/amazingly-alarming-autonomous-ai-agents-62f8a785e4d8"&gt;Amazingly Alarming Autonomous AI Agents&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm"&gt;llm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-agents"&gt;ai-agents&lt;/a&gt;&lt;/p&gt;



</summary><category term="php"/><category term="security"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="llm"/><category term="ai-agents"/></entry><entry><title>PHP 8.1 release notes</title><link href="https://simonwillison.net/2021/Nov/25/php-81-release-notes/#atom-tag" rel="alternate"/><published>2021-11-25T19:53:30+00:00</published><updated>2021-11-25T19:53:30+00:00</updated><id>https://simonwillison.net/2021/Nov/25/php-81-release-notes/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.php.net/releases/8.1/en.php"&gt;PHP 8.1 release notes&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
PHP is gaining “Fibers” for lightweight cooperative concurrency—very similar to Python asyncio. Interestingly you don’t need to use separate syntax like “await fn()” to call them—calls to non-blocking functions are visually indistinguishable from calls to blocking functions. Considering how much additional library complexity has emerged in Python world from having these two different colours of functions it’s noteworthy that PHP has chosen to go in a different direction here.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=29343773"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/async"&gt;async&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;&lt;/p&gt;



</summary><category term="async"/><category term="php"/></entry><entry><title>Quoting Rasmus Lerdorf</title><link href="https://simonwillison.net/2021/Nov/22/rasmus-lerdorf/#atom-tag" rel="alternate"/><published>2021-11-22T19:23:27+00:00</published><updated>2021-11-22T19:23:27+00:00</updated><id>https://simonwillison.net/2021/Nov/22/rasmus-lerdorf/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://news-web.php.net/php.internals/70691"&gt;&lt;p&gt;htmlspecialchars was a very early function. Back when PHP had less than 100 functions and the function hashing mechanism was strlen(). In order to get a nice hash distribution of function names across the various function name lengths names were picked specifically to make them fit into a specific length bucket. This was circa late 1994 when PHP was a tool just for my own personal use and I wasn't too worried about not being able to remember the few function names.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://news-web.php.net/php.internals/70691"&gt;Rasmus Lerdorf&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rasmus-lerdorf"&gt;rasmus-lerdorf&lt;/a&gt;&lt;/p&gt;



</summary><category term="php"/><category term="rasmus-lerdorf"/></entry><entry><title>Is greater comfort with Windows a good enough reason to switch from PHP to ASP.NET?</title><link href="https://simonwillison.net/2013/Dec/4/is-greater-comfort-with/#atom-tag" rel="alternate"/><published>2013-12-04T09:45:00+00:00</published><updated>2013-12-04T09:45:00+00:00</updated><id>https://simonwillison.net/2013/Dec/4/is-greater-comfort-with/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Is-greater-comfort-with-Windows-a-good-enough-reason-to-switch-from-PHP-to-ASP-NET/answer/Simon-Willison"&gt;Is greater comfort with Windows a good enough reason to switch from PHP to ASP.NET?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Learning Linux really isn't that hard, and it will dramatically increase your potential horizons as a programmer. Install Ubuntu on a virtual machine on your laptop and start running through some tutorials.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/aspnet"&gt;aspnet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="aspnet"/><category term="php"/><category term="web-development"/><category term="programming"/></entry><entry><title>How log in my e-mail and send sms via php?</title><link href="https://simonwillison.net/2013/Nov/25/how-log-in-my/#atom-tag" rel="alternate"/><published>2013-11-25T15:09:00+00:00</published><updated>2013-11-25T15:09:00+00:00</updated><id>https://simonwillison.net/2013/Nov/25/how-log-in-my/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-log-in-my-e-mail-and-send-sms-via-php/answer/Simon-Willison"&gt;How log in my e-mail and send sms via php?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you want to send SMS via PHP I suggest taking a look at &lt;span&gt;&lt;a href="http://www.twilio.com"&gt;www.twilio.com&lt;/a&gt;&lt;/span&gt; - they are pretty inexpensive and ridiculously easy to get started with.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sms"&gt;sms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="sms"/><category term="php"/><category term="web-development"/></entry><entry><title>What is the best framework to use, Yii or Ruby on Rails?</title><link href="https://simonwillison.net/2013/Oct/17/what-is-the-best/#atom-tag" rel="alternate"/><published>2013-10-17T18:38:00+00:00</published><updated>2013-10-17T18:38:00+00:00</updated><id>https://simonwillison.net/2013/Oct/17/what-is-the-best/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-is-the-best-framework-to-use-Yii-or-Ruby-on-Rails?no_redirect=1"&gt;What is the best framework to use, Yii or Ruby on Rails?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a big decision, and it's worth taking the time to pick what's the best fit for you. I recommend going through the tutorials for each one, building the basic application they describe and seeing which made the most sense to you. As a Django developer, I suggest trying that framework too :)&lt;/p&gt;

&lt;p&gt;If you just want a recommendation on Rails vs Yii, I would go for Rails. Ruby is a less frustrating language to develop in than PHP and has a higher quality collection of other open source libraries you can use in your project. Rails is also a more mature and more widely used framework.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/startups"&gt;startups&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="php"/><category term="rails"/><category term="web-development"/><category term="startups"/><category term="programming"/></entry><entry><title>How can I get access to the PHP script of websites like Dropbox?</title><link href="https://simonwillison.net/2013/Oct/7/how-can-i-get/#atom-tag" rel="alternate"/><published>2013-10-07T16:39:00+00:00</published><updated>2013-10-07T16:39:00+00:00</updated><id>https://simonwillison.net/2013/Oct/7/how-can-i-get/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-can-I-get-access-to-the-PHP-script-of-websites-like-Dropbox/answer/Simon-Willison"&gt;How can I get access to the PHP script of websites like Dropbox?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If a website doesn't deliberately publish its server-side code (some sites like reddit do this, but it's pretty rare) then you won't be able to see it. You can search for an open source clone but these will often be pretty low quality - the smartest open source developers tend to work on libraries that solve common problems rather than putting their efforts in to building complete clones of existing sites.&lt;/p&gt;

&lt;p&gt;Dropbox in particularly is a poor fit for your question, because a lot of the secret sauce that makes the service work is in the client application that people install on the own devices. This is not the kind of software you would write in PHP.&lt;/p&gt;

&lt;p&gt;Dropbox's functionality is pretty unique, and I would be very surprised if anyone has written a tutorial that was relevant to building your own implementation - there might be academic papers you could dig up though, or conference presentations.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dropbox"&gt;dropbox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cloud"&gt;cloud&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="dropbox"/><category term="cloud"/><category term="php"/><category term="web-development"/></entry><entry><title>Does creating a zip file with php use more RAM then CPU? Or vise versa? Also, is it faster to use a system call, or the php zip library? Exec (zip filename.zip) vs. $zip =new ZipArchive().</title><link href="https://simonwillison.net/2013/Jul/29/does-creating-a-zip/#atom-tag" rel="alternate"/><published>2013-07-29T18:29:00+00:00</published><updated>2013-07-29T18:29:00+00:00</updated><id>https://simonwillison.net/2013/Jul/29/does-creating-a-zip/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Does-creating-a-zip-file-with-php-use-more-RAM-then-CPU-Or-vise-versa-Also-is-it-faster-to-use-a-system-call-or-the-php-zip-library-Exec-zip-filename-zip-vs-zip-new-ZipArchive/answer/Simon-Willison"&gt;Does creating a zip file with php use more RAM then CPU? Or vise versa? Also, is it faster to use a system call, or the php zip library? Exec (zip filename.zip) vs. $zip =new ZipArchive().&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can find out the answer yourself using a very simple benchmark - just call time() before and after each option and loop them a few thousand times to calculate an average.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="php"/><category term="web-development"/><category term="programming"/></entry><entry><title>Which should I learn: Python or PHP?</title><link href="https://simonwillison.net/2013/Apr/16/which-should-i-learn/#atom-tag" rel="alternate"/><published>2013-04-16T16:01:00+00:00</published><updated>2013-04-16T16:01:00+00:00</updated><id>https://simonwillison.net/2013/Apr/16/which-should-i-learn/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Which-should-I-learn-Python-or-PHP/answer/Simon-Willison"&gt;Which should I learn: Python or PHP?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Python will teach you more about programming than PHP - and you'll be able to learn PHP easily if you learn Python first.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming-languages"&gt;programming-languages&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="php"/><category term="python"/><category term="programming"/><category term="programming-languages"/></entry><entry><title>System Administration: What service/product do you recommend for central logging of events and errors from multiple servers? Why?</title><link href="https://simonwillison.net/2012/Feb/15/system-administration-what-serviceproduct/#atom-tag" rel="alternate"/><published>2012-02-15T18:26:00+00:00</published><updated>2012-02-15T18:26:00+00:00</updated><id>https://simonwillison.net/2012/Feb/15/system-administration-what-serviceproduct/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/System-Administration-What-service-product-do-you-recommend-for-central-logging-of-events-and-errors-from-multiple-servers-Why/answer/Simon-Willison"&gt;System Administration: What service/product do you recommend for central logging of events and errors from multiple servers? Why?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We rolled our own solution to this using MongoDB, due to its super-fast writes and ability to store, index and search JSON. We were also attracted by its capped collections, which make it easy to e.g. only log the last 100,000 items.&lt;/p&gt;

&lt;p&gt;It hasn't given us any problems, but we also haven't spent the time to build a good UI for it do we aren't getting as much value out of it as we could. That's the disadvantage of rolling your own: you have to build the whole thing.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/logging"&gt;logging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sysadmin"&gt;sysadmin&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cloud"&gt;cloud&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="logging"/><category term="sysadmin"/><category term="cloud"/><category term="php"/></entry><entry><title>How can you build a search engine for a website built in PHP/MySQL?</title><link href="https://simonwillison.net/2012/Feb/11/how-can-you-build/#atom-tag" rel="alternate"/><published>2012-02-11T18:39:00+00:00</published><updated>2012-02-11T18:39:00+00:00</updated><id>https://simonwillison.net/2012/Feb/11/how-can-you-build/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-can-you-build-a-search-engine-for-a-website-built-in-PHP-MySQL/answer/Simon-Willison"&gt;How can you build a search engine for a website built in PHP/MySQL?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are a bunch of options.&lt;/p&gt;

&lt;p&gt;The easiest to implement is to build search on top of MySQL LIKE queries - performance will be pretty terrible (since every search will require a full table scan) but provided your tables only have a few thousand records on them and your site doesn't have to cope with more than a dozen or so hits a second it should work fine.&lt;/p&gt;

&lt;p&gt;Next easiest: use MySQL's built-in full text indexing feature. It's not particularly good, and it requires you to use MyISAM tables (InnoDB is much more reliable, but doesn't support full text indexing) - but it will do the job. You could always keep your main site data in InnoDB and denormalise in to a MyISAM table just for search - or you could use the trick Flickr used to use, which is to set up MySQL replication and run MyISAM on one of the slaves purely to support fulltext search.&lt;/p&gt;

&lt;p&gt;Past that, you're looking at adding another component to the stack. Sphinx can integrate directly with MySQL and lets you run SQL-style queries against a proper full text index. Personally I'm a big fan of Solr, which runs as a separate (Java) server and requires you to index documents over HTTP. The great thing about Solr is that you can talk to it from any language that has an HTTP client library.&lt;/p&gt;

&lt;p&gt;The last option is to go for a hosted solution. Google Custom Search is free, but not particularly flexible. IndexTank was a good option here but they were acquired by LinkedIn and are shutting down the hosted service - they've since open sourced their software and other companies such as &lt;span&gt;&lt;a href="http://www.searchify.com/"&gt;http://www.searchify.com/&lt;/a&gt;&lt;/span&gt; are starting to offer it as a hosted solution.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/search-engines"&gt;search-engines&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-search"&gt;sphinx-search&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="mysql"/><category term="php"/><category term="search-engines"/><category term="sphinx-search"/></entry><entry><title>What are XML feed best practices?</title><link href="https://simonwillison.net/2012/Jan/31/what-are-xml-feed/#atom-tag" rel="alternate"/><published>2012-01-31T14:29:00+00:00</published><updated>2012-01-31T14:29:00+00:00</updated><id>https://simonwillison.net/2012/Jan/31/what-are-xml-feed/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-XML-feed-best-practices/answer/Simon-Willison"&gt;What are XML feed best practices?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It sounds like you're pretty much screwed already, if you're dealing with companies that still think FTPing XML around is a sensible thing to do.&lt;/p&gt;

&lt;p&gt;I would suggest focusing on what you can control. Assume that you will be passed bad data - weird formats, not-well-formed XML, duplicate entries etc. Your job is to handle all of this without going mad, and without your codebase turning in to an unmanageable ball of mud.&lt;/p&gt;

&lt;p&gt;So, start by figuring out your own core data model / abstraction. It will need to be VERY loose - as few required fields as possible, since you can be sure some if the feeds you are consuming will come in with stuff missing at some point or another.&lt;/p&gt;

&lt;p&gt;Separate your feed consumers from the rest of your code. Having your own good internal Web API (which could consume JSON rather than XML since you control it) might be smart, since that will provide a solid separation and you can then write all of your feed consumers as separate pieces of code that just POST new items to the API.&lt;/p&gt;

&lt;p&gt;Learn to love, respect and cherish unique identifiers... but be very wary of supposedly unique identifiers from external sources unless you can be absolutely sure they won't change on you. Create your own unique IDs at the first available opportunity, treat them properly within your own system and map external identifiers to them whenever you can.&lt;/p&gt;

&lt;p&gt;Write your consumers in a dynamic language with a solid interactive prompt, like Python or Ruby. This will make them much easier to write and debug. Use whatever you like for your core data storage / API.&lt;/p&gt;

&lt;p&gt;Since your incoming data will come in all shapes and sizes, consider a document store such as MongoDB or Riak over a SQL database. Avoiding SQL migrations will help you out a lot.&lt;/p&gt;

&lt;p&gt;Log and store absolutely everything. Ideally you should be able to re-execute every import that the system has ever executed, in order, to make debugging and fixing errors non terrifying. That will almost certainly prove impossible, but it's a nice thought.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/xml"&gt;xml&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="mysql"/><category term="xml"/><category term="php"/><category term="databases"/></entry><entry><title>Is there a framework that allows me to collect input from individual users, and then charge for the aggregate and analysis of that data?</title><link href="https://simonwillison.net/2012/Jan/2/is-there-a-framework/#atom-tag" rel="alternate"/><published>2012-01-02T14:04:00+00:00</published><updated>2012-01-02T14:04:00+00:00</updated><id>https://simonwillison.net/2012/Jan/2/is-there-a-framework/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Is-there-a-framework-that-allows-me-to-collect-input-from-individual-users-and-then-charge-for-the-aggregate-and-analysis-of-that-data/answer/Simon-Willison"&gt;Is there a framework that allows me to collect input from individual users, and then charge for the aggregate and analysis of that data?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No - your needs are extremely specific. You're going to have to build this yourself.&lt;/p&gt;

&lt;p&gt;(Even if there was an open source framework that covered the kind if thing you are talking about, my hunch is that by the time you had finished customising it to your exact needs you would have invested a similar amount of effort as you would building it from scratch)&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/frameworks"&gt;frameworks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="python"/><category term="php"/><category term="frameworks"/><category term="rails"/></entry><entry><title>What are some good examples of a first PHP app to build for someone who is learning?</title><link href="https://simonwillison.net/2012/Jan/2/what-are-some-good/#atom-tag" rel="alternate"/><published>2012-01-02T10:02:00+00:00</published><updated>2012-01-02T10:02:00+00:00</updated><id>https://simonwillison.net/2012/Jan/2/what-are-some-good/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-some-good-examples-of-a-first-PHP-app-to-build-for-someone-who-is-learning/answer/Simon-Willison"&gt;What are some good examples of a first PHP app to build for someone who is learning?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Build a blog. Blog engines are, in my opinion, the ideal starter project for learning any server-side web technology.&lt;/p&gt;

&lt;p&gt;They can be incredibly simple (just a reverse ordered list of posts - a single SQL query) and can be useful after implementing a tiny subset of functionality - but they offer almost limitless scope for extension and learning new things.&lt;/p&gt;

&lt;p&gt;Start with a single entries table and homepage. Now add a form for adding new entries. Then add edit and delete functionality. Add a simple login mechanism.&lt;/p&gt;

&lt;p&gt;Next, add tags (and learn about many-to-many database tables). Add pagination or month/year based archives.&lt;/p&gt;

&lt;p&gt;Next, an Atom feed (learning about syndication and XML output in the process).&lt;/p&gt;

&lt;p&gt;Add comments - and learn about XSS and other issues involved in accepting user-entered content.&lt;/p&gt;

&lt;p&gt;A blog engine us a great learning project because it can be as simple or complicated as you like.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="php"/></entry><entry><title>Can someone improve their knowledge of programming in Ruby or PHP by using a framework like Rails or Zend, or does the framework just do a lot of the work for you without giving you an opportunity to learn from it?</title><link href="https://simonwillison.net/2011/Dec/4/can-someone-improve-their/#atom-tag" rel="alternate"/><published>2011-12-04T13:04:00+00:00</published><updated>2011-12-04T13:04:00+00:00</updated><id>https://simonwillison.net/2011/Dec/4/can-someone-improve-their/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Can-someone-improve-their-knowledge-of-programming-in-Ruby-or-PHP-by-using-a-framework-like-Rails-or-Zend-or-does-the-framework-just-do-a-lot-of-the-work-for-you-without-giving-you-an-opportunity-to-learn-from-it/answer/Simon-Willison"&gt;Can someone improve their knowledge of programming in Ruby or PHP by using a framework like Rails or Zend, or does the framework just do a lot of the work for you without giving you an opportunity to learn from it?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Read the source, luke.&lt;/p&gt;

&lt;p&gt;(By which I mean, the most effective way to become a better programmer is to read good code written by other programmers. You can learn a great deal from reading Rails/Zend/Django)&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="rails"/><category term="php"/><category term="web-development"/><category term="programming"/></entry><entry><title>How are real time web applications achievable with PHP?</title><link href="https://simonwillison.net/2011/Jan/9/how-are-real-time/#atom-tag" rel="alternate"/><published>2011-01-09T15:59:00+00:00</published><updated>2011-01-09T15:59:00+00:00</updated><id>https://simonwillison.net/2011/Jan/9/how-are-real-time/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-are-real-time-web-applications-achievable-with-PHP/answer/Simon-Willison"&gt;How are real time web applications achievable with PHP?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You don't need to build your comet server using the same technology as the rest of your site.&lt;/p&gt;

&lt;p&gt;I build apps in Django, which isn't really suitable for comet due to its core expectation that an HTTP request/response cycle returns immediately rather than blocking. If I was building a comet application, I'd have the main site served by Django and then run a Node.js or Twisted or Tornado server on a separate subdomain (&lt;span&gt;&lt;a href="http://comet.example.com"&gt;comet.example.com&lt;/a&gt;&lt;/span&gt;) and have my comet calls served by that.&lt;/p&gt;

&lt;p&gt;Quora does something similar to this - if you run Firebug or similar you can see that Quora's comet servers run on subdomains with names like &lt;span&gt;&lt;a href="http://tch972015.tch.www.quora.com"&gt;tch972015.tch.www.quora.com&lt;/a&gt;&lt;/span&gt;. This also makes it easy to scale your comet stuff since you can just fire up more servers on more subdomains - you don't even need to load balance them (though you probably should). I believe they use the same Python stack for Comet and the main site, but there's no reason you have to.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webapps"&gt;webapps&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="webapps"/><category term="php"/><category term="web-development"/></entry><entry><title>What are the tradeoffs (e.g. development speed, performance, scalability) between using various php frameworks, ruby/rails, or python/django?  Is there any reason to choose one overwhelmingly over another?</title><link href="https://simonwillison.net/2010/Nov/27/what-are-the-tradeoffs/#atom-tag" rel="alternate"/><published>2010-11-27T18:18:00+00:00</published><updated>2010-11-27T18:18:00+00:00</updated><id>https://simonwillison.net/2010/Nov/27/what-are-the-tradeoffs/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-tradeoffs-e-g-development-speed-performance-scalability-between-using-various-php-frameworks-ruby-rails-or-python-django-Is-there-any-reason-to-choose-one-overwhelmingly-over-another/answer/Simon-Willison"&gt;What are the tradeoffs (e.g. development speed, performance, scalability) between using various php frameworks, ruby/rails, or python/django?  Is there any reason to choose one overwhelmingly over another?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At this point, I'd argue that the decision between them comes down to programming language rather than framework - the frameworks have mostly converged on a very similar set of features.&lt;/p&gt;

&lt;p&gt;All three solutions scale in exactly the same way (shared nothing architecture).&lt;/p&gt;

&lt;p&gt;If you're writing something for people to deploy on their own hosting plans, PHP is far more widely supported. For a service that you host yourself, you need to decide which language ecosystem is the best fit for how you like to develop.&lt;/p&gt;

&lt;p&gt;Update 14 months later: I'd argue today that thanks to hosting platforms such as &lt;span&gt;&lt;a href="http://ep.io"&gt;ep.io&lt;/a&gt;&lt;/span&gt;, Heroku and dotCloud PHP's hosting advantage isn't nearly as pronounced.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/frameworks"&gt;frameworks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="php"/><category term="python"/><category term="django"/><category term="frameworks"/><category term="rails"/><category term="web-development"/></entry><entry><title>In what circumstances should one use "magic quotes" in PHP? </title><link href="https://simonwillison.net/2010/Aug/25/in-what-circumstances-should/#atom-tag" rel="alternate"/><published>2010-08-25T15:05:00+00:00</published><updated>2010-08-25T15:05:00+00:00</updated><id>https://simonwillison.net/2010/Aug/25/in-what-circumstances-should/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/In-what-circumstances-should-one-use-magic-quotes-in-PHP/answer/Simon-Willison"&gt;In what circumstances should one use &amp;quot;magic quotes&amp;quot; in PHP? &lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Absolutely never. Magic quotes was a badly designed feature, and PHP has been trying to escape its legacy for years. If you are constructing SQL strings using string concatenation you're asking for trouble - use prepared statements or a library that interpolates and correctly escapes variables for you.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="quora"/><category term="security"/><category term="php"/><category term="web-development"/></entry><entry><title>Hookbox</title><link href="https://simonwillison.net/2010/Jul/29/hookbox/#atom-tag" rel="alternate"/><published>2010-07-29T09:48:00+00:00</published><updated>2010-07-29T09:48:00+00:00</updated><id>https://simonwillison.net/2010/Jul/29/hookbox/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://hookbox.org/"&gt;Hookbox&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
For most web projects, I believe implementing any real-time comet features on a separate stack from the rest of the application makes sense—keep using Rails, Django or PHP for the bulk of the application logic, and offload any WebSocket or Comet requests to a separate stack built on top of something like Node.js, Twisted, EventMachine or Jetty. Hookbox is the best example of that philosophy I’ve yet seen—it’s a Comet server that makes WebHook requests back to your regular application stack to check if a user has permission to publish or subscribe to a given channel. “The key insight is that all application development with hookbox happens either in JavaScript or in the native language of the web application itself”.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://cometdaily.com/2010/07/26/a-fast-introduction-to-hookbox/"&gt;Comet Daily&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/comet"&gt;comet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/michael-carter"&gt;michael-carter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webhooks"&gt;webhooks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/hookbox"&gt;hookbox&lt;/a&gt;&lt;/p&gt;



</summary><category term="comet"/><category term="django"/><category term="javascript"/><category term="michael-carter"/><category term="php"/><category term="rails"/><category term="webhooks"/><category term="recovered"/><category term="hookbox"/></entry><entry><title>The Onion Uses Django, And Why It Matters To Us</title><link href="https://simonwillison.net/2010/Mar/25/onion/#atom-tag" rel="alternate"/><published>2010-03-25T18:43:24+00:00</published><updated>2010-03-25T18:43:24+00:00</updated><id>https://simonwillison.net/2010/Mar/25/onion/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.reddit.com/r/django/comments/bhvhz/the_onion_uses_django_and_why_it_matters_to_us/"&gt;The Onion Uses Django, And Why It Matters To Us&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The Onion ported their main site from PHP and Drupal to Django in three months with a team of four developers, including a full migration of their archived content. Their developers answer questions about the switch in this thread on the Django sub-reddit.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/drupal"&gt;drupal&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/reddit"&gt;reddit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/the-onion"&gt;the-onion&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="drupal"/><category term="php"/><category term="python"/><category term="reddit"/><category term="the-onion"/></entry><entry><title>HipHop for PHP: Move Fast</title><link href="https://simonwillison.net/2010/Feb/2/facebook/#atom-tag" rel="alternate"/><published>2010-02-02T18:59:55+00:00</published><updated>2010-02-02T18:59:55+00:00</updated><id>https://simonwillison.net/2010/Feb/2/facebook/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://developers.facebook.com/news.php?blog=1&amp;amp;story=358"&gt;HipHop for PHP: Move Fast&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Facebook have open-sourced their internally developed PHP to C++ compiler. They serve 400 billion PHP pages a month (that’s more than 150,000 a second) so any performance improvement dramatically reduces their hardware costs, and HipHop drops the CPU usage on their web servers by an average of 50%. “We are serving over 90% of our Web traffic using HipHop, all only six months after deployment”.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/facebook"&gt;facebook&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/hiphop"&gt;hiphop&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/optimisation"&gt;optimisation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/performance"&gt;performance&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/traffic"&gt;traffic&lt;/a&gt;&lt;/p&gt;



</summary><category term="facebook"/><category term="hiphop"/><category term="optimisation"/><category term="performance"/><category term="php"/><category term="traffic"/></entry><entry><title>Drupal or Django? A Guide for Decision Makers</title><link href="https://simonwillison.net/2009/Nov/15/drupal/#atom-tag" rel="alternate"/><published>2009-11-15T22:14:48+00:00</published><updated>2009-11-15T22:14:48+00:00</updated><id>https://simonwillison.net/2009/Nov/15/drupal/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://birdhouse.org/blog/2009/11/11/drupal-or-django/"&gt;Drupal or Django? A Guide for Decision Makers&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A surprisingly interesting comparison—the author describes Django as “a framework with CMS-like tendencies” and Drupal as “a CMS with framework-like tendencies”, then explores the benefits of those two different approaches.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/drupal"&gt;drupal&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/frameworks"&gt;frameworks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="drupal"/><category term="frameworks"/><category term="php"/><category term="python"/></entry><entry><title>nginx_http_push_module</title><link href="https://simonwillison.net/2009/Oct/17/push/#atom-tag" rel="alternate"/><published>2009-10-17T16:48:31+00:00</published><updated>2009-10-17T16:48:31+00:00</updated><id>https://simonwillison.net/2009/Oct/17/push/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://github.com/slact/nginx_http_push_module"&gt;nginx_http_push_module&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
More clever design with webhooks—here’s an nginx module that provides a comet endpoint URL which will hang until a back end process POSTs to another URL on the same server. This makes it much easier to build asynchronous comet apps using regular synchronous web frameworks such as Django, PHP and Rails.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/comet"&gt;comet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webhooks"&gt;webhooks&lt;/a&gt;&lt;/p&gt;



</summary><category term="comet"/><category term="django"/><category term="nginx"/><category term="php"/><category term="rails"/><category term="webhooks"/></entry><entry><title>Scriptlets - Quick web scripts</title><link href="https://simonwillison.net/2009/Aug/13/scriptlets/#atom-tag" rel="alternate"/><published>2009-08-13T13:51:10+00:00</published><updated>2009-08-13T13:51:10+00:00</updated><id>https://simonwillison.net/2009/Aug/13/scriptlets/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.scriptlets.org/"&gt;Scriptlets - Quick web scripts&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
From the prolific Jeff Lindsay, a pastebin-style tool for short server-side scripts written in Python, JavaScript or PHP that executes them within a Google App Engine powered sandbox. The Java code that implements the service is available on GitHub.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://blog.webhooks.org/2009/04/18/easy-hook-scripts-with-scriptlets/"&gt;Easy hook scripts with Scriptlets&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google-app-engine"&gt;google-app-engine&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/java"&gt;java&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jeff-lindsay"&gt;jeff-lindsay&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scriptlets"&gt;scriptlets&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webhooks"&gt;webhooks&lt;/a&gt;&lt;/p&gt;



</summary><category term="github"/><category term="google-app-engine"/><category term="java"/><category term="javascript"/><category term="jeff-lindsay"/><category term="open-source"/><category term="php"/><category term="python"/><category term="scriptlets"/><category term="webhooks"/></entry><entry><title>djng - a Django powered microframework</title><link href="https://simonwillison.net/2009/May/19/djng/#atom-tag" rel="alternate"/><published>2009-05-19T00:13:31+00:00</published><updated>2009-05-19T00:13:31+00:00</updated><id>https://simonwillison.net/2009/May/19/djng/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;a href="http://github.com/simonw/djng"&gt;djng&lt;/a&gt; is nearly two weeks old now, so it's about time I wrote a bit about the project.&lt;/p&gt;

&lt;p&gt;I presented a keynote at EuroDjangoCon in Prague earlier this month entitled &lt;a href="https://www.slideshare.net/simon/django-heresies"&gt;Django Heresies&lt;/a&gt;. The talk followed the noble DjangoCon tradition (established last year with the help of Mark Ramm and Cal Henderson) of pointing a spotlight at Django's flaws. In my case, it was a chance to apply the benefit of hindsight to some of the design decisions I helped make back at the Lawrence Journal-World in 2004.&lt;/p&gt;

&lt;p&gt;I took a few cheap shots at things like the &lt;code class="django"&gt;{% endifequal %}&lt;/code&gt; tag and error silencing in the template system, but the three substantial topics in my talk were class-based generic views (I'm a fan), my hatred of settings.py and my interest in &lt;a href="http://en.wikipedia.org/wiki/Turtles_all_the_way_down"&gt;turtles all the way down&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;Why I hate settings.py&lt;/h4&gt;

&lt;p&gt;In the talk, I justified my dislike for settings.py by revisiting the problems behind PHP's magic quotes feature (finally going away for good in PHP 6). Magic quotes were one of the main reasons I switched to Python from PHP.&lt;/p&gt;

&lt;p&gt;My main problem with magic quotes was that they made it extremely difficult to write reusable PHP code. The feature was configured globally, which lead to a quandary. What if you have two libraries, one expecting magic quotes on and the other expecting it off? Your library could check &lt;code class="php"&gt;get_magic_quotes_gpc()&lt;/code&gt; and &lt;code class="php"&gt;stripslashes()&lt;/code&gt; from input if the setting was turned on, but this would break in the presence of the common idiom where &lt;code class="php"&gt;stripslashes()&lt;/code&gt; is applied to all incoming &lt;code class="php"&gt;$_GET&lt;/code&gt; and &lt;code class="php"&gt;$_POST&lt;/code&gt; data.&lt;/p&gt;

&lt;p&gt;Unfortunately, global settings configured using settings.py have a similar smell to them. Middleware and context processors are the best example here - a specific setting might be needed by just one installed application, but the effects are felt by everything in the system. While I haven't yet seen two "reusable" Django apps that require conflicting settings, per-application settings are an obvious use case that settings.py fails to cover.&lt;/p&gt;

&lt;p&gt;Global impact aside, my bigger problem with settings.py is that I almost always end up wanting to &lt;em&gt;reconfigure them at run-time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is possible in Django today, but comes at a price:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Only some settings can actually be changed at run-time - others (such as USE_I18N) are lazily evaluated once and irreversibly reconfigure parts of Django's plumbing. Figuring out which ones can be changed requires exploration of Django's source code.&lt;/li&gt;
    &lt;li&gt;If you change a setting, you need to reliably change it back at the end of a request or your application will behave strangely. Uncaught exceptions could cause problems here, unless you remember to wrap dynamic setting changes in a try/finally block.&lt;/li&gt;
    &lt;li&gt;Changing a setting isn't thread-safe (without doing some extra work).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Almost every setting in Django has legitimate use-cases for modification at run-time. Here are just a few examples:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Requests from mobile phones may need a different TEMPLATE_DIRS setting, to load the mobile-specific templates in preference to the site defaults.&lt;/li&gt;
    &lt;li&gt;Some sites offer premium accounts which in turn gain access to more reliable servers. Premium users might get to send e-mail via a separate pool of SMTP servers, for example.&lt;/li&gt;
    &lt;li&gt;Some sections of code may want to use a different cache backend, or talk to a different set of memcache servers - to reduce the chance of one rapidly changing component causing other component's cache entries to expire too early.&lt;/li&gt;
    &lt;li&gt;Errors in one area of a site might need to be sent to a different team of developers.&lt;/li&gt;
    &lt;li&gt;Admin users might want DEBUG=True, while regular site visitors get DEBUG=False.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, settings.py is behind the dreaded "Settings cannot be imported, because environment variable DJANGO_SETTINGS_MODULE is undefined" exception. Yuck.&lt;/p&gt;

&lt;h4 id="turtles-all-the-way-down"&gt;Turtles all the way down&lt;/h4&gt;

&lt;p&gt;The final section of the talk was about turtles. More precisely, it was about their role as an "infinite regression belief about cosmology and the nature of the universe". I want to apply that idea to Django.&lt;/p&gt;

&lt;p&gt;My favourite thing about Django is something I've started to call the "Django Contract": the idea that a Django view is a callable which takes a request object and returns a response object. I want to expand that concept to other parts of Django as well:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;URLconf: takes a request, dispatches based on &lt;code&gt;request.path&lt;/code&gt;, returns a response.&lt;/li&gt;
    &lt;li&gt;Application: takes a request, returns a response&lt;/li&gt;
    &lt;li&gt;Middleware: takes a request, returns a response (conditionally transforming either)&lt;/li&gt;
    &lt;li&gt;Django-powered site: hooked in to mod_wsgi/FastCGI/a Python web server, takes a request, returns a response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of a Django site consisting of a settings.py, urls.py and various applications and middlewares, a site would just be a callable that obeys the Django Contract and composes together dozens of other callables.&lt;/p&gt;

&lt;p&gt;At this point, Django starts to look a lot like WSGI. What if WSGI and the Django Contract were interchangeable? WSGI is a wrapper around HTTP, so what if that could be swapped in and out (through proxies) as well? Django, WSGI and HTTP, three breeds of turtle arranged on top of each other in various configurations. Turtles all the way down.&lt;/p&gt;

&lt;h4&gt;djng&lt;/h4&gt;

&lt;p&gt;djng is my experiment to see what Django would like without settings.py and with a whole lot more turtles. It's Yet Another Python Microframework.&lt;/p&gt;

&lt;p&gt;What's a microframework? The best examples are probably &lt;a href="http://webpy.org/"&gt;web.py&lt;/a&gt; (itself a result of Aaron Swartz's frustrations with Django) and &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;, my all time favourite example of Ruby DSL design. More recent examples in Python include &lt;a href="http://github.com/breily/juno"&gt;juno&lt;/a&gt;, &lt;a href="http://github.com/JaredKuolt/newf"&gt;newf&lt;/a&gt;, &lt;a href="http://github.com/bradleywright/mnml"&gt;mnml&lt;/a&gt; and &lt;a href="http://github.com/toastdriven/itty"&gt;itty&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Microframeworks let you build an entire web application in a single file, usually with only one import statement. They are becoming increasingly popular for building small, self-contained applications that perform only one task - Service Oriented Architecture reborn as a combination of the Unix development philosophy and RESTful API design. I first saw this idea expressed in code by &lt;a href="http://thraxil.org/users/anders/posts/2005/12/12/tasty/"&gt;Anders Pearson&lt;/a&gt; and &lt;a href="http://blog.ianbicking.org/little-apps-instead-of-little-frameworks.html"&gt;Ian Bicking&lt;/a&gt; back in 2005.&lt;/p&gt;

&lt;p&gt;Unlike most microframeworks, djng has a pretty big dependency: Django itself. The plan is to reuse everything I like about Django (the templates, the ORM, view functions, the form library etc) while replacing just the top level plumbing and removing the requirement for separate settings.py and urls.py files.&lt;/p&gt;

&lt;p&gt;This is what "Hello, world" looks like in in djng:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;import djng

def index(request):
    return djng.Response('Hello, world')

if __name__ == '__main__':
    djng.serve(index, '0.0.0.0', 8888)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code class="python"&gt;djng.Response&lt;/code&gt; is an alias for Django's &lt;code class="python"&gt;HttpResponse&lt;/code&gt;. &lt;code class="python"&gt;djng.serve&lt;/code&gt; is a utility function which converts up anything fulfilling the Django Contract in to a WSGI application, then exposes it over HTTP.&lt;/p&gt;

&lt;p&gt;Let's add URL routing to the example:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;app = djng.Router(
    (r'^hello$', lambda request: djng.Response('Hello, world')),
    (r'^goodbye$', lambda request: djng.Response('Goodbye, world')),
)

if __name__ == '__main__':
    djng.serve(app, '0.0.0.0', 8888)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The implementation of djng.Router is just &lt;a href="http://github.com/simonw/djng/blob/c892dddf064d5542c17119d02920ea4f5e9dd7f5/djng/router.py"&gt;a few lines of glue code&lt;/a&gt; adding a nicer API to Django's internal RegexURLResolver class.&lt;/p&gt;

&lt;h4&gt;Services, not settings&lt;/h4&gt;

&lt;p&gt;The trickiest problem I still need to solve is how to replace settings.py. A group of developers (including &lt;a href="http://www.holovaty.com/"&gt;Adrian&lt;/a&gt;, &lt;a href="http://lucumr.pocoo.org/"&gt;Armin&lt;/a&gt;, &lt;a href="http://lazypython.blogspot.com/"&gt;Alex&lt;/a&gt; and myself) had an excellent brainstorming session at EuroDjangoCon about this. We realised that most of the stuff in settings.py can be recast as configuring &lt;em&gt;services&lt;/em&gt; which Django makes available to the applications it is hosting. Services like the following:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Caching&lt;/li&gt;
    &lt;li&gt;Templating&lt;/li&gt;
    &lt;li&gt;Sending e-mail&lt;/li&gt;
    &lt;li&gt;Sessions&lt;/li&gt;
    &lt;li&gt;Database connection - &lt;code&gt;django.db.connection&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Higher level ORM&lt;/li&gt;
    &lt;li&gt;File storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of the above needs to be configured, and each also might need to be reconfigured at runtime. Django already points in this direction by providing hooks for adding custom backends for caching, template loading, file storage and session support. What's missing is an official way of swapping in different backends at runtime.&lt;/p&gt;

&lt;p&gt;I'm currently leaning towards the idea of a "stack" of service implementations, one for each of the service categories listed above. A new implementation could be pushed on to the stack at any time during the Django request/response cycle, and will be automatically popped back off again before the next request is processed (all in a thread-safe manner). Applications would also be able to instantiate and use a particular service implementation directly should they need to do so.&lt;/p&gt;

&lt;p&gt;A few days ago I heard about &lt;a href="http://pypi.python.org/pypi/Contextual"&gt;Contextual&lt;/a&gt;, which appears to be trying to solve a similar problem. Just a few minutes ago I stumbled across &lt;a href="http://pythonpaste.org/modules/registry.html"&gt;paste.registry's StackedObjectProxy&lt;/a&gt; which seems to be exactly what I've been busily reinventing.&lt;/p&gt;

&lt;p&gt;My current rough thoughts on an API for this can be found in &lt;a href="http://github.com/simonw/djng/blob/c892dddf064d5542c17119d02920ea4f5e9dd7f5/services_api_ideas.txt"&gt;services_api_ideas.txt&lt;/a&gt;. I'm eager to hear suggestions on how to tackle this problem.&lt;/p&gt;

&lt;p&gt;djng is very much an experiment at the moment - I wouldn't suggest building anything against it unless you're willing to maintain your own fork. That said, the code is all on GitHub partly because I want people to fork it and experiment with their own API concepts as much as possible.&lt;/p&gt;

&lt;p&gt;If you're interested in exploring these concepts with me, please join me on the brand new &lt;a href="http://groups.google.com/group/djng"&gt;djng mailing list&lt;/a&gt;.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/djng"&gt;djng&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webframeworks"&gt;webframeworks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/settingspy"&gt;settingspy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/my-talks"&gt;my-talks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djangoheresies"&gt;djangoheresies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/eurodjangocon"&gt;eurodjangocon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/services"&gt;services&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="djng"/><category term="django"/><category term="projects"/><category term="python"/><category term="webframeworks"/><category term="settingspy"/><category term="github"/><category term="my-talks"/><category term="djangoheresies"/><category term="eurodjangocon"/><category term="php"/><category term="services"/></entry><entry><title>Cross Browser Base64 Encoded Images Embedded in HTML</title><link href="https://simonwillison.net/2009/Apr/17/base64/#atom-tag" rel="alternate"/><published>2009-04-17T16:12:12+00:00</published><updated>2009-04-17T16:12:12+00:00</updated><id>https://simonwillison.net/2009/Apr/17/base64/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.hedgerwow.com/360/dhtml/base64-image/demo.php"&gt;Cross Browser Base64 Encoded Images Embedded in HTML&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Scarily clever. View the PHP source to see what’s going on—most browsers get image tags that use data URIs starting with data:image/png;base64, but IE gets served a Content-type:message/rfc822 header and a MIME formatted multipart/related document, as used by e-mail clients to embed inline image attachments.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://blog.hedgerwow.com/2009/04/16/updatecross-browser-base64-encoded-images-embedded-in-html/"&gt;HedgerWow&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/base64"&gt;base64&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/hedger-wang"&gt;hedger-wang&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/internet-explorer"&gt;internet-explorer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mime"&gt;mime&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;&lt;/p&gt;



</summary><category term="base64"/><category term="browsers"/><category term="hedger-wang"/><category term="internet-explorer"/><category term="mime"/><category term="php"/></entry><entry><title>Browsing my browsing</title><link href="https://simonwillison.net/2009/Apr/10/roo/#atom-tag" rel="alternate"/><published>2009-04-10T08:48:47+00:00</published><updated>2009-04-10T08:48:47+00:00</updated><id>https://simonwillison.net/2009/Apr/10/roo/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://rooreynolds.com/2009/04/05/browsing-my-browsing/"&gt;Browsing my browsing&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Roo Reynolds used the MeeTimer Firefox extension to gather statistics on his browsing habits, then extracted data directly from the SQLite database and generated his own graphs using PHP and the canvas element.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/canvas"&gt;canvas&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/meetimer"&gt;meetimer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rooreynolds"&gt;rooreynolds&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;&lt;/p&gt;



</summary><category term="canvas"/><category term="firefox"/><category term="javascript"/><category term="meetimer"/><category term="php"/><category term="rooreynolds"/><category term="sqlite"/></entry></feed>