Forum Moderators: coopster
Before I get started, I'd like to have at least a basic understanding of some "best practice" procedures. Some I already know, like clearing a variable when it's no longer needed, but that seems pretty simple and from reading some boards here and there, I know there must be other, more advanced ways of writing good code. So, what are some other good steps to take to write efficient, error-free and lean scripts?
Any and all tips are appreciated!
Thanks,
Matthew
Imo it is the best way to practise direct with working scripts.. press crtl-s and go on..
functions and classes are nice to use in PHP, use includes..
view php.net a lot, you can find nice functions, info's etc..
goodluck and have phun
Remember, that there is ALWAYS more than one way to do something. Just because you code differently than Billy Bob Jones, doesn't make him right, but doesn't make him wrong either. Personally, I like loops. I like case statements. I like clean and neat and tidy code. (Really hard to do with PERL, but I can make it decypherable) Wheras my wife, she hard codes EVERYTHING. She builds Databases and applications for the government, and has a FAR larger knowledge-base than I do, however she still likes to hard code.(It's not like she's building a PC game where milliseconds of lag-time count, and this is where we argue ;0)) What I am trying to say is practice, and try to get more complex every time you write a new script. You'll develope your own style, unique to only yourself.
This may not be what you were looking for, but I felt it was inportant to say :0)
Other good practices
- use variable and function names that are "self-documenting"
- always use braces around control structures (if, while, for)
- don't cram too much onto one line. Remember, extra lines and spaces don't cause the same performance hit in a server-side language as they do when you're sending it out over the web.
Not really a "best practices" suggestion, but more a "programming 101" kind of thing: Try to break routines out into functions or classes.
- If things get too complicated in your head, think of the tasks and make each task a function and "code" it in plain English (or Swahili if that suits you). So on a first iteration, your script might look like this
Read data from text file
Search text file for all rows that contain the search term
Sort the results
Format the results as a list
Output the list as a web page.
Each of those tasks becomes a function that is either simple enough to break down in your head or simple enough to break down into another set of tasks. Don't start in coding until you've broken the tasks down enough to know basically how you're going to code it.
When you're developing, the function can be a simple placeholder that outputs a message "function 1 entered" just to make it run while you develop.
Tom
One thing that helped me is when I got strict with myself when it comes to naming conventions. That is, when you have a variable, set of variables, or functions, try to name them in a way that makes sense, and in how they can correspond or interrelate with each other. This will help when your code later gets more complicated and you need to branch out with special functions, classes, and dynamically included files. Also little conventions - like $whateverurl is a url for something, $whateverurls is a url I decided to save which includes the trailing slash.
[evolt.org...]
Alot of the tips from that page have already been mentioned above. Pretty wise crowd around here :)
Personally, I like to omit braces on one-liner loops and ifs. Also, I prefer the opening bracket on the same line as the construct.
function form_error($error){
$qry_string = '?error=' . $error;
foreach($_POST as $key => $val) $qry_string .= '&' . $key . '=' . $val;
header('Location: /orig_form.php' . $qry_string);
exit;
}
Personally I code in the style mentioned in the evolt article
Braces should always be included when writing code using if, for, while etc. blocks. There are no exceptions to this rule...
....
Braces should always be placed on a line on their own; again there are no exceptions to this rule.
Why? It makes it much easier to identify blocks, even in an editor with brace matching. The code is clearer and easier to read and less likely to end up with unmatched for improperly matched braces. It takes up more vertical space, but the solution to that is just to get a taller monitor :-)
Other random thoughts
*I think the PEAR guidelines on using spaces makes more sense. Tabs tend to create problems when multiple authors open the document in different editors.
*For variable names, I think either underscores or StudlyCapsStyle are fine.
*For file names, I often use prefixes like cl_MyClass.php, t_MyTemplate.php
*function names try to begin with a verb
*On one of my good days, functions have comment headers that include the name of the function, one line description, an argument list with expected types and return value and type.
*All scripts have a header file that sets basic parameters such as declaring a constants that will be used throughout, opening the db connection, starting the session, checking login and other functions that must be done for every page.
*The header file looks for a "local settings" file that should include all customizations necessary to move the script from one server to another (at least of similar type). So it will declare a constant DOC_ROOT which may be the same as _SERVER['DOCUMENT_ROOT'] but may not if the script will run in a subdir.
*Use lots of constants for basic settings - they are memory efficient and globally available.
*Run with error reporting set to max (E_ALL) but ideally on a live server you would set it to log errors rather than output them to the screen (I guess that's not a coding practice, but it's good practice all the same).
*Before using any variable, either make sure it has a default value or that you test that it is initialized with isset() [php.net] or empty() [php.net]
*Always check that variables are initialized if there's a chance they won't be.
I don't know if those are "best practices" but they work nicely for me.
BTW -for a lighthearted approach from the opposite angle, see the forum library for the thread on Spaghetti Code.
Tom
There are a few suggestions that I don't know what they mean . . .
*Before using any variable, either make sure it has a default value or that you test that it is initialized with isset() or empty()*Always check that variables are initialized if there's a chance they won't be.
. . . but I'm sure this is all great advice and a good portion will undoubtedly make it into my PHP style!
One specific question; my development box is Windows 2000 but I'll only host a site on Linux. Are there any issues that this might cause? The evolt article mentioned potential line-break problems but I'm not quite sure what it meant.
Keep the ideas coming - thanks!
*Before using any variable, either make sure it has a default value or that you test that it is initialized with isset() or empty()*Always check that variables are initialized if there's a chance they won't be.
This means you cannot assume a variable is going to be there and that it is going to contain values you expect. ALWAYS assume the opposite. What if you have a form with checkboxes and the user doesn't check any of them? Then that variable may not exist in the $_POST superglobal. You need to check for it's existence first.
Initializing variables; OK, let's take a simple loop, without any error_reporting() [php.net], this would work fine...
<?phpHowever, turn on your error_reporting and PHP will let you know you are getting sloppy.
error_reporting(0);
for ($i = 1; $i <= 10; $i++) {
$list .= "<li>$i</li>";
}
print $list;
?>
<?php
error_reporting(E_ALL);
for ($i = 1; $i <= 10; $i++) {
$list .= "<li>$i</li>";
}
print $list;
?>
Notice: Undefined variable: list in ...
If you initialize the variable first, no more errors...
<?php
error_reporting(E_ALL);
$list = '';
for ($i = 1; $i <= 10; $i++) {
$list .= "<li>$i</li>";
}
print $list;
?>
The biggest thing to keep in mind for cross-platform development is OS-specific differences. PHP is pretty good about offering predefined constants for many of them, such as PATH_SEPARATOR and DIRECTORY_SEPARATOR. Try to program everything for cross-platform implementations. Line breaks [webmasterworld.com] are probably the biggest issue, particular in mail functions. Any time I fear there may be an issue, that particular piece of code gets it's own function in case it needs to be tweaked or in case logic needs to be inserted to allow cross-platform functionality.
Another Resource:
PEAR Coding Standards [pear.php.net]
my development box is Windows 2000 but I'll only host a site on Linux.
That's why I offered this suggestions
*The header file looks for a "local settings" file that should include all customizations necessary to move the script from one server to another (at least of similar type). So it will declare a constant DOC_ROOT which may be the same as _SERVER['DOCUMENT_ROOT'] but may not if the script will run in a subdir.
Coopster is right (of course!) that you should always try to make things cross-platform and use built-in constants. But for cases where you can't, I usually do something like this.
In my main settings file, rather than
define('DOC_ROOT', $_SERVER['DOCUMENT_ROOT']);
I make it more complicated. I create a local_settings.php file that has this in it:
define('DOC_ROOT', $_SERVER['DOCUMENT_ROOT'] . /path/to/script/root/on/test/location/);
Then, in the main settings file, I have
if (file_exists(C:/script/root/local/local_settings.php))
{
include (C:/script/root/local/local_settings.php)
}
if (!defined(DOC_ROOT))
{
define('DOC_ROOT', $_SERVER['DOCUMENT_ROOT'] . /path/to/script/root/on/live/location/);
}
The main reason I do this is it lets me test things on one server before making them live. When I make them live, I should only need to upload the script and make sure that it doesn't find the local_settings.php file. That will pretty much never happen with a Windows path on a linux machine, and requires a bit more care if your test and live platforms are on similar machines (or even the same machine as is often the case). Still, I find it avoids a lot of stupid errors. If you're testing on different machines (and especially different OSes), you're likely to run into a few oddities when you go live, but this system lets you reduce them. If you use this system to run on the same server, but in a different directory, you can test pretty effectively and keep surprises to a minimum.
As a secondary consequence, though I don't generally use it this way, it makes it possible to build in server-specific constants as need be, so you could
define('LINE_BREAK', '\r\n'); // in local_settings.php on Win; '\r' on Mac
if (!defined(LINE_BREAK))
{
define('LINE_BREAK', '\n'); // on *nix
}
I can't say I've ever done that, but why not?
Great topic BTW.
Tom
*The header file looks for a "local settings" file that should include all customizations necessary to move the script from one server to another (at least of similar type). So it will declare a constant DOC_ROOT which may be the same as _SERVER['DOCUMENT_ROOT'] but may not if the script will run in a subdir.
I find I may not understand just what the header file is. I would have thought it was an included file, but if the header file sets the include path, I don't see how it itself can be included too. Could you elaborate on this just a bit, or point me toward an explanation somewhere?
Thanks,
Matthew
Okay, this is just the way I do things. I'm not sure I would say it's a "best practices" method, but is at least a "makes sense to me" method.
Generally (there are exceptions), I use mod_rewrite to rewrite URLs so that they all get redirected to /siteroot/index.php.
From there I grab $_SERVER['REQUEST_URI'] and figure what file the user really wants. This might just parse it out based on certain rules and grab a file, or it might query the database, or whatever is appropriate .
In that case, index.php always "includes" a file that has all the site settings and sets all the site-wide constants. If you do not have it set up that way, you can use auto_prepend_file directive in php_ini to always append a given file.
I like this system because (using the gateway script rather than the auto_prepend)
- it's easily portable to any AMP system with mod_rewrite.
- it allows easy customization to different servers and, once in place, I can simply upload the new files and not worry aoubt which server they're going to.
- my file structure does not have to correspond to my information structure (i.e. URLs are abstract pointers that are unrelated to the file system). That means that if I find I need to restructure the way things are physically stored on disk, it doesn't need to affect my URLs
- it lets me get lazy and not have to think about things like DB connections. Once the gateway script has run, that means that either things have failed horribly, or the DB connection has been established, a DB abstraction layer in place and DB class instantiated.
- it let's me set flags to debug. So if there is a problem with a DB query, I don't need to go into my DB class and do anything, I just set the DEBUG constant to TRUE and the relevant debugging error messages will be generated for each query.
Now, that doesn't really give you much detail on how to do it, though, does it?
The following article from Zend should get you started. It's a little different in approach, but it's along the same lines.
[zend.com...]
Then you combine that with using "clean" or "friendly" URLs
evolt article [evolt.org]
longer evolt article [evolt.org]
There are similar, perhaps better, articles on sitepoint.com and A List Apart and many other places.
Now you combine those and add in:
1. Have a config file with lots of constants that define your script defaults. Those should be the settings you want on your main server if the script is for you. If you'll be distributing it, you would want to have settings that would work for the largest number of people without customization.
2. In your config file you have something like this
if (file_exists('/absolute/path/to/my/local_settings.php'))
{
include('/absolute/path/to/my/local_settings.php');
}
This includes settings that will differ from those on your live server. In the case of a distributed script, you could have a "Windows" version that would be nothing other than a version for which this path correctly points to a file that adjusts for Windows.
If the file does not exist, the script will run with its default settings.
You should always use absolute paths here (not relative and not paths using system variables from the $_SERVER array) because you don't want this to accidentally find a file, only very purposefully. Since this include is only called in one place - from the top of your config.php - it's not a big trouble to get it right and do it manually for each installation.
If the local settings file is something just to be used for development, I include it in a directory named something like "dev" which does not exist on the live server. Even if I do accidentally upload it to the live server, since it is an absolute path, the live script still won't find it and everything runs as it should (er... at least it runs as it shouldn't for completely different reasons).
I started doing this because I typically develop and test on one machine, then upload and test on another, then finally go live. When I first started doing PHP scripts, I found I would get everything set, go live and have everything crash because I still had things set for the development server. This system takes a few minutes up front, but has pretty much gotten rid of the problem for me. The only exception, of course, is when the live servers have settings I don't expect (i.e. wrote a script for someone who had register_globals on, which I didn't check before uploading).
Hmmm.... in looking this post over, I'm guessing that you're more confused than ever. If I have time, I'll try to write out a more "tutorial" style explanation someday. In the meantime, ask away.