The Montoya Herald — ChristianMontoya.com
Back in February, I was writing CSS every day and I decided to share some of the CSS techniques that I always use. Lately I've been writing more PHP than anything else and I've found myself using some very useful techniques all the time.
Now I know that PHP isn't very "cool" these days; it's probably the most misunderstood web language because all the script kiddies use it to include files on their webpages and put up forms that get hacked hours later. But I think PHP deserves a whole lot more credit than it gets; it's fast, it integrates very nicely with Apache, it got all the right features in version 5, and even if you don't like using it, there's always a chance you might have to write some anyway. So, maybe these tips can help you out:
$s = "This is something "; $s .= "that will make "; $s .= "a long string, with "; $s .= "a variable: $x.";
$s = <<<HEREDOC This is something that will make a long string, with a variable: {$x}. HEREDOC;
$x = "Something with {$y['key']} and {$z}.";
$error to store a possible error, and then I realize 10 minutes later that there might be more than one error, and I need to make an array of errors, and I have to go back and rewrite things to use an array instead. Nowadays I don't get into that mess anymore; I just start with arrays and save myself the trouble.$pages = array( 'index' => 'startPage.php', 'contact' => 'sendForm.php' ); // could do some function here $thePage = 'contact'; $theAddress = $pages[$thePage]; // here I'll get sendForm.php
if( this condition ) { $x = 5; } else { $x = 10; }
$x = 10; if( this condition ) { $x = 5; }
foreach. Even arrays with numerical indexes. It's just way easier than worrying about a counter, figuring out the size of the array, etc. Even if I need a counter for any reason, I tend to use foreach anyway, and add a counter as if it were a while loop.
Update 11/12: As Jon H mentioned, this is only for when you want to retrieve the data in an array, like when you are getting the results from a database query. If you need to modify the data in the array, then you foreach$arr = array(0,1,2,3,4,5,6,7,8,9); foreach($arr as $key => $num) { if($num==5) { $arr[$key]=0; } } print_r($arr); //5 got replaced to 0
switch, not a mess of if statements. It reads better, by far. Just don't forget your breaks:
switch($x) { case 'one': ...; break; case 'two': ...; break; default: ...; break; }
case 'one': case 'two': ...; break;
<?= $out ?>
if( this condition ) : ... else : ... endif;
method='post' is always the way to go), then you should send it to a separate script that does all the work, then when it's done, send it off to a different script with:
header('Location: ' . $url);
$url can be relative or absolute. This way, the user can't hit refresh and post the data all over again, which is annoying, and you can send the user off to different scripts depending on the outcome of the processing.
Update: Andrew made a good point in the comments about reporting errors back to the user, which totally slipped my mind. I might write up something soon about a better way to handle forms.if statement without the curly braces because you only need one line, that doesn't mean you should do it. Code is easiest to read when you are consistent about the way you do things, regardless of the exact way you decide on, and when you develop a habit for writing things one way, you reduce the chance of making stupid typos down the road, because you'll have the correct method engraved into your head.That's my list; if I think of more things, I'll share them. In the meantime, share your PHP techniques, I would be very glad to hear them!
Also, don't forget to read my previous post about easy form processing with PHP.
1 something disappeared here. Don't know why it was there to begin with.
Nice stuff, a few comments:
In the 'Shortcut the else' part:
Why not use:
$x = ( this condition )And skip the whole If-statement. Of course this only applies when setting $x to a boolean.
In the part about clean syntax elements:
Support for short open tags is often limited by hosts and considered a bad habit because it might cause a conflict with languages like xml.
All the rest some nice advice, especially what you said about always needing an array.
Some pretty good tips . Thanks for sharing some of the techniques you use. Just like Larixk, one thing I'd question though is the use of short tags. Even though it does make life easier, it's probably better you stick to doing it the long way. What if you need to move your code/application to a server that doesn't have php short tags enabled? You're going to have errors in your code and either have to edit the code or make modifications to the php.ini config file. This could be troublesome for some people, especial those not PHP savvy. It also conflicts with the XML prolog, if it is used.
Oh and correct me if I'm wrong but I don't think PHP short tags is a PHP5 only feature. I do believe it is in the 4.x.x branch as well.
Regarding the "processing user input with a seperate script" thing:
My preference is to have the form POST go to the same page as the form and only use the redirect-after-post pattern on a success. The reason is, if you POST to the page you're already on and there's an error, you can easily prepopulate the form with the values that were entered first time round and highlight the mistakes. You can do this with a redirect-after-post but it requires saving things in a session, cookie or query parameters (you may be able to tell the browser to redirect with a POST but it's not guaranteed to work since the spec says a browser is only required to handle redirects with a GET).
Of course you could always get your separated POST handler to render the same page as the original form even though it's a different URL, but that's introducing an unnecessary URL and makes the app less RESTful.
I also prefer multi-stage forms to always take place on the same page (the ideal persistence method varies based on the length of the form and number of fields) so that intermediate stages aren't bookmarkable (you can save the current form stage in the session if you want people to be able to return to the same point later).
Larixk: Yeah, I gave a bad example, it makes more sense when dealing with strings.
As for the short tags, I guess I shouldn't have mentioned them because I don't actually use them at all, I know they are not good to use since they are definitely limited in support.
Yannick: I didn't say PHP short tags is PHP5 only, I said that the quick print,
<?= $x ?>is.Andrew: That's a good point. I think I have used sessions for errors in the past but admittedly I've done other things too. I had one case where I had a main script that handled the form processing and then included one of two different files, depending on whether the form was successful or not. I guess I should revise what I said then.
"Don’t like the ugly curly braces in your if-else statements? Just do:"
I dont think this will work.
I know it exists but whenever I looked at stuff like phpBB and similar "big" projects, they do not use this.
Anyway, I am saying this from afar, I have since quite long left the world of PHP, only do it for a very few things and hoping that I can one day completely move away from it, for a scripting language it feels extremely poor for general purpose tasks, and I would rather prefer to use one language that gives me optional GUI and can also be applied to the WWW - at best with a nicer syntax too.
Granted though, PHP is not hard to learn.
she: Wordpress uses it, that's where I learned it. It's just a matter of preference, but being rare does not make something bad.
I personally don't care if I use one language for offline and another for the web. I've used so many languages, but I'm a web developer and everything I do lately is online so I can't speak for your point. The merits, however, are more than just that it's "easy to learn," which really doesn't say much because learning it and learning it well are two different things. What counts, though, is that PHP has to be the easiest scripting language to set up on a web server. That's why almost every web server has it by default. I optimized some dedicated servers for my employer, and all I had to do was install some packages and restart Apache. I like that level of simplicity.
an even shorter if else statement:
$foo = ($bar == 10) ? $bar : $x;
that translates to:
if ($bar == 10) {
$foo = $bar;
}
else {
$foo = $x;
}
Christian: Hope you don't mind me asking, but where did you see that the 'quick print' syntax is only available in PHP 5?
Hmm, maybe I misspoke, I can't find it anywhere. I guess it's available in PHP4? That would be good to know.
Another nifty use of short tags is the echo equivalent: which saves you some typing and looks very clean when used within HTML.
oops!
<?= $variable; ?>should work better.Regarding "shortcut the else", I ran some timer tests, and got this (1 million iterations):
if-else: 0.8151798248291 sec
if: 0.9922239780426 sec
tertiary: 0.93130898475647 sec
That is,
if($condition){ $x=5; }else{ $x=10; }is fastest, followed by$x=($condition ? 5 : 10);, followed by$x=10; if($condition){ $x=5; }. However, this is if$conditionis true. If$conditionis false:if-else: 0.82969498634338 sec
if: 0.81288599967957 sec
tertiary: 0.82582521438599 sec
So, perhaps, one should choose a style depending on whether the condition is more likely to be true or false. I personally either define a default value immediately (as suggested in the article) or use tertiary. It's nice to ensure that a variable is defined early on, rather than forgetting to write the
elseblock.Speaking of speed, and in relation to your bit about loops,
++$iis consistently faster than$i++, but supposedly only in PHP.Ron, you have to share your setup. CGI or CLI? v. 4 or 5? FastCGI/Zend/APC/eaccel/Turk or no optimizer at all? Obviously the performance here would depend on how the code is compiled (or if it is not compiled at all).
Christian: Yeah I think the 'quick print' syntax is available in PHP 4 as well. At least that is the impression I get from the manual and of course as long as short open tags is enabled.
In any case thanks again for sharing the techniques.
Christian, you might remember this better than I do from the optimization class, but way down at the assembly level, I think the two methods should be the same. (Granted I didn't screw up the assembly too bad, which I've completely forgotten the syntax for.)
preload-if
1. add 5 0 x (move 5 into $x)
2. bgt/blt/beq 4 (the if statement)
3. add 10 0 x (move 10 into $x)
4. (continue)
regular if/else
1. bgt/blt/beq 4 (the if statement)
2. add 5 0 x (move 5 into $x)
3. jmp 5 (skip the else block)
4. add 10 0 x (move 10 into $x)
5. (continue)
I seriously need to review my assembly again. Regardless, at the machine level, that should be what's happening? In the preload case, it executes 2 commands if it's true, and 3 commands if it's false. In the if/else case, it executes 3 if the statement is true and 2 if the statement is false.
Regardless, that's is a great space saver in code. I fall into two syndromes that you listed above: I have having a bajillion short if/else statements, so I often cut corners and skip the consistency of putting curly braces. I think this trick will save me from my curly braces syndrome. =P
Christian: I used PHP 4.x, CGI, no optimizer. I avoid optimizers whenever measuring the raw speed for a piece of code.
Albert: Wow, I never wanted to see that kind of stuff ever again. I guess Ron would be correct, it depends which one is more likely.
Ron: Would it matter if you used CLI instead of CGI?
The quickprint syntax is available in both 4 and 5.
Thanks JGM.
I like using variable variables in my main nav file.
At the top of every page I set a page id variable.
In the nav file I set a variable variable to "selected". Note the double dollar sign. This dynamically creates a variable with the same name as the current value of $page_id.
Now for each item in the nav I just include the page id variables into the class of each item so I can style the selected link differently.
<li class=''>Home
<li class=">About
<li class=">Contact
It's easy to lose track of where these variables names are coming from so I would suggest limiting it's use to obvious cases like navigation. To help eliminate the possibility of one variable overwriting another with the same name, I've added 'p_' to the name of each id.
Bah, the code tags were stripped!
The variable variable syntax is: $$page_id.
So, if
$page_id = 'p_home'
and
$$page_id = 'selected'
then
$p_home == 'selected'
Oh my god….
There are so many things wrong with these little pieces of advice I don't even know where to begin. I had to read it twice to make sure it wasn't meant as a joke. Literally everything you've written is wrong on so many levels it's hard to believe this is meant a serious advice.
It's the PHP equivalent of telling people to build their layout exclusively with tables, putting div-tags around everything and naming your classes 'bigredheadline' and sticking their CSS definitions somewhere in between the HTML.
In just a few examples you've just managed to show how to make even the most simple bits PHP-code utterly unreadable, impossible to maintain, non-portable and easy to hack.
This kind of crap is exactly what gives PHP such a bad reputation.
Cornelius: I remember doing that once, it's cool that with PHP you can use $$x to reference a string as the name of a variable. Then I got into the habit, though, of storing navigation in an array where they key is the page name and the value is true/false, and putting that in a function that sets which one is true:
$nav[$truepage] = true;Horses for courses
Rick: Are you sure?
There's nothing in this article about security. It would have taken me hours to talk about that. Obviously you can't just do:
$page = $_GET['page'];You have to make your own function for validating it and run it through that first. I know my readers aren't stupid, people know this stuff.
AFAIK, that's the only glaring concern in my code. If there's something that you think is wrong, instead of making a poor analogy, why not educate me and the other readers here on why it's wrong and why it should be done differently?
There's a lot of money depending on the "noobish" PHP that I write so sooner rather than later would be helpful.
All good tips I also came to over the years, except one: never ever use PHP short open tag (ie <? instead of <?php, or <?=)
It is not always working, depending on server configuration (see ini.core on php.net for more info.
Regarding always using ForEach.
ForEach is a loop construct designed for iterating collections. You can use it on any collection that implements the Iterator interface.
ForEach is not a designed to replace "for".
If you need to modify the collection you are iterating, you never want to use foreach, because it can cause very strange results (and in most languages, throw an exception).
Okay, let's not take the easy targets like <?= (goodbye portability), omitting curly braces in if/then (try maintaining other peoples code, and you'll find out soon enough why this is an awful idea), using flaky expensive constructs complex strings syntax and the 'please inject malicious code' $_GET.
(Sorry, I don't assume people you seem to have to explain to what associative arrays are to know about the dangers of using unsanitized GET parameters. Maybe you shouldn't either.)
Under "Build arrays. Always" you explain this by saying that it saves you time not having to rewrite the code when circumstances change. But your just shifting the problem, not solving it. Now the rest of your code needs to know that the errors are contained in an array, and has now been rewritten to address them as such. Same problem, different data type. This is why people much, much cleverer then you and me have thought of the notion of 'abstraction'. There is no need for the piece of script to know how the errors are stored, as long as it can get at them. (This besides the fact that $error is wide open to change by pieces of code that have no business touching it.) So the smart thing to do for instance to create a simple class that stores and returns error messages through neutral interfaces like 'ErrorHandler::getMessage()'. And whatever you do with your errormessages, (for instance make them multilingual) you won't have to change the rest of your code. Ever.
(Yes, this to is a gross oversimplification, but at least it sends people in the right direction instead of another dead end.)
Jon H: Yes, I should have mentioned that, thanks.
Rick: Do you seriously not prefer heredoc syntax?
Also, please read the article again, you are ~12 hours behind here.
As for abstraction, yes, I might write my code that way if I'm making something that's really complex, but seriously, a lot of the stuff I do never needs to be that complex. Thanks for the tip though. You could have shared it without being so insulting.
Anyone else: This is a damn discussion. Let's share, ok?
Regarding the use of foreach, you mentioned that you can't use it to modify arrays. That's only true if the contents of the array are native values, such as numbers or strings. If your array is full of objects or other arrays, you can do this:
foreach ($arr as &$thing)
$thing->var1 = 23;
As a previous comment suggested, the use of iterators is probably preferred over this, but for those of you who are adventurous, give the above code a try.
One thing to point out about the alternate "no-brackets" if-else syntax is that it doesn't fold well in editors.
Great Post! PHP Rocks. It's fun, simple, and very powerful.
hmmm… i'm pretty sure that the "default" and the end of a switch statement doesn't need a break after it.
Regarding "the bug" you mention. Following your link I see no comments since ~2001. Is this relevant.
Anyway, it's only mentioning "addslashes". Does anyone use that these days? PHP security books I've read of late recommend the (thoughtful, not reflexive) use of HTMLENTITIES().
Thoughts?
Yes,
htmlentitiesis much more useful, since turning < into < really eliminates about 99% of injection hacks. Also, if you can white-list input rather than trying to clean it (like, you only allow letters and numbers), that's much more safe.In "Walk through arrays with foreach" you say you can't modify values using foreach. Why?
You could use something like
foreach($array as $key=>$value)
{
$array[$key]="new value";
}
Hi Chrisitan:
Good to see your post and getting into php.
However, I have to disagree with:
Don't do that.
Use instead:
Sure it's a bit more typing, but using short tags is not recommended for a variety or reasons, one of which includes readability.
Also, check out the use of heredoc with variables in css, as I show here:
http://sperling.com/examples/pcss/
It make doing css a bit easier.
Cheers,
tedd
Arrg:
With above I meant:
No:
<?= $out ?>
Yes:
<?php echo($out) ?>
I guess I need to break that habit now.
Anyway, I forgot to tell you: great post, man. Really useful,
Some pretty good tips (I've grown fond of the Heredoc syntax myself), but in any case: "There should be one–and preferably only one–obvious way to do it."
Stuff like <?= is a bad idea. Just do
As for the conditionals, I find it much nicer to just put the braces where they logically should go instead of on their own lines all the time:
if (blah) {
// something
} else {
// something
}
Switches are worthless if your condition has any complexity to it, and when you start putting cases and their predicates and the break on the same line readability severely drops.
My own personal tip is to always validate your input, especially when you are inserting or retrieving things from a database. A function I've used:
function escape_data($data) {
global $dbc;
if (ini_get('magic_quotes_gpc')) {
$data = stripslashes($data);
}
return mysqli_real_escape_string($dbc, trim($data));
}
Which reminds me: if you have PHP >= 5, use mysqli stuff.
As for using multiple files for form processing and the like, that brings in tricky things especially if you don't consistently code against redirecting if the page is accessed directly. I love PHP Redux, Andrew made some good points for it. Besides, if the user is using Firefox it will ask them if they really want to resubmit the POST data if they decide to reload for some reason.
Thank you. Some of these tricks I know and use, some are new. The idea about everything being an array is great!
Talking about this point: "Walk through arrays with foreach". Currently you CAN edit data in the array. Here is an example:
$numericarray = array(0,1,2,3,4,5,6,7,8,9);
foreach($numericarray as $key => $number)
{
if($number===5)
{
$numericarray[$key]=0;
}
}
print_r($numericarray);//5 got replaced to 0
Also, makes code unreadable and it won't work with short-tags disabled.
In the last part, the short-tag code got removed. It should be:
Also, makes code unreadable and it won’t work with short-tags disabled.
Arggg it won't appear correctly :S the code is
Also, makes code unreadable and it won’t work with short-tags disabled.Emilio, how about you e-mail me what you are trying to post and I will add it to the post? Or, you could use Postable to sanitize your code.
Hi,
There is a better way to walk through an array and modify the data :
and of course there is function as array_walk() or array_map()
Bye
$array
JuG,
yep, I've learned since I wrote this that you can do:
foreach($array as $index=>&$value)I use it all the time now. I should probably post an updated article with the things I've learned!
A few tips of modifications, haven't read comments so sorry for possible echoing.
About sending back errors, try catching all errorcodes in the processing script ($error_codes[] = 1, $error_codes[] = 4), send back user (header('Location: '.$url.'?error_codes='.implode("-", $error_codes)), give feedback to user:
$error_codes = isset($_GET['error_codes']) : explode("-", $_GET['error_codes']) : null;
and in html
0): ?>
About "Use switch, not a mess of if statements", a previously undefined $x will return in a notice/warning (can't remember), and a line such as the one below (ofc depending on where $x is coming from) should be added before switch case.
$x = isset($_GET['x']) ? $_GET['x'] : null;
About "Follow a strict order in your scripts": I'd rather se much earlier inclusion of files, setting general options (error reporting etc), sessions, other headers, output-buffering and such. All of these options might need input/config from databases, so querying those might be wanted done earlier (especially earlier than user input, since that often need to be compared to data from db).
Finally: ternary operators, don't forget to mention those. Especially in the "runtime" script (the output/template patr), they barely take up any space at all.
chunk that disappeared after "and in html":
< ?php if(sizeof($error_codes) > 0): ?>
< ?php endif; ?>
Talking about this point: "Use switch, not a mess of if statements.".
You can also use an array in the switch case. Here is an example:
switch($x)
{
case (in_array($x, $arrayVar)):
…
…
break;
}
thanks for sharing! learned new
i've been doing most of your techniques too =)
Some interesting tips.. but you are sacrificing readability and portability in some of them.
I dont agree with "Shortcut the else" concept.
Readablity is much more impoertant that these 3 lines you win.
And i'm always using curly braces with if, else, for, foreach etc. Always.