How is this not just a slower .htaccess implementation with OOP boilerplate? The instructions even mention editing .htaccess, so it's not like this is necessarily intended for people who don't have control of it.
Not really, most frameworks use a router to ensure the application has a single point of entry. Most PHP frameworks adopt a router too. A very common use case that requires a router that .htaccess wouldn't provide is if you want to have some middleware run before or after a request (for setting cache values for some resources and not others). There are other benefits too when it comes to testing as you can test (route) requests to views without having to make a call to the server.
There's always the argument of abstraction vs simplicity. I appreciate both and I didn't used to like using routers. But I do use it these days for most of my work.
Apps often don't have a physical URL that you could map to in .htaccess like /foo/bar.php. There's only index.php and the appropriate controller/method is executed based on the URL. Of course you could just map .htaccess to something like /index.php?method=foobar instead. But then you're still dealing with a router in your index.php, perhaps a simple switch statement or something. It wouldn't bother me to see this kind of setup but I think having a router is just a bit cleaner.
You can also abstract retrieving values from the URL like /account/1234 (obtaining "1234") so that the router does all the parsing and the controller isn't aware of the URL implementation. Perhaps useful for altering URLs later without affecting the controller code. Keeping things separate also can make unit testing somewhat easier as you can substitute the router with a mock object and get your controller to respond to all the variations of user input.
There are benefits to keeping your routing in your app. Using different route sets based on environment variables, specifically migrating from one version of an api to another is not possible or easy with a purely htaccess router.
But I agree that in some cases the fastest you can do is an htaccess route.
> There are benefits to keeping your routing in your app. Using different route sets based on environment variables, specifically migrating from one version of an api to another is not possible or easy with a purely htaccess router.
I'm not excited to correct you, because the way it it doesn't quite hold true involves dragons, but I thought I would in case people doubt the power of apache configuration (nginx is much the same way).
It is possible, with included modules like the one below. There is also the ability to use custom modules, which are programmatic in nature and have access to Apache at a low level. Since it wasn't specified whether a shared host is being used, I think it's within the scope of the discussion.
Well done! This has a really clean interface, I wish I'd come across it when looking for a simple router a few weeks ago. A lot of the other ones lean heavily towards a style associated with their related framework, or are too esoteric/basic.
There are a few things I would have to add before I would use it however, and I hope they make it in some day:
1) You should add a whitelist for the HTTP request method names, i.e only allow calls to get(), post(), not some_suspicious_call_from_bad_user(). People will often put non HTTP methods in their views (view as in django sense) and you don't want them to be able to be called by a malicious user.
2) You should make it easy for the developer to configure their own xhr headers.
3) Nitpick: You should probably use 405 (Method Not Allowed) instead of 404 for when a method handler isn't found.
Maybe I'll use it in this project, in which case you may have a few pull requests headed your way!
Related: A similar project (a simple php router) called Klein exists (https://github.com/chriso/klein.php) which is really great. Does anyone have experience with both and can explain which is better?
I love Klein, but this looks WAY easier to work with and much more specific. Klein includes a bunch of stuff for request and response handling, output buffers and views and stuff. Toro appears to completely leave all that to the implementer, which is very nice if you've already got code to handle those things.
Thanks for posting this. I hadn't seen Klein before and the simplicity and minimalism of the API really appeals to me. I'm using Slim so far and it has some serious drawbacks (no namespaced routes, no response()->write(json), overly complicated view system)
It's an interesting approach. I use SlimPHP, which approaches the RESTful API problem as a DSL, much like Sinatra. Slim has a few nice features, and my new preferred PHP stack is Slim Framework + Zend Framework as a library.
This illustrates exactly why PHP projects often become such an unmaintainable mess and security hole. This looks like an example collection of PHP worst practices.
I mean, what could possibly go wrong with untestable code and unfiltered input...?
That's not the problem this solves. This is 91 lines of code that solve one problem, routing, and that problem only. You filter input right afterwards.
And how is this untestable, exactly? It's really easy to fake http requests in php-cli from a unit test, for instance. And is there any other way you'd want to test a router than by faking php requests?
I somewhat wonder whether the fact that this happens to be PHP biased your judgment.
If anything I find that a router makes your code easier to test. If you're 100% consistent about using the router to obtain user input, then your code is easy to unit test with a mock router. Routers themselves can be easy to test as well.
It's a clean separation of concerns - the router is the only thing that knows about the URL implementation. The rest of the code relies on the router to do that. That's a good practice to me.
What would you would consider "best practice" in place of a router?
Is this a parody of how not to do things? I was prepared to take it on face value of being rather misguided, but thinking about it... It's a glorified mess of goto statements and objects being misused.
I confess that I stopped digging further after I saw files being included in methods, absolutely one of the biggest sins in PHP:
Not that I've looked at it in much depth, but those just look like simplistic examples to show how the routing works. If it's just a router, then how you render views is beyond the scope of the library, and cutting corners in examples for the sake of expediency doesn't seem so bad.
Not that I've looked at it in much depth, but those just look like simplistic examples to show how the routing works. If it's just a router, then how you render views is beyond the scope of the library, and cutting corners in examples for the sake of expediency doesn't seem so bad.
I'm fine with the idea of a router simply being a router; I really take exception to yet more PHP examples illustrating bad coding habits being on the web though.
The actual library code is just the 91 line file in the root directory. I don't understand why people are being so negative about 91 lines of code. It's very simple and most of it is quite well written.
How is this project 'misguided', just from face value?
And they do show the best way to use the router. Rendering content is not related to the library at all.
How you write your handlers and display your views is completely up to you. Why should a simple router dictate how you write the rest of your code?
I agree that it could negatively influence code that more novice programmers might write with it. They should replace the code inside the page handlers with a comment indicating that the user should render output there.
Most frameworks hide including views by way of extract() and output buffering. There's no real benefit to doing that on a small app. And no benefit on an api.
Most frameworks hide including views by way of extract() and output buffering. There's no real benefit to doing that on a small app. And no benefit on an api.
There's always a benefit to not copying and pasting code, which is what writing an include in each of your handlers boils down to.
Could you offer some form of explanation as to why you find conditional includes so odious?
It boils down to sloppy thinking; your objects should be well defined enough that you're not having to include extra pieces of code in the middle of execution.
You could argue code reuse - but why isn't that code either part of the object in the first place, or an object in its own right?
If you know some examples of elegant code written with includes scattered throughout it, conditionally or otherwise, I'd love to see it.
I will make exceptions for some parts of page rendering; it can sometimes be a good idea to put page elements into their own files and pull together when necessary. But for including code? No. Just no.
Edit: I took exception in this case, as it's non-obvious what happens once the view file has been included, not to mention the lack of a base example handler class implies and encourages people to write code like this.
RewriteRule ^foo/([0-9]+)/bar /foo/bar.php?x=$1 [L,NC,QSA]
Done.
These things come across as monkey-see-monkey-do. Other frameworks in other languages have routers, therefore they must also make sense in PHP.