Using this Standard. If you want to make a local copy of this standard and use it as your own you are perfectly free to do so. That's why we made it!
|
All credits go to the original authors. A number of changes have been made to the version dated 2002-03-04, that was available at the time of writing at the URL provided: http://utvikler.start.no/code/php_coding_standard.html |
Before you start please verify that you have the most recent document. |
The use of the word "should" directs projects in tailoring a project-specific standard, in that the project must include, exclude, or tailor the requirement, as appropriate.
The use of the word "may" is similar to "should", in that it designates optional requirements.
In any case, once finalized hopefully people will play the adult and understand that this standard is reasonable, and has been found reasonable by many other programmers, and therefore is worthy of being followed even with personal reservations.
Failing willing cooperation it can be made a requirement that this standard must be followed to pass a code inspection.
Failing that the only solution is a massive tickling party on the offending party.
A name is the result of a long deep thought process about the ecology it lives in. Only a programmer who understands the system as a whole can create a name that "fits" with the system. If the name is appropriate everything fits together naturally, relationships are clear, meaning is derivable, and reasoning from common human expectations works as expected.
If you find all your names could be Thing and DoIt then you should probably revisit your design.
For example: RetryMax to mean the maximum number of retries, RetryCnt to mean the current retry count.
For example: IsHitRetryLimit.
Take for example NetworkABCKey. Notice how the C from ABC and K from key are confused. Some people don't mind this and others just hate it so you'll find different policies in different code so you never know what to call something.
class FluidOz // NOT FluidOZ class GetHtmlStatistic // NOT GetHTMLStatistic
class NameOneTwo class Name
class JjLinkList { }
class NameOneTwo { function DoIt() {}; function HandleError() {}; }
class NameOneTwo { function VarAbc() {}; function ErrorNumber() {}; var $mVarAbc; var $mErrorNumber; var $mrName; }
class NameOneTwo { function StartYourEngines(&$someEngine, &$anotherEngine) { $this->mSomeEngine = $someEngine; $this->mAnotherEngine = $anotherEngine; } var $mSomeEngine; var $mAnotherEngine; }
function HandleError($errorNumber) { $error = new OsError; $time_of_error = $error->GetTimeOfError(); $error_processor = $error->GetErrorProcessor(); }
Array element names follow the same rules as a variable.
$myarr['foo_bar'] = 'Hello'; print "$myarr[foo_bar] world"; // will output: Hello world $myarr['foo-bar'] = 'Hello'; print "$myarr[foo-bar] world"; // warning message
$myarr['foo_bar'] = 'Hello'; $element_name = 'foo_bar'; print "$myarr[foo_bar] world"; // will output: Hello world print "$myarr[$element_name] world"; // will output: Hello world print "$myarr['$element_name'] world"; // parse error print "$myarr["$element_name"] world"; // parse error
class Test { var $mrStatus; function DoSomething(&$rStatus) {}; function &rStatus() {}; }
global $gLog; global &$grLog;
define("A_GLOBAL_CONSTANT", "Hello world!");
function test()
{
static $msStatus = 0; }
function some_bloody_function() { }
if ($condition) while ($condition) { { ... ... } }
if ($condition) { while ($condition) { ... ... } }
There are more reasons than psychological for preferring the first style. If you use an editor (such as vi) that supports brace matching, the first is a much better style. Why? Let's say you have a large block of code and want to know where the block ends. You move to the first brace hit a key and the editor finds the matching brace. Example:
if ($very_long_condition && $second_very_long_condition) { ... } else if (...) { ... }To move from block to block you just need to use cursor down and your brace matching key. No need to move to the end of the line to match a brace then jerk back and forth.
function func() { if (something bad) { if (another thing bad) { while (more input) { } } } }
if (condition) { } while (condition) { } strcmp($s, $s1); return 1;
Create an Open() method for an object which completes construction. Open() should be called after object instantiation.
class Device { function Device() { /* initialize and other stuff */ } function Open() { return FAIL; } }; $dev = new Device; if (FAIL == $dev->Open()) exit(1);
if (condition) // Comment { } else if (condition) // Comment { } else // Comment { }If you have else if statements then it is usually a good idea to always have an else block for finding unhandled cases. Maybe put a log message in the else even if there is no corrective action taken.
if ( 6 == $errorNum ) ...
One reason is that if you leave out one of the = signs, the parser will find the error for you. A second reason is that it puts the value you are looking for right up front where you can find it instead of buried at the end of your expression. It takes a little time to get used to this format, but then it really gets useful.
switch (...) { case 1: ... // FALL THROUGH case 2: { $v = get_week_number(); ... } break; default: }
Continue and break like goto should be used sparingly as they are magic in code. With a simple spell the reader is beamed to god knows where for some usually undocumented reason.
The two main problems with continue are:
Consider the following example where both problems occur:
while (TRUE) { ... // A lot of code ... if (/* some condition */) { continue; } ... // A lot of code ... if ( $i++ > STOP_VALUE) break; }Note: "A lot of code" is necessary in order that the problem cannot be caught easily by the programmer.
From the above example, a further rule may be given: Mixing continue with break in the same loop is a sure way to disaster.
(condition) ? funct1() : func2(); or (condition) ? long statement : another long statement;
var $mDate var& $mrDate var& $mrName var $mName $mDate = 0; $mrDate = NULL; $mrName = 0; $mName = NULL;
while ($dest++ = $src++) ; // VOID
if (FAIL != f())is better than
if (f())even though FAIL may have the value 0 which PHP considers to be false. An explicit test will help you out later when somebody decides that a failure return should be -1 instead of 0. Explicit comparison should be used even if the comparison value will never change; e.g., if (!($bufsize % strlen($str))) should be written instead as if (0 == ($bufsize % strlen($str))) to reflect the numeric (not boolean) nature of the test. A frequent trouble spot is using strcmp to test for string equality, where the result should never ever be defaulted.
The non-zero test is often defaulted for predicates and other functions or expressions which meet the following restrictions:
Do not check a boolean value for equality with 1 (TRUE, YES, etc.); instead test for inequality with 0 (FALSE, NO, etc.). Most functions are guaranteed to return 0 if false, but only non-zero if true. Thus,
if (TRUE == func()) { ...must be written
if (FALSE != func()) { ...
while ($a != ($c = getchar())) { process the character }
The ++ and -- operators count as assignment statements. So, for many purposes, do functions with side effects. Using embedded assignment statements to improve run-time performance is also possible. However, one should consider the tradeoff between increased speed and decreased maintainability that results when embedded assignments are used in artificial places. For example,
$a = $b + $c; $d = $a + $r;should not be replaced by
$d = ($a = $b + $c) + $r;even though the latter may save one cycle. In the long run the time difference between the two will decrease as the optimizer gains maturity, while the difference in ease of maintenance will increase as the human memory of what's going on in the latter piece of code begins to fade.
Developing a common framework takes a lot of up front design effort. When this effort is not made, for whatever reasons, there are several techniques one can use to encourage reuse:
One reason for this is because people don't like making small libraries. There's something about small libraries that doesn't feel right. Get over it. The computer doesn't care how many libraries you have.
If you have code that can be reused and can't be placed in an existing library then make a new library. Libraries don't stay small for long if people are really thinking about reuse.
If you are afraid of having to update makefiles when libraries are recomposed or added then don't include libraries in your makefiles, include the idea of services. Base level makefiles define services that are each composed of a set of libraries. Higher level makefiles specify the services they want. When the libraries for a service change only the lower level makefiles will have to change.
In an ideal world a programmer could go to a web page, browse or search a list of packaged libraries, taking what they need. If you can set up such a system where programmers voluntarily maintain such a system, great. If you have a librarian in charge of detecting reusability, even better.
Another approach is to automatically generate a repository from the source code. This is done by using common class, method, library, and subsystem headers that can double as man pages and repository entries.
These headers are structured in such a way as they can be parsed and extracted. They are not useless like normal headers. So take time to fill them out. If you do it right once no more documentation may be necessary.
The documentation process begins with the most basic element of phpDocumentor: a Documentation block or DocBlock. A basic DocBlock looks like this:
/**
*
*/
A DocBlock is an extended C++-style PHP comment that begins with "/**" and has an "*" at the beginning of every line. NOTE: Any line within a DocBlock that doesn't begin with a * will be ignored. DocBlocks precede the element they are documenting. To document function "foo()", type:
/**
* Defies imagination, extends boundaries and saves the world ...all before breakfast!
*/
function foo()
{
}
define() statements, functions, classes, class methods, and class vars can all be documented.
A DocBlock contains three basic segments in this order:
The Short Description starts on the first line, and can be terminated with a blank line or a period. A period inside a word (like example.com or 0.1 %) is ignored. If the Short Description would become more than three lines long, only the first line is taken. The Long Description continues for as many lines as desired and may contain html markup for display formatting. Here is a sample DocBlock with a Short and a Long Description:
/**
* return the date of Easter
*
* Using the formula from "Formulas that are way too complicated for anyone to
* ever understand except for me" by Irwin Nerdy, this function calculates the
* date of Easter given a date in the Ancient Mayan Calendar, if you can also
* guess the birthday of the author.
*/
Tags are single words prefixed by a "@" symbol. Tags inform phpDocumentor how to present information and modify display of documentation. All tags are optional, but if you use a tag, they do have specific requirements to parse properly.
More tags may be added in the future, not all tags are implemented at this time in phpdocumentor, however they are all recognized as tags and will at least be displayed
/**
* The short description
*
* As many lines of extendend description as you want {@link element} links to an element
* {@link http://www.example.com Example hyperlink inline link} links to a website
* Below this goes the tags to further describe element you are documenting
*
* @param type $varname description
* @return type description
* @access public or private
* @author author name <author@email>
* @copyright name date
* @version version
* @see name of another element that can be documented, produces a link to it in the documentation
* @link a url
* @since a version or a date
* @deprecated description
* @deprec alias for deprecated
* @global type $globalvarname
or
* @global type description of global variable usage in a function
* @staticvar type description of static variable usage in a function
* @name procpagealias
or
* @name $globalvaralias
* @magic phpdoc.de compatibility
* @todo phpdoc.de compatibility
* @exception Javadoc-compatible, use as needed
* @throws Javadoc-compatible, use as needed
* @var type a data type for a class variable
* @package package name
* @subpackage sub package name, groupings inside of a project
*/
Tag Information:
type is the type of the global variable. $globalvarname is the EXACT name of the global variable as it is defined in the source code. Use this tag sytanx to document global variable definitions/** * @global string $GLOBALS['mystring'] */ $GLOBALS['mystring'] = "this is my string";
type is the type of the global variable. Use this tag syntax to document the declaration of global variables used in functions and declared with global keyword./** * @param string $param what is added to the global variable * @global string parameter is added to it */ function myfunction($param) { global $mystring; }
type is the type of the static variable. Use this tag syntax to document the declaration of static variables used in functions./** * @param string $param what is added to the global variable * @staticvar string parameter is added to it */ function myfunction($param) { static $mystring = 'hello'; }
$globalalias is the name you'd like displayed for documentation. This tag should be used to display a simpler version of a global variable. It will also make it more likely that the function version of @global will link to the global variable's documentation properly./** * now the @global declaration for function myfunction can find this variable * @global string $GLOBALS['mystring'] * @name $mystring */ $GLOBALS['mystring'] = "this is my string";
procpagealias is the name you'd like to use for a procedural page instead of its file.ext. Note that using this tag break auto-linking from include("file.ext") where the procedural page's filename is file.ext
type is the type of a class variable. @var grabs the first word only, the rest is shunted to description. Default type is "mixed" if not included. Use this tag to document variables with type restrictions.
type is the type of a value returned from a method or function.
description a brief description of the value returned
use this tag in a DocBlock preceding a function or method./** * return the day of the week * * @param string $month 3-letter Month abbreviation * @param integer $day day of the month * @param integer $year year * @return integer 0 = Sunday, value returned is from 0 (Sunday) to 6 (Saturday) */ function day_week($month, $day, $year) { ... }
type is the type of parameter$varname, which may be one of: (string, array, integer, double, class, mixed)the @param tag can refer to more parameters than the function lists, to allow documenting of functions that use func_get_args
$varname is the name of the parameter, and must be preceded by the $ punctuator
description a brief description of this parameter, which may be more than one line
element may be the name of any documented element (procedural page, constant, function, class, class method, class variable). phpDocumentor recognizes the element type based on the presence of punctuators:If elements are separated by commas, phpDocumentor will parse each element and return them as a list (time-saver). To resolve name conflicts resulting from class inheritance, use the :: scope operator classname::method() or classname::$variable.
- $element is a class variable in the current class - the class that contains the DocBlock
- element() is either a method in the current class or a function in the current file
- element is either the name of a constant or of a class
- file.ext is a procedural page's filename
/** * class 1 * * example of {@link http://phpdocu.sourceforge.net Inline linking to a website} * example of use of the :: scope operator * @see subclass::method() */ class main_class { /** * example of linking to same class, outputs "function main_class::parent_method() * @see function parent_method */ var $foo = 3; /** * example of comma-separated @see and of linking to a procedural page "funcs.php" * @see $foo, funcs.php */ var $bar = array('15'); /** * subclass inherits this method. * example of a word which is either a constant or class name, in this case a classname * @see subclass * @see subclass::$foo */ function parent_method() { if ($this->foo==9) die; } } /** * this class extends main_class * @see main_class */ subclass extends main_class { /** * bar. * example of same class lookup - see will look through parent hierarchy to find the method in {@link main_class} * the above inline link tag will parse as main_class * @see parent_method() */ var $foo = 9; }
@package is used to group documented elements together. If @package is not included, elements are grouped in the "default" package. The name of the default package can be changed using command-line switch -dn or --defaultpackagename. A package name must be one word containing only letters, numbers and [, ], - or _, allowing output of directories by package name to work. The parser will exit on an illegal package name.
There are two kinds of @package tags: class-level and page-level. For version 1.0, the parser uses certain rules to determine whether a @package tag applies to a procedural page or to a class.
@package should be used to help you logically group related elements. You write classes to group related functions and data together, and phpDocumentor represents the contents of files (functions, defines, and includes) as "Procedural Pages." A package is used to group classes and procedural pages together in the same manner that a directory groups related files together.
Page-level @package
Page-level @package must be in a page-level DocBlock. The first DocBlock in a file is a page-level DocBlock IF AND ONLY IF it is immediately followed by another DocBlock. Here is a page-level DocBlock followed by a class DocBlock:Note that the 2nd DocBlock may document any element, such as:/** * this page-level DocBlock is used to describe all of the functions, includes, and defines in this file * @package pagepackage */ /** * this class-level DocBlock is used to describe all of the methods and vars in this class */ class foo { ... }
Class-level @package/** * this page-level DocBlock is used to describe all of the functions, includes, and defines in this file * @package pagepackage */ /** * this DocBlock describes the include that follows */ include('file.ext');
Class-level @package must be in a DocBlock preceding a class definition.
@subpackage is used to group classes and functions into sub-sections of the main package, and has the same naming conventions as @package
- a @see-style element is described above, and may be the name of a class, function, variable, or constant
- any valid URL (uniform resource locator) will be output as this html: <a> tag as <a href="URL">optional description of URL<\a>
valid URLS include websites (http://www.example.com), ftp sites (ftp://ftp.example.com), telnet sites (telnet://example.com), etc./** * class 1 * * example of {@link http://phpdocu.sourceforge.net} * displays as "example of http://phpdocu.sourceforge.net" */ class main_class { /** * This function sends the user to {@link http://www.sf.net The SourceForge Website} * displays as "This function send the user to The SourceForge Website" */ function goto_sourceforge() { header("Location: http://www.sf.net"); } } /** * this class extends {@link main_class} * displays as "this class extends main_class" */ subclass extends main_class { /** * bar. * this class inherits {@link main_class::goto_sourceforge()} */ var $foo = 9; }
Every major reusable element in PHP can be documented:
Packages are used to help you logically group related elements. You write classes to group related functions and data together, and phpDocumentor represents the contents of files (functions, defines, and includes) as "Procedural Pages." A package is used to group classes and procedural pages together in the same manner that a directory groups related files together. A Package-level doc is an html file that documents general things about an entire package. It has the name "package.html" where package is the name of the package. In other words, for package foo that contains class bar in file /php/bar.php, phpDocumentor will attempt to document "foo.html" by looking for /php/foo.html. Package-level docs should be in the same directory as any file that contains a page-level or class-level @package tag
Package-level docs should be in standard html format. The only valid tags in Package-level documentation are inline tags. For this release, that means ONLY {@link} may be used in package-level documentation
Here is some example package-level documentation for package baz that contains classes fooclass and barclass
<html>
<head>
<title>Package baz</title>
</head>
<body>
This package is essential. using class {@link fooclass} you can instruct your computer to brush your teeth for you.
Combining this functionality with the back massage given by {@link barclass}, you may truly retire in comfort.
In particular, {@link barclass::footmassage()} is a most exquisite experience.
Please follow this list of links for more help:
<ul>
<li><a href="http://www.microsoft.com/support"><The reinstall Windows hotline>
<li><a href="http://www.php.net"><Heaven>
<li><a href="http://phpdocu.sourceforge.net"><The phpDocumentor Homepage>
<li><a href="http://www.chiaraquartet.net"><The most beautiful music you've ever heard>
</ul>
</body>
</html>
Procedural Pages are any physical files that phpDocumentor documents. They may contain any documentable element, or nothing at all, but should contain at least a page-level DocBlock to be of any use for the end-user. phpDocumentor documents all includes, defines, and functions found in the file, and links to documentation for classes defined in the file, but every class is documented separately from the physical file that contains it. In other words, if foo.php contains function bar() and class baz, bar will be documented as part of Procedural Page foo.php, and class baz will be documented as class baz. phpDocumentor interprets the first DocBlock in a file as a Procedural Page-Level DocBlock, or page-level DocBlock, if and ONLY if it does not precede any elements before the next DocBlock.
/**
* Page-level DocBlock
*
* This procedural page contains many functions that blah blah blah
*/
/**
* function or define DocBlock
*/
function blah()
{
...
}
The following is an example of a NON-page-level DocBlock
/**
* Almost a Page-level DocBlock
*
* This procedural page contains many functions that blah blah blah
*/
define("screwedup",66);
/**
* function or define DocBlock
*/
function blah()
{
...
}
phpDocumentor will interpret the first DocBlock as belonging to define("screwedup",66)
rather than to the page
Global Variables can only be documented by using the @global tag in a DocBlock preceding the global variable's definition. See the manual for @global for detailed information on how to use @global to document your project's global variables.
All other elements are documented by placing the DocBlock before the element. Any valid PHP Code may be placed between a DocBlock and an element as long as it isn't another element.
/**
* DocBlock will document function blah()
*/
// random comment
$a += strpos(get_another_thingy(66,$ufta));
$ark->build($a);
// etc. etc.
function blah()
{
...
}
The code below is a sample class showing phpDocumentor in action
phpDocumentor can parse any valid PHP code, but it is recommended to follow this style for ease of programming for both you and people who will be using your packages
/**
* A sample page-level DocBlock
* The package tag applies to all global variables, functions, defines and includes in this page
* @package pagetest
*/
/**
* A sample global variable
* @global array $GLOBALS['sample']
* @name $sample
*/
$GLOBALS['sample'] = array('1',2,"3","4" => 5);
/**
* A sample class
*
* This class is just random php used as a {@link http://phpdocu.sourceforge.net phpdoc} example.
* To see the $sample variable, you must use the command-line option --parseprivate or click the
* box in the web interface
*
* @version 1.0
* @author Joshua Eichorn <jeichorn@phpdoc.org>
* @package test
*/
class phptestclass
{
/**
* not parsed unless you use -pp on
* @access private
*/
var $privatevar;
/**
* A sample class variable
* @var string
*/
var $sample;
/**
* The class constructor
*
* This sets up the class and does other random stuff
*/
function phptestclass()
{
$this->sample = "test";
}
/**
* A test function
*
* This function returns {@link $sample}
*
* @see set(), $sample
* @global array used to get some sample stuff
* @return string
*/
function test()
{
global $sample;
return $sample == $this->sample;
}
/**
* Set the sample var
*
* @param string $var
* @see phptestclass::$sample, phptestclass::test()
*/
function set($var)
{
$this->sample = $var;
}
}
/**
* This function is in the page package, which is "pagetest" and not "ignored"
*
* this @package tag is ignored - only the page-level tag is parsed.
* @staticvar phptestclass an example of our static documentation
* @package ignored
*/
function inPagePackage()
{
static $mememe;
$mememe = new phptestclass;
}
// :TODO: tmh 960810: possible performance problem // We should really use a hash table here but for now we'll // use a linear search. // :KLUDGE: tmh 960810: possible unsafe type cast // We need a cast here to recover the derived type. It should // probably use a virtual method or template.
In practice the Open/Closed principle simply means making good use of our old friends abstraction and polymorphism. Abstraction to factor out common processes and ideas. Inheritance to create an interface that must be adhered to by derived classes.
if ($abool= $bbool) { ... }Does the programmer really mean assignment here? Often yes, but usually no. The solution is to just not do it, an inverse Nike philosophy. Instead use explicit tests and avoid assignment with an implicit test. The recommended form is to do the assignment before doing the test:
$abool= $bbool; if ($abool) { ... }
function example() { great looking code if (0) { lots of code } more code }
You can't use /**/ style comments because comments can't contain comments and surely a large block of your code will contain a comment, won't it?
class X { function GetAge() { return $this->mAge; } function SetAge($age) { $this->mAge = $age; } var $mAge; };Get/Set is ugly. Get and Set are strewn throughout the code cluttering it up.
But one benefit is when used with messages the set method can transparently transform from native machine representations to network byte order.
class X { function Age() { return $this->mAge; } function Name() { return $this->mName; } var $mAge; var $mName; } $x = new X; // Example 1 $age = $x->Age(); $r_age = &$x->Age(); // Reference // Example 2 $name = $x->Name(); $r_name = &$x->Name(); // ReferenceAttributes as Objects is clean from a name perspective. When possible use this approach to attribute access.
A layering violation simply means we have dependency between layers that is not controlled by a well defined interface. When one of the layers changes code could break. We don't want code to break so we want layers to work only with other adjacent layers.
Sometimes we need to jump layers for performance reasons. This is fine, but we should know we are doing it and document appropriately.
My god he's questioning code reviews, he's not an engineer!
Not really, it's the form of code reviews and how they fit into normally late chaotic projects is what is being questioned.
First, code reviews are way too late to do much of anything useful. What needs reviewing are requirements and design. This is where you will get more bang for the buck.
Get all relevant people in a room. Lock them in. Go over the class design and requirements until the former is good and the latter is being met. Having all the relevant people in the room makes this process a deep fruitful one as questions can be immediately answered and issues immediately explored. Usually only a couple of such meetings are necessary.
If the above process is done well coding will take care of itself. If you find problems in the code review the best you can usually do is a rewrite after someone has sunk a ton of time and effort into making the code "work."
You will still want to do a code review, just do it offline. Have a couple people you trust read the code in question and simply make comments to the programmer. Then the programmer and reviewers can discuss issues and work them out. Email and quick pointed discussions work well. This approach meets the goals and doesn't take the time of 6 people to do it.
Some issues to keep in mind:
Make a web page or document or whatever. New programmers shouldn't have to go around begging for build secrets from the old timers.
Programmers generally resist bug tracking, yet when used correctly it can really help a project:
Source code control should be linked to the bug tracking system. During the part of a project where source is frozen before a release only checkins accompanied by a valid bug ID should be accepted. And when code is changed to fix a bug the bug ID should be included in the checkin comments.
Face it, if you don't own a piece of code you can't possibly be in a position to change it. There's too much context. Assumptions seemingly reasonable to you may be totally wrong. If you need a change simply ask the responsible person to change it. Or ask them if it is OK to make such-n-such a change. If they say OK then go ahead, otherwise holster your editor.
Every rule has exceptions. If it's 3 in the morning and you need to make a change to make a deliverable then you have to do it. If someone is on vacation and no one has been assigned their module then you have to do it. If you make changes in other people's code try and use the same style they have adopted.
Programmers need to mark with comments code that is particularly sensitive to change. If code in one area requires changes to code in an another area then say so. If changing data formats will cause conflicts with persistent stores or remote message sending then say so. If you are trying to minimize memory usage or achieve some other end then say so. Not everyone is as brilliant as you.
The worst sin is to flit through the system changing bits of code to match your coding style. If someone isn't coding to the standards then ask them or ask your manager to ask them to code to the standards. Use common courtesy.
Code with common responsibility should be treated with care. Resist making radical changes as the conflicts will be hard to resolve. Put comments in the file on how the file should be extended so everyone will follow the same rules. Try and use a common structure in all common files so people don't have to guess on where to find things and how to make changes. Checkin changes as soon as possible so conflicts don't build up.
As an aside, module responsibilities must also be assigned for bug tracking purposes.
<?php print "Hello world"; ?> // Will print "Hello world" <? print "Hello world"; ?> // Will print "Hello world" <script language="php"> print "Hello world"; </script> // Will print "Hello world" <% print "Hello world"; %> // Will print "Hello world" <?=$street?> // Will print the value of the variable $street
if (22 == $foo) { start_thermo_nuclear_war(); } else if (19 == $foo) { refund_lotso_money(); } else if (16 == $foo) { infinite_loop(); } else { cry_cause_im_lost(); }In the above example what do 22 and 19 mean? If there was a number change or the numbers were just plain wrong how would you know?
Heavy use of magic numbers marks a programmer as an amateur more than anything else. Such a programmer has never worked in a team environment or has had to maintain code or they would never do such a thing.
Instead of magic numbers use a real name that means something. You should use define(). For example:
define("PRESIDENT_WENT_CRAZY", "22"); define("WE_GOOFED", "19"); define("THEY_DIDNT_PAY", "16"); if (PRESIDENT_WENT_CRAZY == $foo) { start_thermo_nuclear_war(); } else if (WE_GOOFED == $foo) { refund_lotso_money(); } else if (THEY_DIDNT_PAY == $foo) { infinite_loop(); } else { happy_days_i_know_why_im_here(); }Now isn't that better?
The two extremes are thin classes versus thick classes. Thin classes are minimalist classes. Thin classes have as few methods as possible. The expectation is users will derive their own class from the thin class adding any needed methods.
While thin classes may seem "clean" they really aren't. You can't do much with a thin class. Its main purpose is setting up a type. Since thin classes have so little functionality many programmers in a project will create derived classes with everyone adding basically the same methods. This leads to code duplication and maintenance problems which is part of the reason we use objects in the first place. The obvious solution is to push methods up to the base class. Push enough methods up to the base class and you get thick classes.
Thick classes have a lot of methods. If you can think of it a thick class will have it. Why is this a problem? It may not be. If the methods are directly related to the class then there's no real problem with the class containing them. The problem is people get lazy and start adding methods to a class that are related to the class in some willow wispy way, but would be better factored out into another class. Judgment comes into play again.
Thick classes have other problems. As classes get larger they may become harder to understand. They also become harder to debug as interactions become less predictable. And when a method is changed that you don't use or care about your code will still have to be retested, and rereleased.
© Copyright 1995-2002. Todd Hoff and Fredrik Kristiansen. All rights reserved.
© Copyright 2003. Carl Swart. All rights reserved.