Refreshing with these micro frameworks as a counter movement to the monoliths. In my opinion PHP is so fragmented library wise, that trying to streamline everything is a failure - Much better to have a collection of independent modules that are stitched loosely together. It's of course very much a matter of taste.
On the technical side, I have come to prefer using straight regular expressions for routes, rather than a dsl or made-up routing syntax a la rails. It's a relatively unknown feature of preg, but you can have named captures, that pretty much gives you all that you need. For example, here's a couple of routes from a recent application of mine:
My front controller is then just a loop over the routes, that will resolve first match.
For example, a GET request to `/devices/1234` will resolve to the file `handlers/devices/show.php`, where `device_id` is available as a parameter.
Yes, the syntax is a bit awkward, but it's computationally efficient without a cache/compilation step and it's a standard language, rather than a proprietary one.
I wouldn't call that computationally efficient. It is possible to make routing an O(log n) operation; your routing solution, like all routers that use regexes directly, is O(n).
Technically, yes. What I was referring to was more mundane though. The difference between writing a parser/interpreter partially in PHP or minimizing the php code to a loop and then push all the hard stuff down to preg is significant. Optimizing it further is probably futile, compared to what else goes on in a typical http req-res cycle.
It's a red herring anyway, so I sort of regret bringing it up. Much more important is the other point - that this is a standard tool, rather than a tailored one.
If n is the number of routes, and they are sorted (e.g. implicitly through a prefix tree) and evenly distributed accordingly then then the claim might be plausible by doing log n prefix comparisons of increasingly smaller suffixes of the route to be resolved. I find this line of reasoning unusual and misleading though.
The lower bound is linear in the length of the route if all routes participate in your regular expression and if you have it pre-compiled to, for example, a DFA or an LL parser ahead of time.
Great work. Hope to see this evolve into a great little toolset.
I have gone through your code a bit and think that you should consider error handling first. Some methods return strings on error but arrays on success but this is not validated before using the value as an array (this results in the error reported before). I guess when no caching system is available the Dummy will break your code as well as it only returns false.
Also, I think using "False" instead of "false" results in two internal checks when PHP tries to determine if something is false or true, instead of one. Not sure if that is still the case with the latest PHP version, though. I reckon using "false" would be better.
Thanks, and I agree. I usually use exceptions but for some things (like cache misses) it seems unwieldy. For the example of the cache calls, I check when I've done a cache call like:
$data = $cache->get("key");
if(!$data) {
// do the stuff if cache misses
}
Though yes, I do need to go through and make sure it's extremely robust, as I did hack it up in an evening :)
I was thinking about the part where you check the cache with if (!$found)
On that note, I adapted a rule of an open source project (can't quite remember which) that said to never use !$var to validate your data. One should always use isset($found['args']) or empty($found['args']) or for example is_array($found) && count($found) > 0 to check.
I've been burnt by this in the past. Nowadays I use ($found === false) to check for a cache miss (though this means you can't store boolean values verbatim).
I remember seeing an article or talk or something by one of the main Symfony guys on the fact that PHP was wanting, as a community, to move away from monolithic frameworks in favour of smaller, front controller style implementations. I was actually tring to find it the other day - does anyone have a link?
It's interesting to see the growing trend in this direction for PHP.
I wrote one a little while back[1] that does even less than this :) The only goal being to provide a simple way to execute classes from the web or command line with a default autoloading implementation and a way to inject new ones[2]
Personally a lot of my current direction in PHP comes from watching what's happening the node.js community - I like the way that there are a lot of small-ish packages/modules that are pretty independent.
I've written a couple of packages for RocketSled already which you can see on my github page if you're interested.
I don't know about the article. But the truth is that Symfony2 is built from a collection of independent components. And each component could be used independently in your app without a need to use symfony framework.
Here is for example a link for a routing component: https://github.com/symfony/Routing
The Zend Framework is the same. You can use each component completely separately. I know a few people who have used the Zend Controller classes and Zend Router in their own frameworks.
It's an encouraging trend. One of the things I like from the Java ecosystem is that there are a couple of really nice general-purpose frameworks that can be mixed and matched with other, more specific frameworks. Like Spring + Hibernate + JSF. I never liked the idea of a big, monolithic framework pretty much forcing its way of working on me. I'm very happy to see the PHP community moving in this direction!
I like the idea of that for command line, its a wicked project. Seems a bit painful for web use though - I moved away from mapping routes to controllers automatically in favour of specifying them manually, I feel that it gives great control and leads to less magic. Not knocking your project, though! Good stuff
Actually it doesn't even do that! It does so little - that r= argument just has to be a class that is autoloadable and implements the Runnable interface.
In fact, if you want routes you'd have to build it as a package and in FACT you could just create a class like this:
Looks nice, keep it simple though. There are already a lot of feature full frameworks about that to be honest you can't compete with. I've never used a really lightweight framework like this but I would definitely consider it if I had a project that was the right size.
I wonder what would be hard about FatFree? To me it does not get much simpler than that. And it has a router, templating engine, auto loader (even with name space support, if you wish) and several other plugins.
It helps when you need it and lets you do things your way at the same time.
The only caveat is there's basically only one guy developing it.
The most important one is that everything is done within an $app context. This means that theoretically you could build two apps, and I later will be creating functionality to "mount" apps on paths, like in Flask Blueprints.
Second one is that I am going to be extending it (but keeping it lean) with other microframework-y goodness, patterns, ways to use PHP's native stuff in elegant ways, etc.
Essentially, klein is a router, but Ham will be a microframework, providing patterns for application development with it, extensions to work with other libraries, etc, ala Flask.
I'm definitely looking at Klein and seeing what I can learn from it, what can be done better, etc.
Will definitely check this out, although I just started a project using Slim.
Slightly off-topic but besides Doctrine, RedBean and PHP ActiveRecord, is there any good and well-tested 'ORM' or some DataMapper/ActiveRecord implementation for PHP?
(I actually like PHP ActiveRecord but had some odd problems with it a while ago and well, there's 60+ issues on GitHub and no proper release since 2010.)
Am I reading this correctly that currently the route results will stay cached for the caches TTL? So if you change a route to have a different callback in your main program it will not update?
Really cool framework and congratulations on hacking this up in a night!
Ah there were a couple of bugs. It now doesn't require a caching module to be installed, but is slower with out it. XCache is preferred as it allows closure caching.
Yeah, I tried to drop in a Redis caching class and immediately ran into that problem. I noticed someone had published a method for serializing PHP closures using magic methods and reflection, but that's way too complex a workaround.
Looks good. I'd use named regex matches for the routes (like in Django) to make routes more flexible, ie. !/path/(?P<id>[0-9]{1,5}_(?P<kw>[a-z0-9_]{5,20})! etc.
For Photon, a micro framework for Mongrel2, we use regex. Even with 100's or 1000's of routes, you cannot even see the cost of preg_match when benchmarking because the regex are organized in a tree following the URI structure like Django. It means that most of the time, a view is found within 2 to 3 preg_match calls.
This - regex is slow to match anchored strings on a 120 character long URI - is a myth.
Mostly because mod_rewrite rules are fairly complicated, and the ability to put them in the server's conf file means any changes require a server restart. You could put them in .htaccess, but now your having to parse out the route every time. Using a front controller (like index.php) makes everything so much easier. Finally, you have to consider hosting support. This hasn't even touched on numerous other benefits.
Yes, their are tradeoffs, but they don't overcome the benefits, not the least of which is simplicity.
On the technical side, I have come to prefer using straight regular expressions for routes, rather than a dsl or made-up routing syntax a la rails. It's a relatively unknown feature of preg, but you can have named captures, that pretty much gives you all that you need. For example, here's a couple of routes from a recent application of mine:
My front controller is then just a loop over the routes, that will resolve first match.For example, a GET request to `/devices/1234` will resolve to the file `handlers/devices/show.php`, where `device_id` is available as a parameter.
Yes, the syntax is a bit awkward, but it's computationally efficient without a cache/compilation step and it's a standard language, rather than a proprietary one.