Zend Framework gets hostname routing
Saturday, July 19, 2008
Introducing note: The examples in this article don't reflect the usage of the final hostname routing in 1.6 anymore.There were many needs for it, in different mailing lists, forums and blogs, but it was never officially requested. There were also many approaches to accomplish it, but they were all kinda hacky. As I neeed it in an earlier project yet, were I had do do the hacky way as well, and now a new project came up, were I even need a more complex implementation, I decided to put a feature-rich hostname routing implementation into the ZF core itself. I did most of the implementation stuff myself, created unit tests and documenation, and SpotSec did some finetuning on it afterwards.
Well, it will be included in ZF 1.6 RC1, which will come very soon. Between the final release will be an RC2, which gives you enough time to try it out and send in issues, if there are any. I really guess, this is a feature which was hardly required by many developers. For those of you who are interested yet, you can check it out from trunk. Here is the documentation part:
Hostname routing
You can also use the hostname for route matching. For simple matching there is a static hostname option:If you want to match parameters in the hostname, there is a regex option. In the following example, the subdomain is used as username parameter for the action controller. When assembling the route, you simply give the username as parameter, as you would with the other path parameters:<?php$route = new Zend_Controller_Router_Route(array('host' => 'blog.mysite.com','path' => 'archive'),array('module' => 'blog','controller' => 'archive','action' => 'index'));$router->addRoute('archive', $route);<?php$route = new Zend_Controller_Router_Route(array('host' => array('regex' => '([a-z]+).mysite.com','reverse' => '%s.mysite.com''params' => array(1 => 'username')),'path' => ''),array('module' => 'users','controller' => 'profile','action' => 'index'));$router->addRoute('profile', $route);
Comments to this article
Leave a comment
Please note that your email address will not be shown, it is only used to fetch your avatar image from gravatar.com and for notifications.


Awesome :D Nice work boys!
Actually, the real reason why I comment this is because I want to test out Figlet :x
This seems pretty cool, but can you give some examples of situations where this functionality would be useful?
Well, two good examples are given in the documentation itself, no?
Whoo!
Wey! It was great to have this implemented so quickly :)
I'm thinking of trying to use this to support multiple application instances (on sub domains) without duplicating the directory structure for each. I'll let you know how it works out :)
I'm seeing your two examples of 'how' in this article, but what I'm really looking for is 'why'. You're describing functionality that exists to serve a purpose that I have not yet encountered, and I'm interested in learning more. Do you have a link to the full documentation? I'd love to check it out!
This is in fact the full documentation. I was too lazy (as I always am), to write some more ;)
I think this implementation has a flaw, the routes should be loaded based on the current hostname and, optionally, module.
If hostname A has 30 routes, why do you need to load another 60 for host B and C? It doesn't make sense.
Nice captcha BTW ;)
In order to make the design more scalable, the router should get an instance of the request object and load a set of routes based on the hostname. Here's a better solution:
application
|-- bootstrap.php
|-- config
| `-- routes
| |-- blog.mysite.com
| |-- forum.mysite.com
| `--eshop.mysite.com
The router maps the hostname to a directory and loads the routes from there. Otherwise, every time I visit blog.mysite.com the router has to iterate through all the routes and perform regular expression until it finds a positive match.
The problem with that design and the current router is that it isn't exactly easy to lazy load routes for the Url and Redirector Helpers that rely on route's being loaded to generate urls.
eg. $this->url(array(), 'someRouteFromAnotherSubDomain') no workie.
It wouldn't be too hard to implement though as some modules are segregated enough from another to not have to refer to routes from the other. This is the same style of routing that symfony does. It has it's pro's and con's.
Yes, it seems to me that they copied the idea from Symfony instead of looking for a new and better solution.
There are many ways you can implement a faster and more scalable routing process without the need of loading unnecessary objects or running an excessive amount of regular expressions. You can even have a map as a config file telling the router where to find the routes, this solves all the problems.
I've looked at the trunk/ and I honestly think that the code added to the router is more of a hack than a solution, and the router is starting to smell. The router at the moment is not very effective when used in high-traffic ecommerce and/or social networking sites.
This routing process is sometimes referred as *highway to hell*.
At the current stage, this was was the only solution to implement hostname support for me, as no BC breaks are allowed until 2.0. With 2.0 then, I plan to make an entire rewrite of the router system, which will allow to only load necessary routes. Until then, you have to live with this solution.
And no, it wasn't copied from any other framework.
Ah and by the way, currently this makes the performance better than worse. Yet it really has to iterate over all routes and evaluates every path pattern (may it be static, normal route or regex).
When you now use the static hostname in 1.6, it will simply skip all hostnames, which don't match the hostname in the route, and the path won't get evaluated. Remember here, that the static routes are simple string comparisions, and only the extended hostname matching uses regex.
Hi Ben,
You don't have to break backward compatibility to add this. I know that it skips the hostnames that don't match, but if you are using INI files, then the config object has to process all the routes and the router load all the objects into memory.
What I'm suggesting is making this optional, for example:
$router = $frontController->getRouter();
$router->setRoutesDirectory('../application/config/routes');
$router->setRoutesFilename('routes.php');
$router->enableHostnameMapping();
It will only load a set of routes from: /application/config/routes/blog.mysite.com/routes.php
What I meant is that Zend's router follows the same logic as Symfony and CodeIgniter. Django, on the other side, offers a different solution, it allows you to include other routes based on a given pattern.
Great work on the hostname router.
I've written a blog post about how to use the router to use sub-domains as account keys. I wrote it after the changes to the router had been made so usage is quite different to that in this post.
Check it out: http://www.noginn.com/2008/09/03/using-subdomains-as-account-keys/
Thanks for the great work DASPRiD
Very good article Tom. I think that this will be a good start-up for users of the new hostname route.