Entity errors generating Atom feeds

Posted 09:08, 11/04/2013

After switching to Zend_Feed_Writer for my blog RSS and Atom feeds, I was frequently seeing errors like this in the logs:

PHP Warning:  DOMDocument::loadXML(): Entity 'rsquo' not defined in Entity, line: 14 in /path/to/library/Zend/Feed/Writer/Renderer/Entry/Atom.php on line 393

(the error appeared for several different entities). This happens because entities such as ’ or   are not valid in XML feeds, you need to use their numeric equivalents. Zend_Feed_Writer uses the PHP HTML Tidy extension to perform this conversion if it is available, so you should only see this error if you don't have the extension installed.

You can fix the error simply by installing the extension. On Ubuntu this is as simple as sudo apt-get install php5-tidy. This is covered in ZF ticket #9566, and the discussion indicates it's an area that was flagged for improvement in ZF2.

Comments (0) Tags: zend framework

Dojo: preventing form submit on Enter

Posted 14:28, 14/02/2011

If you've got some AJAX functionality inside a standard form (e.g. an address lookup feature where people type in their postcode and click a button) then there's a temptation for people to hit 'Enter' to perform the search. By default this will submit the whole form, which isn't what you want.

Catching the 'Enter' key press is fairly easy to do with some inline Javascript, but it can easily be done unobtrusively with Dojo as well:

dojo.ready(
    function() {
        dojo.connect(dojo.byId('postcode'), 'onkeydown', function(event){
            if (event.keyCode == dojo.keys.ENTER) {
                // CALL AJAX CODE HERE

                dojo.stopEvent(event);
            }
        });
    }
);

I found that 'onkeyup' as the event doesn't work here - the code runs but doesn't prevent the submission, so stick to onkeydown.

Update: Here's the AMD (Dojo 1.7+) equivalent of the same thing:

require(['dojo/ready', 'dojo/dom', 'dojo/on', 'dojo/keys'], function(ready, dom, on, keys){
    ready(function(){
        on(dom.byId('postcode'), 'keydown', function(e){
           if (e.keyCode == keys.ENTER) {
               // AJAX stuff here
 
               event.stop(e);
           }
        });
    });
});
Comments (5)

Vanity URLs in Zend Framework

Posted 20:22, 09/09/2010

A question I've seen pop up in a few places is that of vanity URLs, and how to achieve them using frameworks. What I mean by a vanity URL is one where you have a URL structure such as http://example.com/<something>, where something is some kind of user-generated string stored in the database, perhaps to give users a more memorable profile URL. Twitter is the obvious example of this, e.g. my twitter page is http://twitter.com/tfountain.

How would you setup routing for this? Easy, you might think, you just create a standard route containing a 'username' variable:

$route = new Zend_Controller_Router_Route(
    ':username',
    array(
        'controller' => 'users',
        'action' => 'profile'
    )
);

But what if you want to add http://twitter.com/about and route it to a different place? Your router has no way of knowing whether 'about' is a username or not, so you end up having to add a load of static routes for your normal pages, leaving your username route as the default one checked last.

What if you then want a top level vanity URL for another feature? Say you introduced a groups feature, where groups can be setup with URLs like http://twitter.com/<group name>. You now have two identical URL structures which need to route to different places.

This is something that can be quite difficult to do with a lot of frameworks. Most follow the Rails-style approach where you define a load of URL patterns in a routes file along with details of where each should map to. This is easy to use and will cover 90% of routing cases. But for the other 10% you're screwed - you're left with a handful of messy options which either involve bypassing the routing process or compromising on your desired URL structure.

In my opinion the biggest strength of Zend Framework is its flexibility, and the routing is a great example of this. You can just load all your routes in from a file in ZF, but you can also create route objects and assign them to the router directly, or define your own route types and assign these, or extend the router class with some customisations, or define your own router; or some combination of these options. There's a slightly bigger learning curve, but you're able to setup your routing to match your application's needs, rather than having to change your application to work within the limitations of the framework.

In ZF a custom route class is the way to go to for vanity URLs. Remember how I said the router has no way of knowing whether 'about' is a username or not? Well if you create a 'username' route class that checks the database to see whether a particular string is a user or not, then suddenly your router can handle these routes like any other. You are also able to add additional route classes for any other vanity URL structures you need.

A custom route class at its most basic just needs to implement the Zend_Controller_Router_Route_Interface. This defines three methods:

interface Zend_Controller_Router_Route_Interface {
    public function match($path);
    public function assemble($data = array(), $reset = false, $encode = false);
    public static function getInstance(Zend_Config $config);
}

the match function is where the action is - this takes the path from the URL as a parameter, and should return an array of params to be passed to the controller if the route matched, false if it did not. The params array should also contain the module, controller and action name that tell the router which part of your application to route the request to. 

I'd recommend extending the Zend_Controller_Router_Route_Abstract class, as this is used by all of the standard ZF route classes and will allow you to follow some of their conventions.

Here's an basic example for a username route:

<?php

class Application_Route_User extends Zend_Controller_Router_Route
{
    public static function getInstance(Zend_Config $config)
    {
        $defs = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
        return new self($config->route, $defs);    
    }

    public function __construct($route, $defaults = array())
    {
        $this->_route = trim($route, $this->_urlDelimiter);
        $this->_defaults = (array)$defaults;
    }

    public function match($path, $partial = false)
    {
        if ($path instanceof Zend_Controller_Request_Http) {
            $path = $path->getPathInfo();
        }

        $path = trim($path, $this->_urlDelimiter);
        $pathBits = explode($this->_urlDelimiter, $path);

        if (count($pathBits) != 1) {
            return false;
        }

        // check database for this user
        $result = Zend_Registry::get('db')->fetchRow('SELECT userID, username FROM users WHERE username = ?', $pathBits[0]);
        if ($result) {
            // user found
            $values = $this->_defaults + $result;

            return $values;
        }

        return false;
    }

    public function assemble($data = array(), $reset = false, $encode = false)
    {
        return $data['username'];
    }
}

(This example assumes that there's a Zend_Db instance in the registry under the key 'db'.)

All it's doing is extracting the string from the path and looking this up in the database. If a match is found, it adds the user ID and username to an array that already contains the module, controller and action names and passes this back to the router.

To use this route you assign it to the router during your bootstrap:

    protected function _initRoutes()
    {
        $router = Zend_Controller_Front::getInstance()->getRouter();

        // general page route
        $router->addRoute('about', new Zend_Controller_Router_Route(
            ':page',
            array(
                'module' => 'default',
                'controller' => 'pages',
                'action' => 'show'
            )
        ));

        // username route
        $router->addRoute('user', new Application_Route_User(
            'user',
            array(
                'module' => 'default',
                'controller' => 'users',
                'action' => 'show'
            )
        ));
    }

You'll see I've included a page route here as well, and note that I can now use a more conventional :page variable route instead of a static route, as there's no longer any ambiguity. This way you don't have to modify your routes whenever you add a new page.

I've only scratched the surface of what you can do with ZF's routing. I've used a similar approach (custom route class) for hierarchical routes, where you have /category/sub-category/sub-sub-category type structure, but that is a topic for another blog post.

Comments (10) Tags: zend framework

Speeding up phpMyAdmin

Posted 23:35, 29/07/2010

Here's a quick tip for speeding up phpMyAdmin when using it on a remote server. A big drain on rendering speed for the app seems to be the sheer number of theme related requests (images and stylesheets) the browser makes on every page load. An easy way around this is to use the Apache module mod_expires to send an expires header with these files, which tells the browser not to bother requesting them again for a set period. This cuts down the total requests by about 90%.

Firstly, make sure mod_expires is enabled:

sudo a2enmod expires
sudo apache2ctl restart

then open the phpMyAdmin Apache configuration file (by default in located at /etc/apache2/conf.d/phpmyadmin.conf) in your text editor of choice. You'll see an IfModule block which sets up some php values:

<Directory /usr/share/phpmyadmin>
    Options FollowSymLinks
    DirectoryIndex index.php

    <IfModule mod_php5.c>
        AddType application/x-httpd-php .php

        php_flag magic_quotes_gpc Off
        php_flag track_vars On
        php_flag register_globals Off
        php_value include_path .
    </IfModule>
</Directory>

insert the following mod_expires block below the existing </IfModule>:

    <IfModule mod_expires.c>
        ExpiresActive On
        ExpiresByType image/gif "access plus 7 days"
        ExpiresByType image/jpg "access plus 7 days"
        ExpiresByType image/png "access plus 7 days"
        ExpiresByType text/css "access plus 7 days"
        ExpiresByType application/javascript "access plus 7 days"
    </IfModule>

which should leave you with this:

<Directory /usr/share/phpmyadmin>
    Options FollowSymLinks
    DirectoryIndex index.php

    <IfModule mod_php5.c>
        AddType application/x-httpd-php .php

        php_flag magic_quotes_gpc Off
        php_flag track_vars On
        php_flag register_globals Off
        php_value include_path .
    </IfModule>

    <IfModule mod_expires.c>
        ExpiresActive On
        ExpiresByType image/gif "access plus 7 days"
        ExpiresByType image/jpg "access plus 7 days"
        ExpiresByType image/png "access plus 7 days"
        ExpiresByType text/css "access plus 7 days"
        ExpiresByType application/javascript "access plus 7 days"
    </IfModule>
</Directory>

Restart Apache (sudo apache2ctl restart) so the changes take effect.

What this does is tell Apache to send an expires header of 7 days into the future for all image, CSS and javascript files within phpMyAdmin. The initial request after the restart will be as before, but after that the browser knows that the files it got back are good for 7 days, so on subsequent requests it will only request the HTML page. You should see a noticeable speed improvement.

Normally when using mod_expires you would use a much longer expire time than 7 days, but this way if a future phpMyAdmin update does change these theme files you're less likely to get caught out.

Comments (1)

Digital Economy Bill

Posted 21:32, 16/03/2010

I wouldn't normally do a blog post on a political topic, but the Digital Economy Bill is big news at the moment, and I, like many, am worried about the direction in which it is going, particularly the parts aimed at addressing copyright infringement.

What problem is it trying to solve?

The copyright infringement parts of the bill seem to be written largely to answer the complaints of the music industry, whose arguments just don't add up.

The BPI claim that illegal file sharing cost the music industry £200m in 2009, however this figure seems to be based on the mistaken idea that a download is a lost sale.[1]

The BPI's own figures on UK music sales from last year paint a different picture. The full article is worth a read but here's the choice quote:

"Across the entire year, singles sales increased by 32.7% to a record 152 million, with 98% of those being digital downloads."

So last year, the height of a recession, was a record year for music single sales. Another analysis of the same figures, which merges single and album sales shows a steady increase in overall sales since 2003, which was the year that digital music seemed to take off.

So where is this £200m hole created by illegal file sharing?

It seems obvious to me that if 10 years ago the majority of single sales were CD singles costing ~£3.99, but now the majority of single sales are digital downloads costing ~99p; that this is going to result in a revenue loss. But this isn't something I've ever seen pointed out in news stories on this topic. This also has nothing to do with file sharing.

At the same time the industry seems to be ignoring some potentially positive aspects of illegal file sharing. 

In the BBC's Panorama on this topic (15th March 2010), UK singer Kate Nash attributed file sharing to launching her career. Her view is that after starting on MySpace, people downloaded her songs via. file sharing networks and came to her gigs as a result. She has since had three successful albums and three top 40 hits in the UK.

The overall increase in music 'consumption' is also likely to be why 2009 was the first year in which income from live music (festivals, concerts etc.) overtook income from recorded music.

As musician Billy Bragg put it in the Panorama programme mentioned above - "the Music Industry is thriving, it's the Record Industry that is dying on its feet".

Protecting our creative industries

Apparently the measures put forward to combat file sharing are required to protect our creative industries. However none of the aspects of the bill provide any extra money to the content creators, or pave the way for innovation that might do this. In fact the opposite is true - the music industry will likely bear the majority of the cost of identifying illegal downloaders, and the administrative costs of dealing with complaints will be born by the ISPs. The latter will inevitably be passed on to the consumer. How does this benefit anyone?

Even some Government estimates apparently concede that these measures might cost £500m to implement. And this is to try and address the music industry's dubious claim of £200m in lost revenue?

An exercise in futility

If these measures had any hope of working, perhaps opinion might be different. But the measures in the bill are so trivially easy to circumvent that it seems unlikely they will result in any reduction file sharing.

As long as there is a demand for the content, technology will exist to meet that demand. And the technology will always be 10 steps ahead of any methods to try and combat it.

In the Panorama programme I mentioned above, one of the musicians interviewed likened the plans to cut off persistent downloaders to "going round and confiscating people's record players, and then complaining that no-one's buying records".

Democracy at work

I've watched a few of the House of Lords' sessions debating this bill via. the iPlayer and the BBC's Democracy Live website. Not being one to generally show much interest in politics, it was interesting for me to see how the process works and the sort of things being debated. At the same time it was scary to see how few people in the debates seemed to have a good understanding of the issues being discussed, and how these stalwart few are the sole reason the bill doesn't just get approved without question or modification.

Even more worrying is the way in which this bill is being pushed through parliament, a concern that was raised in the House of Lords debates.

Here's an example. The item in the bill that caused the most controversy was "clause 17", which would have given the Secretary of State sweeping powers to amend copyright law at will, without parliamentry oversight. Apparently this was to allow laws to quickly be introduced to combat future forms of piracy. However no justification was provided to suggest why existing laws might not be sufficient (after all, if it's illegal it's illegal, future technology is not going to change that).  

The Lib Dem's put forward a "clause 18" to replace clause 17. This was the 'web blocking' clause which caused an equal amount of controversy. It would have allowed court injunctions to be issued forcing ISPs to block access to websites that enable access to copyright material. Ignoring the futility of such an action, filtering the Internet is an extremely dangerous road for the Government to go down. This is exactly the sort of thing that recently earnt Australia a place on the international "Internet Enemies" list due to their own filtering plans; a list currently occupied by countries such as Iran and North Korea.

This clause was also eventually withdrawn. Hooray for common sense.

However what seems to happen now is that the Government will replace these two clauses with 'something else'. This 'something else' will not be discussed in the House of Lords, cannot be amended, and will be first seen in something called the "wash up", which is where Parliament tries to rush through lots of last minute bills before it is disbanded in preparation for the upcoming General Election. 

Even members of the House of Lords seem to have concerns over this. Here's a quote from the Earl of Erroll (one of the few people talking sense), from the final debate on the DEB where clause 18 was being discussed:

"I'm delighted that the noble minister realises the shortcomings of this clause and also that this clause has replaced the previous clause 17 which I didn't like either, and therefore something better will appear. On the other hand, I think that the method by which it's appearing is by complete and absolute abuse of parliamentry process, and I'm not quite sure why we bothered to sit and debate any laws at all if basically the front benchers in another place can get together and put whatever they like into legislation."

Digital Economy Bill, House of Lords, 15/3/10, approx 1:24 in.


My fundamental problem with the Digital Economy Bill is that I believe it will do nothing to stimulate the digital economy, and will in fact have the opposite effect, giving record companies reason to continue their doomed attempts to try and regain control over the distribution of music.

The Internet is the greatest medium for content distribution ever invented. Remember those music sales figures I quoted from 2009? 98% of single sales last year were digital downloads - this medium didn't even exist 10 years ago.

Spotify has over 2 million users in the UK, however last month Warner Music announced it was going to withdraw its music from streaming services, saying such services were "clearly not positive for the industry" (which I read as meaning they will never get as much revenue from it as they would equivalent music sales).

Surely the Digital Economy Bill should be implementing measures that encourage innovation and new business models, paving the way for the iTunes and Spotifys of the future. Until these services exist, any attempts to combat illegal file sharing will fail, and generations of people will grow up knowing file sharing software as the only way to get music.

1. Whilst there will certainly be some people who will illegally download songs that they would have previously gone out and bought, there will also be a (in my opinion much larger) proportion of people who will download large amounts of music simply because it's free, and readily available. How many people have built up collections of many thousands of mp3s through file sharing? Would these people have instead spend tens of thousands of pounds on buying music? I think not.

Comments (1) Tags: file sharing, rants

External articles that may be of interest:

Rasmus - PHP Performance

Full video of a talk Rasmus gave at Digg HQ on PHP performance. After a brief look at PHP 5.3's new features he sets about improving the performance of an out-of-the-box Wordpress installation.

Adobe releases 64-bit flash player for Linux

Adobe have released an alpha of their 64-bit flash player for Linux (I believe this is the first 64-bit player they've done on any platform, it's not available for Windows or Mac yet). And it works great. It may be an alpha but so far I've found it considerably more stable than the released 32-bit wrapper version. It also uses considerably less CPU power. Easy installation instructions via. this blog entry.

MS Office in the browser

The most interesting thing about this announcement is that MS say the system will work in Firefox and Safari as well as IE. The Microsoft of 10 years ago would have used proprietary IE only code and used it as a way to leverage IE market share.

The Future of Advertising, Branding, Media and Communications

Video of a very interesting talk by Gerd Leonhard on the future of the media industry, and content on the Internet.

Future of web browsing from Mozilla Labs

Some interesting ideas about how web browsing might look in the future (watch the first video).