Perl coding standards

General Principles

Foswiki is a large project which, over the years, has had a lot of contributors. It is also mainly written in perl, which has a reputation for not being the most readable language. It is therefore critical that contributors write code that is clear, concise, well structured and well documented. Contributors are strongly recommended to discover and use the principles of literate programming i.e. to write code that is self-explanatory, by using good variable names, common programming idioms, and well-known patterns, and to avoid obscure idioms. If you have to use obfuscated perl (and we all do, on occasion), then make sure it's clearly commented.

Foswiki started out life as a suite of disconnected perl CGI scripts, and that legacy is still present in the codebase today. Over the years there has been a steady drive to move towards a more object-oriented style of perl, implementing a MVC architecture. Contributors are asked to constantly bear this in mind when adding new code.

If you find yourself adding code that duplicates something that is already there, then don't be afraid to refactor the existing code to improve sharing. Refactoring is an essential part of continuous improvement. However please make sure you have a clear, well documented, reason for refactoring, and make doubly sure that you don't compromise any published APIs when you do so.

If you are unsure if your approach is good, then there are plenty of people ready to critique on IRC. There are also tools (described below) that can help you develop a good perl style.

System Requirements

Core code should be based on the SystemRequirements.

Avoid adding dependencies on external Perl modules not shipped with standard Perl. This is because in some intranet environments, Internet access is difficult or non-existent, making it hard to install add-on modules directly from CPAN. Also, the installer is often not the server administrator, requiring someone else to perform the module installation (or a more complex CPAN configuration). You can check which modules are available in which perl releases by looking at the perl module on CPAN.

If you find you do have to add a dependency from core code on an external perl module, make sure it is discussed to death first.

Foswiki Coding Conventions

Please follow these coding conventions when contributing to core code. This is to make source code more consistent and readable. Please provide your feedback in CodingStandardsDiscussions.

Note: the general rule is that readable code is more important than slavish adherence to coding standards. The format of the POD headers for exported functions is important, though, as they are automatically extracted to form the source code documentation in the release.

  • use strict
  • use warnings
  • use Assert
    • This defines ASSERT, a function that causes a die if a condition is false. ASSERT is used as follows: ASSERT( $condition ) if DEBUG; This is conditionally compiled i.e. is enabled only during development.
  • Function names:
    • Use bumpy words that start with lower case, i.e. sillyOperation
    • Prepend private function names with _, i.e. _secretOperation
  • Function documentation:
    • Use the documentation templates below for header documentation for functions.
  • Variable names:
    • Use bumpy words that start with lower case, i.e. $sillyVariable
    • Always initialize, even if it's to undef.
    • Avoid global variables like the plague, to avoid problems with mod_perl
  • Subclassing:
    • Define the @ISA variable using the our keyword and set it — for example, our @ISA = 'Parent::Class';
  • White space:
    • Follow the style of the current Perl scripts for indentation (four characters per level of indent), spacing around brackets and placement of braces (e.g. of '{}')
    • Use only spaces for indent, don't use tabs. Mixing tabs and spaces often causes problems, especially with patches. To simplify doing this with specific editors, see VimEditor, EmacsCPerlMode...
  • Comments:
    • Use comments to describe what you are doing where appropriate
    • Special comments: In case some parts of your code need later attention, state that in your comments as:
      • # SMELL: for unclean code that looks like a bug, or violates common coding practices, or misses some corner case
      • Open a Task for the # SMELL: and reference the task item in the Smell so that it doesn't get overlooked. If it is critical for correct operation, be sure to mark the task urgent and set the correct release so that it blocks the release.

Example package header

=pod

---+ package Namespace::Vector
Objects of this type represent 3D vectors

SMELL: quaternions are more flexible

=cut

package Namespace::Vector;

Example Class Method

A class method is an OO static method that is invoked using indirection off the package object e.g. Foswiki::Vector->new() or new Namespace::Vector()

=pod

---++ ClassMethod new( $x, $y, $z )
Constructor for a new vector object
   * =$x= - X component
   * =$y= - Y component
   * =$z= - Z component

=cut

sub new {
    my( $class, $x, $y, $z ) = @_;
    my $this = bless( {}, $class );
    $this->{x} = $x; $this->{y} = $y; $this->{z} = $z;
    return $this;
}

Note how $class is not listed in the parameter documentation.

Example Object Method

An object method is a OO method that is invoked using indirection off a blessed object of the package type e.g. my $c = new Namespace::Coord(1,2,3); $c->normalise(); or Namespace::Coord::normalise( $c )

=pod

---++ ObjectMethod normalise() -> $length
Normalise the vector to have length 1 i.e. become a direction vector.
Return the magnitude of the original vector.

=cut

sub normalise {
    my( $this ) = @_;
    ASSERT( $this->isa("Namespace::Vector") ); # it is always best to make sure....
    my $mag = sqrt( $this->{x} * $this->{x} + $this->{y} * $this->{y} + $this->{z} * $this->{z} );
    $this->{x} /= $mag; $this->{y} /= $mag; $this->{z} /= $mag;
    return $mag;
}

Note how $this is not listed in the parameter documentation.

Example exported static method

Header for a static method intended to be visible outside the package. A static method is any non-OO method.
=pod

---++ StaticMethod dot( $vectorObject, $vectorObject ) -> $number
Return the dot-product of two vectors.

=cut

sub dot {
   my ( $a, $b ) = @_;

   ASSERT( $a->isa( "Foswiki::Vector" )) if DEBUG;
   ASSERT( $b->isa( "Foswiki::Vector" )) if DEBUG;

   return $a->{x} * $b->{x} + $a->{y} * $b->{y} + $a->{z} * $b->{z};
}

Private methods

headers for private methods (private class methods, static methods and object methods) should follow the same pattern as exported methods except that they should be documented using # comments rather than POD. For example,

# ---++ ObjectMethod _crush( \@x ) -> $boolean
# Crush the vector with an array.
# Note use of \@ to indicate an array reference
# Note also use of $boolean to indicate the result type.
# Remember that undef, "" and 0 are all FALSE in perl, and any non-zero number or non-empty string is TRUE.
sub _crush {
   my( $this, $x ) = @_;
   ASSERT( ref($this) eq "Namespace::Vector" );
...

Internationalisation support

See: InternationalisationGuidelines

You don't need to know anything about internationalisation (I18N) to make your code work with international characters in WikiWords and much more, and it also makes your code more readable. The guidelines cover both core code and plugins.

Tool Support

Options to ensure consistent formatting:

  • Perl
    • use PerlTidy ( perltidy -b filename.pm will modify the file inplace using the default settings)
      • If you want to use perltidy on a file, be sure to perform a distinct checkin containing only its reformatting changes
    • use PerlCritic to learn how to avoid common coding issues
      • As a general rule, it is helpful to run perlcritic --brutal or even perlcritic --cruel for educational purposes; and also to ensure you're not unnecessarily adding new criticisms to the code at these levels (even if they aren't shown by default perlcritic settings).
    • EmacsCPerlMode

Gotchas and Wotchers

Perl is a vast and complex language, and supports some hairy programming constructs. Some lead you by the hand into common errors, so here are some common gotchas and things to watch out for:
  • If you need to be sure that a value is defined (e.g. you are going to use it in a condition) then test it with defined $blah, don't use $blah || '' to assign it a known value. This is because zero (0) tests as false, so will end up being overwritten if the || syntax is used.
  • Validate all URL parameters, to avoid any risk of code injection. URL parameters need to be checked to ensure their values are legal, and cannot carry a code payload that could compromise the server. Avoid blind untainting $value =~ /^(.*)$/; $value = $1 if possible, and comment it to death if you do use it to explain why you think it's safe.
  • Use use Module () in preference to use Module in the head of a module. By forcing all calls to refer explicitly to the defining module, you avoid polluting namespaces and minimise the risk of nasty accidents.
  • Avoid obscure, obfuscated perl, regardless of how clever it makes you feel. If you think you can save a few picoseconds by coding something in a really clever, but somewhat obscure, way, then don't. Remember the poor schmuck who has to read and understand your code. If you find a case where some horrible perl construct is required, then document the life out of it.

CSS coding standards

See: CssCodingStandards?

Javascript coding standards

See: JavascriptCodingStandards

Topic revision: r11 - 09 Nov 2010 - 08:01:37 - CrawfordCurrie
 
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. see CopyrightStatement. Creative Commons LicenseGet Foswiki at sourceforge.net. Fast, secure and Free Open Source software downloads