Hello everyone!
I have a small issue with URL rewriting in .htaccess. Not that anything is not working, its just the issue with the size of the whole thing.
Here is a sample from my file:
RewriteEngine On RewriteRule ^/news/([0-9]+)/$ /news.php?news_id=$1 RewriteRule ^/article/([0-9]+)/$ /article.php?art_id=$1 RewriteRule ^/user/([0-9]+)/$ /show_user.php?user_id=$1 RewriteRule ^/login/$ /login.php ...
See? It gets awfully long. What can I do here? Is there anything I can do? I thought of a unified URL structure like NEWS/ACTION/ID so its like news/delete/23 or news/edit/35 but that doesn't work for all of the stuff.
So, is there anything I can do? Or am I cursed to editing the file every time I add or change something?
RewriteRule ^(.*) index.php?q=$1 [L,QSA]
OK, this opens up a new problem though, how do I parse the input? Something like this:
index.php?q=/news/edit/45/monkey/5/blah/10
So, the first parameter would always be the file ( news.php ), the second would be the name of a parameter and then the value, the next the name and then the value etc.
So the upper URL would be:
news.php?edit=45&monkey=5&blah=10
Correct?
I got this from CakePHP:
RewriteEngine on RewriteRule ^$ engine/index.php [L] # Translate path into the webroot if not already done RewriteRule !app/webroot/ - [C] RewriteRule (.*) app/webroot/$1 # If file doesn't exist in webroot then use Cx RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule .* engine/index.php
This is cool. It works like this: you have a website at http://www.contoso.com/mysite/. On the server, mysite/app/webroot is the folder where you put things like static HTML pages, CSS files, whatever, and mysite/engine is where CakePHP lives. When a user requests http://www.contoso.com/mysite/style.css, the RewriteRules check mysite/app/webroot/style.css. If it exists, it serves that file. If it doesn't, the rules instead run mysite/engine/index.php, which uses $_SERVER["REQUEST_URI"] to get the name of the requested file (so, if you request http://www.contoso.com/mysite/news/delete/27, it will look like "mysite/app/webroot/news/delete/27", and you can just remove everything up to "app/webroot").
This method is better than X-G's because the query string isn't affected by the rules, and because it allows static documents to co-exist.
[append]
You parse the output by exploding your request (explode("/", $request)), and then parsing the resulting array (array("news", "edit", "45", "monkey", "5", "blah", "10")). If you're using CakePHP or CodeIgniter or something similar, then you have code like this:
class controller_news { function edit($id) { // news/edit/45 calls this method with $id = 45. // You can get all the parameters passed like this: $args = func_get_args(); var_export($args); // Prints: array("45", "monkey", "5", "blah", "10") } }
In one project, I was just passing everything to a file like X-G said, checking a set directory to see if the file existed, if it did I served it up, if not I checked for plugins/pages before 404ing.
You should see the stuff I setup for the wiki:
| 1 | RewriteEngine on |
| 2 | |
| 3 | RewriteCond %{REQUEST_URI} ^/bin/view/.+ |
| 4 | RewriteRule ^/bin/view/[^/]+/(.*) /$1 [R] |
| 5 | |
| 6 | RewriteCond %{REQUEST_URI} ^/bin/view/.+ |
| 7 | RewriteRule ^/bin/view/[^/]+ / [R] |
| 8 | |
| 9 | RewriteCond %{REQUEST_URI} ^/bin/view$ |
| 10 | RewriteRule ^/bin/view$ / [R] |
| 11 | |
| 12 | RewriteCond %{HTTP_USER_AGENT} Ask\ Jeeves/Teoma [OR] |
| 13 | RewriteCond %{HTTP_USER_AGENT} VoilaBot [OR] |
| 14 | RewriteCond %{HTTP_USER_AGENT} BecomeBot [OR] |
| 15 | RewriteCond %{HTTP_USER_AGENT} Exabot [OR] |
| 16 | RewriteCond %{HTTP_USER_AGENT} Googlebot [OR] |
| 17 | RewriteCond %{HTTP_USER_AGENT} Yahoo!\ Slurp [OR] |
| 18 | RewriteCond %{HTTP_USER_AGENT} Google\ Desktop [OR] |
| 19 | RewriteCond %{HTTP_USER_AGENT} MQBOT/Nutch [OR] |
| 20 | RewriteCond %{HTTP_USER_AGENT} msnbot [OR] |
| 21 | RewriteCond %{HTTP_USER_AGENT} YahooSeeker [OR] |
| 22 | RewriteCond %{HTTP_USER_AGENT} OutfoxBot [OR] |
| 23 | RewriteCond %{HTTP_USER_AGENT} FreshCrawler [OR] |
| 24 | RewriteCond %{HTTP_USER_AGENT} robot [OR] |
| 25 | RewriteCond %{HTTP_USER_AGENT} spider [OR] |
| 26 | RewriteCond %{HTTP_USER_AGENT} Twisted\ PageGetter [OR] |
| 27 | RewriteCond %{HTTP_USER_AGENT} yacybot [OR] |
| 28 | RewriteCond %{HTTP_USER_AGENT} yacy [OR] |
| 29 | RewriteCond %{HTTP_USER_AGENT} Yahoo-MMCrawler |
| 30 | RewriteRule ^(.*) $1?useskin=simple [C,QSA] |
| 31 | |
| 32 | # Verifying if user forgot to put trailling slash. If so, we'll rewrite to Main_Page |
| 33 | RewriteCond %{REQUEST_URI} ^/?$ |
| 34 | RewriteRule ^/?$ /index.php/Main_Page [L] |
| 35 | |
| 36 | # rewrite Actions |
| 37 | # 'edit', 'watch', 'unwatch', 'delete','revert', 'rollback', 'protect', |
| 38 | # 'unprotect','info','markpatrolled','validate','render','deletetrackback','print', |
| 39 | # 'dublincore','creativecommons','credits','submit','viewsource','history','raw', 'purge' |
| 40 | RewriteCond %{REQUEST_URI} ^/(purge|edit|watch|unwatch|delete|revert|rollback|protect|unprotect|info|markpatrolled|validate|render|deletetrackback|print|dublincore|creativecommons|credits|submit|viewsource|history|raw|purge)/ |
| 41 | RewriteRule ^/(purge|edit|watch|unwatch|delete|revert|rollback|protect|unprotect|info|markpatrolled|validate|render|deletetrackback|print|dublincore|creativecommons|credits|submit|viewsource|history|raw|purge)/(.+) /index.php/$2?action=$1 [L,QSA] |
| 42 | |
| 43 | # Don't rewrite requests for files in MediaWiki subdirectories, |
| 44 | # MediaWiki PHP files, HTTP error documents, favicon.ico, or robots.txt |
| 45 | #RewriteCond %{REQUEST_URI} !^/(stylesheets|pub|config|stats|skins|bin|languages|locale|math|images)/ |
| 46 | #RewriteCond %{REQUEST_URI} !^/.+.php |
| 47 | RewriteCond %{REQUEST_URI} !^/error/(40(1|3|4)|500).html |
| 48 | RewriteCond %{REQUEST_URI} !^/favicon.ico |
| 49 | RewriteCond %{REQUEST_URI} !^/robots.txt |
| 50 | |
| 51 | |
| 52 | # Do main rewrite |
| 53 | RewriteCond /var/www/%{REQUEST_FILENAME} !-f |
| 54 | RewriteCond /var/www/%{REQUEST_FILENAME} !-d |
| 55 | RewriteCond %{REQUEST_URI} !^/(stylesheets|pub|config|stats|skins|bin|languages|locale|math|images|history|icons)/ |
| 56 | RewriteCond %{REQUEST_URI} !^/(stylesheets|pub|config|stats|skins|bin|languages|locale|math|images|history|icons)$ |
| 57 | RewriteCond %{REQUEST_URI} !^/.+\.php |
| 58 | RewriteCond %{REQUEST_URI} !^/~.* |
| 59 | RewriteRule ^(.*)$ /index.php$1 [L,QSA] |
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
Was also in the file. I just neglected to mention that.
#RewriteCond %{REQUEST_URI} !^/.+.php
Technically, "!^.+\.php"
Perhaps you'd rather have news/edit/35 be news/35/edit, which is hard to do (along with other similar style of URLs) unless you use a global dispatcher.
I use this for my CMS in the /var/www/cms/site directory:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /.dispatcher.php [QSA,L]
The document root directory is: /var/www/cms/site/domainname.com/web. I then use an alias in each vhost to point /.dispatcher.php to /var/www/cms/site/dispatcher.php.
That single file just looks at the domain name, loads the configuration file for it, loops through each module that implements IRequestHandler, and lets them determine who gets to serve the request. (First come, first serve.)
It's beautiful because URLs can be anything and every site can share the same exact PHP module code.
Each module can implement any URL how it sees fit. So the news module could see /news/2008/01/01/newyear and decide to handle the request by loading up the news item and calling $tpl->display('News/view') which would first load up the website's specific news template.
The CMS module can look at URLs in a database and display HTML as entered via a WYSIWYG online editor. And anything in the /var/www/cms/site/domainname.com/template directory automatically gets viewed as a last resort (if the name matches the URL) so you can easily add one-off pages per each site.
Those are just examples of a couple modules; there are plenty of ways to do this. The key thing is, though, that "one PHP file per page" based websites are not good.
I'll think about the modules idea. I think its a pretty good idea. But I only have one site, so isn't it kinda pointless?
Those are just examples of a couple modules; there are plenty of ways to do this. The key thing is, though, that "one PHP file per page" based websites are not good.
Hmm... I am getting something wrong here. Don't you do "one file per page" anyway? Isn't the difference in the fact that you hide it?
You're right that it is a lot of overhead if never reused. But one site becomes two sites ... and so on until you are sick of writing the same stuff over and over again.
And no, my approach is far from page based. The entire module sits inside of a class. For instance, a "pretend" module:
| 1 | <?php |
| 2 | class Module_News extends Module implements IRequestHandler |
| 3 | { |
| 4 | public function handleGetRequest(HTTPRequest $req) |
| 5 | { |
| 6 | // /news/view/$id |
| 7 | if ($req->path[0] != 'news') return FALSE; // doesn't handle the url |
| 8 | |
| 9 | switch ($req->path[1]) |
| 10 | { |
| 11 | case 'view': |
| 12 | $tpl->news = DBO_News::find($req->path[2]); |
| 13 | $tpl->display('news/view'); |
| 14 | return TRUE; |
| 15 | |
| 16 | case 'foo': // other logic, etc |
| 17 | } |
| 18 | |
| 19 | return FALSE; |
| 20 | } |
| 21 | } |
| 22 | ?> |
All of the logic goes inside the class. That's the stuff you don't ever want to rewrite. If I have a second website that needs something a little bit different, I first try to add the functionality via configuration. If that fails to be practical, I just extend the class module and use it for that particular website.
The template stuff is per page, but that's fine. The basic news "view" template might look like:
<mef:template container="default"> <html> <h1>{news.title}</h1> <div>{news.body}</div> </html> </mef:template>
Then for a particular website, I'll probably end up copy/pasting it into the website's local template directory and editing it to look like however I want.
The key is that I can easily build a new website without having to write a line of duplicated PHP. I expect to write custom template files ... that's what makes web sites unique. But the "business logic"? It's mostly reusable.
This is my approach that works great for me, and you may find something you like better. But never resort to spaghetti PHP code. It's never worth it.
Now I get it. So, this is just theoretical, you have modules in a directory, like modules and when someone requests the page the request is send to index.php or whatever the main file is.
Then the modules directory ( or database, don't really know ) is scanned for modules. And since every module is derived from Module you can just do:
$request = new IRequest(); $request->page = ...; while(!EndOfModules()) { $module = new Module($data["module_name"]); $module->handleGet($request); }
Wow, even if this is not exactly what you use, its pretty darn good. This way you can easily add way more modules without any hassle. No hard-coding... Real flexible. Hehe I'm all excited now! 
Just one more thing, how do you handle where the stuff gets displayed. The website has a certain structure. Header, footer, navbar etc. How do you know where each module will go? It could have a $type member var in it... Or is there a better way to handle this?
I have an ini file per website (domain) that says which modules to load:
[modules] News=true AutoTemplate=true
Those are looped through and added to a modules array within the base Application class.
I have a function called something like Application::callInterface('IRequestHandler', 'handleRequest', $args) and that is responsible for looping through each module, calling 'handleRequest' until one of them returns TRUE. It's been a while since I've looked at my base classes, so I don't remember exactly what I'm doing.
Regarding your second question, I'm not sure exactly what you are talking about. But I use "containers." One might look like:
<html> <head><title>Default Container</title></head> <body> {html} </body> </html>
The {html} correlates to the <html> field in the template file. Same goes with any other field you want to pass as a tag or as a parameter (in the <mef:container /> tag).
Also note that templates can also pull in data as long as a recognized object is used.
// loop through recent threads <mef:loop collection="news.recent(count: 5)" item="n"> {n.title} </mef:loop>
For instance, that would call DAO_News::recent() inline. (The DAO class of objects are just read-only interfaces from a template to the more general purpose DBOs.)
That allows templates files to do more than what they might originally have been intended to do by the module writer. (e.g., maybe on your News/View template page you also want to show something from a different module.)
Seems pretty logical. But, there is one line I do not understand as I do not have so much experience in the OOP world:
class Foo extends Bar implements Baz
So, what does this mean? That Bar implements Baz and that Foo extends Bar? That would mean that Baz is an abstract class and that Bar implements it and Foo extends the implemented class Bar?
Regarding the HTML thing, I think there was a misunderstanding. I maybe just talking garbage but anyway. Allegro.cc has two sidebars and the center where the content is. Now, I am speculating, but I reckon that the sidebars are constant ie. are not tied to modules? You know what I mean? Sure, the content is tied to a module, but what about the sidebars etc.? Guess theres not much need for that anyway... Just asking.
Baz is an interface, so class Foo inherits from Bar and implements everything required by the Baz interface.
OK, I thought it was something like that. Thanks for destroying my doubts.
Looking at the PHP code Matthew posted, does PHP really look almost like Java these days?
Looking at the PHP code Matthew posted, does PHP really look almost like Java these days?
Yes. A horrible mix of Perl and Java. I still wonder why people like it.
Don't make me laugh! Even PHP at its worst isn't nearly as bad as perl! Yuck!
Good PHP isn't bad. Bad PHP is horrible. cough*zencart*cough.
Don't make me laugh! Even PHP at its worst isn't nearly as bad as perl! Yuck!
You obviously haven't seen actual live PHP applications then. Right now I'm doing a small job working with ZenCart, if you feel like going out of your mind, take a look at all the template files. Almost every PHP program I've ever seen is made the same way.
However, my Perl programs are much much cleaner.
Look at it this way, PHP is the most popular scripting language currently. So naturally there will be more people that write ugly code. Its also very simple, contributing to people writing said bad code.
Perl is scary, thats why beginners don't use it*.
But PHP code can be very clean, readable and efficient if written in the right way. Works for any language.
* At least not as much as PHP IMHO.
PHP is pretty fast and has an enormous amount of junk that makes web development easy. That junk is really invaluable.
(more on other bad coders coding in php): You shouldn't judge your language choices by the stupidity of other coders.
Look at it this way, PHP is the most popular scripting language currently. So naturally there will be more people that write ugly code
Clueless much? There area actually still more usefull sites that run perl than PHP. Well known fact, php was once implemented with Perl.
Perl is scary, thats why beginners don't use it.
Its the same syntax as php! Except theres three separate special datatypes and BUILT IN regexes. I don't see much of a difference.
But PHP code can be very clean, readable and efficient if written in the right way.
Oh sure, apply that to your precious php, and not perl? 
Perl was my first programming language. Taught it to myself by reading a book and playing with scripts. If I can do it, anyone can.
PHP is pretty fast and has an enormous amount of junk that makes web development easy. That junk is really invaluable.
Junk is indeed the right term. And it dumps it all in the global namespace. hundreds of functions you're likely to not use in most apps!
(more on other bad coders coding in php): You shouldn't judge your language choices by the stupidity of other coders.
Except when most of the apps are coded in that way, then its more of a language issue. PHP was created to write spagetty <?php code ?> crap.
If you want to learn how not to write something, look at Zencart.
Well known fact, php was once implemented with Perl.
Not that it's related, but hasn't php been implemented in a lot of language?
Except when most of the apps are coded in that way, then its more of a language issue.
You can't actually think that... Whats the point of a language but to aide you in some purpose?
Not that it's related, but hasn't php been implemented in a lot of language?
It started out as a Perl program, and is now written in C afaik. From the state of PHP code itself, I can only imagine what php's C code looks like 
You can't actually think that... Whats the point of a language but to aide you in some purpose?
Ok, then it must be the other option, MOST php coders are completely worthless. Then again if it is that, then PHP attracts MOSTLY worthless coders, which doesn't put php in a good light.
Clueless much? There area actually still more usefull sites that run perl than PHP. Well known fact, php was once implemented with Perl.
If George Bush and Vladimir Putin prefer some Zango Cola over Coca-Cola then that does not mean that Coca-Cola is any less popular...
Oh sure, apply that to your precious php, and not perl?
Sure it applies to Perl, but you started the debate.
Perl was my first programming language. Taught it to myself by reading a book and playing with scripts. If I can do it, anyone can.
Same here man, just replace Perl with PHP. 
Junk is indeed the right term. And it dumps it all in the global namespace. hundreds of functions you're likely to not use in most apps!
Yeah it has its quirks, but PHP 6 will fix that AFAIK.
Except when most of the apps are coded in that way, then its more of a language issue. PHP was created to write spagetty <?php code ?> crap.
Popularity applies here... 90% of the code is bad because 90% of the users are beginners that don't have a clue. Its not a language issue... Anymore. Granted, it was some time ago... cough register globals cough
EDIT:
It started out as a Perl program, ...
No:
PHP was written as a set of Common Gateway Interface (CGI) binaries in the C programming language by the Danish/Greenlandic programmer Rasmus Lerdorf in 1994, to replace a small set of Perl scripts he had been using to maintain his personal homepage.
Popularity applies here... 90% of the code is bad because 90% of the users are beginners that don't have a clue. Its not a language issue... Anymore. Granted, it was some time ago... cough register globals cough
Take any popular php app and prove to me that its not crap code wise. Dare you.
PHP was written as a set of Common Gateway Interface (CGI) binaries in the C programming language by the Danish/Greenlandic programmer Rasmus Lerdorf in 1994, to replace a small set of Perl scripts he had been using to maintain his personal homepage.
I just said that. It started out as a perl program/script, and was then rewritten in C.
Yeah but technically, the Perl scripts weren't PHP! They were PHP after they were rewritten!
Anyway, phpBB 3?
Yeah but technically, the Perl scripts weren't PHP! They were PHP after they were rewritten!
This is only an asumption, but I'm betting his original site loaded and executed Php like template files with embeded Perl code, which explains PHP's similarity to Perl.
Anyway, phpBB 3?
phpBB? At least v3 seems to be using a templating system of sorts (custom format using html comments? huh?). Being brand new, I know little about it. But if its anything like the prior phpBB's, its pure crap.
phpBB? At least v3 seems to be using a templating system of sorts (custom format using html comments? huh?). Being brand new, I know little about it. But if its anything like the prior phpBB's, its pure crap.
OK Thomas, your turn! You show me some popular application written in Perl thats not crap!
Any popular application is likely pure crap. Thats why I use custom code for the most part.
Ever heard of slashcode? How 'bout apt?
Large projects written in Perl include Slash, Bugzilla, TWiki and Movable Type. Many high-traffic websites, such as bbc.co.uk, Amazon.com, LiveJournal.com, Ticketmaster.com and IMDb.com[16] use Perl extensively.
Those high traffic websites aren't popular applications.
A.cc uses PHP, and works just as well as those sites, just not on as large a scale.
Many high-traffic websites, such as bbc.co.uk, Amazon.com, LiveJournal.com, Ticketmaster.com and IMDb.com[16] use Perl extensively.
Cheater! Those are not available to the public and the applications aren't popular but the websites are! Different!
Hence, I could say Allegro.cc, I dare you to say otherwise, Matthew will smite you! 
Wow, you guys totally missed the first sentence. Try paying attention.
A.cc uses PHP, and works just as well as those sites, just not on as large a scale.
Scalability is part of usability.
Large projects written in Perl include Slash, Bugzilla, TWiki and Movable Type.
MediaWiki, Typo3, phpBB3, Drupal
apt is not a web application.
I thought we were talking about those. Anyways, time to go look at all the projects source code and vote which one has the dirtiest...
Scalability is part of usability.
I didn't say it wasn't scalable. I just said it wasn't on as large a scale. The sites from WP likely run on quite a few servers, whereas a.cc runs on one.
Intead of those apps, look at PHPBB2, ZenCart, and Moodle.
http://avatraxiom.livejournal.com/58084.html
Also, nobody ever claimed those apps were written well. What about Drupal or e107? There are many PHP apps that aren't coded badly. (Although I've never looked at the source of those applications, I've never heard anybody say anything bad about them).
Dude Thomas, why are you so angry about PHP?
He has to deal with Zencart. I only looked at it for under an hour and that was almost enough to make me angry about PHP. Zencart's devs do have a place on my hated developers list, though.
Dude Thomas, why are you so angry about PHP?
I'm more iritated about Vaneeto's (and many other people's random) Perl comments. But then I've just spent 3 days trying to hack an old template into a new version of ZenCart. God my head hurts.
Oh sure, sure, now I write random comments... I wonder what this is:
Yes. A horrible mix of Perl and Java. I still wonder why people like it.
Yes. A horrible mix of Perl and Java. I still wonder why people like it.
You can mix one or more non horrible things and get a horrible result you know.
Personally, my Perl comments are in return to PHP comments. I don't hate Perl. I don't like how the code looks, but I don't have enough experience with it to form a solid opinion.
You can mix one or more non horrible things and get a horrible result you know.
That was not the point. The point was that you were the first to make a random comment. I did not write random comments about Perl, one maybe, but all others were not random.
Well, at least not any less or more random then yours.
I didn't necessarily mean yours was random, most people's are.
Well, seeing as how we single handedly derailed this thread, I might as well give out cookies for the ones that helped! (with the problem at hand, not the derailing part
)
Regarding the HTML thing, I think there was a misunderstanding. I maybe just talking garbage but anyway. Allegro.cc has two sidebars and the center where the content is. Now, I am speculating, but I reckon that the sidebars are constant ie. are not tied to modules? You know what I mean? Sure, the content is tied to a module, but what about the sidebars etc.? Guess theres not much need for that anyway... Just asking.
A.cc isn't written on my complete CMS engine, but if it might look something like this if it weren't static:
| 1 | // index.tpl |
| 2 | <mef:template container="three-columns"> |
| 3 | |
| 4 | <html> |
| 5 | <ul> |
| 6 | <li mef:loop="n; news"> |
| 7 | <h2>{n.caption</h2> |
| 8 | <div>{n.body}</div> |
| 9 | </li> |
| 10 | </ul> |
| 11 | </html> |
| 12 | |
| 13 | <leftcolumn> |
| 14 | <h2>Allegro.cc</h2> |
| 15 | <ul> |
| 16 | <li>Depot</li> |
| 17 | <li>Forums</li> |
| 18 | </ul> |
| 19 | </leftcolumn> |
| 20 | |
| 21 | </mef:template> |
The first layer of tags is just a way to name a block of HTML. The container file could then reference them as {html} and {leftcolumn}.
Or another approach could be:
| 1 | // index.tpl |
| 2 | <mef:template container="three-columns" sidebar="Depot,Resources"> |
| 3 | // blah |
| 4 | </mef:template> |
| 5 | |
| 6 | // three-columns.tpl |
| 7 | <html> |
| 8 | |
| 9 | <body> |
| 10 | |
| 11 | <div id="left-column"> |
| 12 | <acc:column resources="{sidebar}" /> // a custom tag written in PHP |
| 13 | </div> |
| 14 | |
| 15 | <div id="main"> |
| 16 | {html} |
| 17 | </div> |
| 18 | |
| 19 | </body> |
| 20 | |
| 21 | </html> |
There's plenty of ways to do it. Obviously you want to keep the template files as close to pure HTML as possible. Sometimes this means creating custom tags that generate HTML that's otherwise hard to represent.
OK I got something figured out. Just need to put that down into code now. I have a question though, everything beyond the base directory of the domain is the Query string?
And I still don't get this line:
class News_Module extends Module implements IRequestHandler
I mean, I thought I got it but I don't. I tried to code something similar and now I know my ignorance is bigger then I thought.
An interface is an empty class:
interface Foo { public function bar(); } class Bar implements Foo { } // error - it has no function called bar
In a language like PHP, it's more of just a formality. It's just a way to make sure a class implements a set of functions.
OK I just got some of the basics working, pretty nifty I must say. So, do you actually have two separate methods in a module - that handle get and post requests? For example:
public function handle_get_request(httpreq $request); public function handle_post_request(httpreq $request);
Although I do not see the benefit in this as you can just as easily handle everything as GET and then check for $_POST vars in the get handler. Maybe I can make some sort of automatic form handler for these things.
I handle them separately just because there's an ideological difference: GET should be used when requesting a read-only page, and POST should be used when pushing data to a read/write page.
But technically there's little difference between them, and they could easily be handled together.
I don't like the idea of splitting GET and POST. It would seem like the biggest advantage of this method comes from being able to GET a form and then POST to the same URL, and by splitting it, you make the code more modular and readable. But I've found that much of the code is shared between the two requests (typically, the code for a POST request is just a preamble to the code for the GET request), and so you harm your readability by splitting it.
Yes, but I handle it much differently within my CMS. I don't care what URL you submit the POST data to. It's all encapsulated within a POST wrapper request. So my POST request handler looks more like:
function handlePost($post) { if ($post->namespace != 'News') return false; switch ($post->action) { case 'delete': // check permissions // validate fields // perform operation // redirect break; } }
This is because I don't want the CMS to define the URLs for all the applications it hosts. In short, you make a request with the namespace/action (e.g., News.Delete) along with a redirect URL on success (if wanted).
If you don't redirect and there is no error, then the "GET" request handler would kick in.
OK, I got it working with a prefixed set of Modules. Now, does PHP have any INI handling functions? Yeah, I know I can write my own and quite easily. But, just to avoid the hassle... Is there?
Great, got everything working. Except one little thing. For example, www.mysite.com/ should display the news. So, it works if the url is www.mysite.com/news/ or something but how do I do it if no params are defined?
EDIT:
Another thing, lets say you have a Statistics module. Now, this has to be called in every page. But if News is first and the param is news then News module will return true and loading of modules stops there. Confusion!
Great, got everything working. Except one little thing. For example, www.mysite.com/ should display the news. So, it works if the url is www.mysite.com/news/ or something but how do I do it if no params are defined?
I don't understand your problem. There's nothing stopping any module from handling any request.
Another thing, lets say you have a Statistics module. Now, this has to be called in every page. But if News is first and the param is news then News module will return true and loading of modules stops there. Confusion!
A module could do something transparent within the handleRequest (e.g., log to a database), but still return FALSE as to say that it didn't handle the request. Just make sure the Statistics module is loaded first. I treat the INI file as first come, first serve. That's been good enough for me.
I don't understand your problem. There's nothing stopping any module from handling any request.
Well I have something like this:
But you already answered that question:
A module could do something transparent within the handleRequest (e.g., log to a database), but still return FALSE as to say that it didn't handle the request. Just make sure the Statistics module is loaded first. I treat the INI file as first come, first serve. That's been good enough for me.
But that seems so, hackish, interfering with the natural flow of the PHP code is a dangerous business indeed.
I could implement something like an always_load option or something similar, but your solution sounds so much simpler!
There are various ways to solve it. In this particular case, I actually just rolled logging into the main application class. I build my CMS (actually it's a framework with a CMS module) on the fly. So while it may be useful to loop through the modules twice, by first calling a pre-processing function, I've just not had the need to yet.
That preprocessor function idea is great! There could be a handle_preprocessing() function! No double looping!
This is just great!
Well, this is basically everything I need for now. Just one tiny question. Its about style, its not particularly relevant, but I would like to know anyway.
What is better, that every class is extended from System and that System has all the functions that the module could use:
class Foo // Is it possible to extend from 2 different classes? { public function Bar() { // If that is not possible, this could be a solution: global $system; $result = $system->db->query("SELECT something FROM everything"); /* ... Moar code ... */ } }
This seems like its quaint. But yeah, I am infected from the "perfect design" virus. If its not perfect, its not good! Damn, I need to get over this!
You can only extend one class.
I do this:
class Module_Foo extends Module { public function bar() { $app = Application::getInstance(); // in reality, I rarely make direct calls like this. // usually an object (e.g., DBO_New) is used. $st = $app->db->prepare('...'); $results = $st->getAll(); } }
I wanted to avoid using globals, although there's really nothing wrong with it considering PHP's syntax can otherwise get cumbersome (thanks to its retarded scope rules). Or you could create a $this->app variable in the Module constructor.
Sorry for being stupid, but how is DBO_New used?
I'm suspecting:
/* ... class ... */ $db = DBO_New(); /* ... */
?
It's supposed to be "DBO_News." DBO stands for "database object," but it's sort of a misnomer in that it doesn't necessarily have to be stored in the database. There's nothing exposed in the interface that requires it.
Basically a DBO is any conceptual object. It may or may not correspond one to one with a database table.
$news = DBO_News::find($foo); // the constructor is private if ($news) { $news->title = "A new title"; // or $news['title'] = "A new title"; $news->update(); }
It gives me runtime flexibility to only query the data I need. For instance:
$recent_news = DBO_News::findRecent(); foreach ($recent_news as $news) { echo $news->reporter->name . "<br />"; }
The "reporter" isn't actually loaded by the initial query. It's used on demand.
Yes, this generates more SQL than is needed, but I don't care. The reason is that I cache HTML and data objects in memory if needed for performance.
In the above example, the findRecent() will generate a query that returns an array of news items without any linked information. Then for every unique reporter_id, it will load on demand the data for that person. That reporter data is then stored in memory so that the next time the page loads, only one query is run (findRecent()).
This is accomplished by:
The generic find() automatically will pull from memory if it can, thus eliminating simple one-off queries.
Again, this may seem like overkill if you know exactly how you will be using your data. But I chose this design with my templates in mind. I don't know when they will want to use extended information, nor do I want to force the template designers to use some SQL like syntax to inform PHP what to grab.
In this template, no extended data is used or queried:
<ul> <li mef:loop="n; news"> {n.title} </li> </ul>
But it's possible it might look like:
<ul> <li mef:loop="n; news"> {n.title}, reported by {n.reporter.name} </li> </ul>
Or even...
<ul> <li mef:loop="n; news"> {n.title}, reported by {n.reporter.name} who has a game called {n.reporter.projects[0].title} </li> </ul>
OK, but does the Module implement DBO News or is it a system object? I am thinking of letting each module have its own folder and then can have all classes it wants in there. That way the module-ness is not lost.
Oh, by the way, is it even useful to have these:
// include.php if(!defined("IN_SITE")) die; // index.php define("IN_SITE", true); include "include.php";
I'm getting bad headaches because of these plus __autoload! :S
The DBO objects live outside of the modules. They can be used with or without the rest of the framework. Module_News "knows" that DBO_News exists, but the reverse isn't true. The other modules probably never need to use DBO_News, but they could if need be.
I don't know what you mean by your include question. (I don't even use includes.)
Its used so someone doesn't access your classes by doing:
http://www.mysite.com/include/myclass.class.php
And the defines are there to kill the script if its accessed directly. But I know what your gonna say, you have all the include files out of the web directory!
Oh, by the way, is it even useful to have these:
No. Your include file doesn't execute any code, it just defines functions and classes. If a user accessed it, it would be a blank page.
And the defines are there to kill the script if its accessed directly. But I know what your gonna say, you have all the include files out of the web directory!
Yes, I do.
class/ All of my classes, including modules, DBOs, etc template/ System-wide (default / generic) templates site/dispatcher.php The driver for everything site/domain.com/template Website templates site/domain.com/web Static Web Files (this is the doc root)
That's a subset of the directories I use. Each website has a mod_rewrite that directs everything to /.dispatcher.php which is just an Alias to the global dispatcher.php file.
Anything in the "web" directory can be accessed directly by the web browser, but in general it's just static images and downloads. Occasionally I may slip a PHP file in there, but only has a hack because I'm not interested (at the time) in creating a proper module for the functionality.
I'm interested in how thats actually setup, do you place it all in /var/www and have just one virtual host setup in apache? or?
It's in /var/www/cms (I have other regular sites in /var/www/domain.com) with each cms-driven domain having its own vhost pointing to /var/www/cms/site/domain.com/web.
Ah, I figured as much, and the "links" are actual symlinks to the main controller?
Each sites' vhost has an Alias from /.dispatcher.php (which doesn't exist) to /var/www/cms/site/dispatcher.php. That's the only kind of link between the two. That main dispatcher handles everything via the classes that sit in the cms/class directory.
Haha I'm so lucky this thread isn't dead yet!
Time is of essence.
So, I still don't get it. What if there is no parameter in Request->path? What does a module show then? Index.tpl? This is now bugging me for some time and I just can't seem to get it yet... My brain is hardwired with page-based thinking!
Use whatever method that makes you happy inside. For me a path like /foo/bar/blah?z=y would look like (in JSON):
{
path: ['foo', 'bar', 'blah'],
params: {z: 'y'}
}
A path like / just becomes:
{
path: []
params: {}
}
I don't treat it as a special case. If nothing implements the empty request, then a 404 would be triggered just as if someone went to '/asdfsadfasdfasdf'.
So you could create a Module_EmptyRequest if you wanted. It could just load index.tpl.
I always load Module_AutoTemplate as the last, catch-all module. It just looks for any matching template. So /foo/bar would match in order: /foo/bar/index.tpl, /foo/bar.tpl, /foo/index.tpl, /foo.tpl, /index.tpl.
The null case just naturally searches for /index.tpl.
Matthew: what is your opinion on the CakePHP-style request handling like at the bottom of this post?
Are you referring to requiring that function names match the URL? I don't like it. But there's nothing preventing taking my generic request handler function and farming out requests to individual methods.
Still not sure what to do... I could let the News module handle the empty request and just display the page as the index. I could implement an Empty request module and when an empty request happens I fetch the news and everything thats needed on the front page and display it.
Anyway, while this thread is still open, could anyone help me with a regex? I need a regex for something like this:
<lang string="SOMETHING" args=($foo, $bar)/>
This would be translated into:
sprintf($language[SOMETHING], $foo, $bar);
I'm currently trying with a regex but it wont work no matter what I do. My regex skills are broken.