Skip to content
Aaron Narwhaal edited this page Oct 18, 2016 · 8 revisions

This document contains the coding standards for Gazelle. This document is a work-in-progress and is subject to change.

NOTE: The standards defined in this document will likely differ from what is actually seen in the Gazelle code. This document is the first step in properly enforcing coding standards throughout the project.

These coding standards apply to legacy (PHP) Gazelle. For the new project, see https://github.com/meteor-gazelle/.

Table of Contents

  1. FILE FORMATTING
  2. CODE STYLING
    1. Code styling for PHP and JavaScript
    2. Code styling for CSS
    3. Code styling for SQL
  3. NAMING CONVENTIONS
    1. Function names
    2. Variable names
    3. Class names
    4. SQL names
    5. Miscellaneous names
  4. COMMENTS
  5. USER INTERFACE
  6. MODEL VIEW DESIGN
  7. EXAMPLES
    1. PHP examples
    2. CSS examples
    3. SQL examples

This document contains the coding standards for Gazelle. This document is a work-in-progress and is subject to change.

NOTE: The standards defined in this document will likely differ from what is actually seen in the Gazelle code. This document is the first step in properly enforcing coding standards throughout the project.

FILE FORMATTING

Tabs shall always be used for indentation.

Files shall be encoded in ASCII.

Files shall always use Unix-style line endings (LF). The program dos2unix will take care of this for you if you have files that use Windows-style line endings (CR+LF) or old Mac-style line endings (CR).

File names for PHP, CSS, and JavaScript files shall be all lowercase and use underscores instead of spaces.

CODE STYLING

Code styling for PHP and JavaScript

All statement blocks, including functions, shall have the opening brace at the end of the same line with a space before the brace. The astute reader will note that this is K&R style with the exception of functions.

There shall be a space between a control structure statement (e.g. if, elseif, for) and the following parenthesis.

There shall be a space around conditional operators.

When using ternary operators, spaces shall be used around the operators.

For conditional blocks, elseif is to be used instead of else if.

In loops and conditional blocks, there shall be braces even if there is only one statement.

In loops and conditional blocks, the statement(s) shall be placed on the following lines.

When opening a PHP statement, <? shall be used instead of <?php.

Switch cases in index files shall not contain substantial code. The use of include statements is acceptable.

When building strings in PHP, single quotes shall be used when not printing a variable.

When declaring JavaScript variables, var shall always be used.

Code styling for CSS

property: value; pairs shall be separated by a space, and the value shall be followed by a semi-colon.

Multiple, related CSS selectors with the same declarations shall appear on multiple lines to improve readability.

The opening brace shall be on the same line as the last related selector with a space between the selector and the brace.

Code styling for SQL

Long SQL queries shall be separated on multiple lines.

Short SQL queries may be on a single line.

The primary SQL keywords should be at the same indentation level unless part of a JOIN or other complex statement.

Use indents as appropriate to aid readability.

The SQL keywords JOIN, RIGHT JOIN, LEFT JOIN must be indented once from the SELECT statement.

The SQL keyword AND must be indented once from the WHILE (and similar) statements.

The "not equal to" operator != must be used instead of the alternative operator <>.

NAMING CONVENTIONS

Function, variable, and class names shall always be descriptive.

Function names

PHP function names shall be written in lowercase_with_underscores.

JavaScript function names shall be written in camelCase with a leading lowercase letter.

Variable names

PHP variable names shall be written in CamelCase with a leading uppercase letter.

JavaScript global-scope variables shall be written in camelCase with a leading lowercase letter.

JavaScript local-scope variables shall be written in lowercase_with_underscores.

Class names

PHP class names shall be written in CamelCase with a leading uppercase letter.

PHP class constants shall be written in CamelCase with a leading uppercase letter.

SQL names

All SQL keywords shall be written in all UPPERCASE.

All SQL table names shall be written in lowercase_with_underscores.

All SQL column names shall be written in CamelCase with a leading uppercase letter.

All automatically-incremented ID columns shall be named ID, while the other columns for Identification numbers shall have names like RequestID, TorrentID, etc.

Miscellaneous names

PHP global constants shall be written in ALL_CAPS.

PHP constants true, false, and null shall be written in all lowercase.

COMMENTS

Use C89-style /* ... */ comments for multi-line comments.

Use C99-style // ... comments for single-line comments.

USER INTERFACE

All button labels shall use sentence case.

All table headings shall use sentence case.

All text-based buttons shall use the brackets CSS class.

Use common sense for design-related code. Microsoft's UI design guidelines explain when certain form input controls should be used over others to provide a familiar and intuitive interface. Refer to the following links for the most likely issues to encounter in web design:

MODEL VIEW DESIGN

All new additions to Gazelle should follow this Model + View design pattern. Current code in Gazelle will be refactored to coincide with this pattern.

For this explanation we shall use labels' code as an example. Complex pages should be split up into 3 files, the model (classes/labels.class.php) this handles all the data fetching and manipulations via function calls dedicated to each portion. The view (classes/labelsview.class.php) this class only contains static functions whose sole purpose is to render received data as html. Each function that renders html has a name that begins with "render_". Try to limit uses of extra DB queries within these functions and instead rely on the data passed within arguments to contain all the information you need. The last file is sections/labels.php, this ties everything together. It instantiates the model, creates a skeleton html layout, and calls the necessary functions in the view to render data.

For less complex pages, such as edit and take_edit, the MV design is not necessary as long as the DB/Cache and data logic is separated from the html. DO NOT mix queries with html; by the time you begin rendering html all your data should already be prepared. For the take_* files, if you find that code is being reused, then make it a static function in the model.

Our goal is to have simple, reusable, and easily extendable code.

The following 3 code samples demonstrate what the Model View design should look like.

class Label {
    private $LabelID;
    private $LabelInfo;

    function __construct($LabelID, $Load = true) {
        $this->LabelID = $LabelID;
        if ($Load) {
            $this->get_info();
            $this->get_torrent_list();
        }
    }

    public function get_info() {
        if (!isset($LabelInfo)) {
            G::$DB->query("SELECT
                            l.ID, l.Name, w.Body, w.Image, l.ParentID
                        FROM labels AS l
                        LEFT JOIN wiki_labels AS w ON l.ID = w.ID
                        WHERE l.ID = '$this->LabelID'");
            if (G::$DB->record_count() == 0) {
                error(404);
            }
            $this->LabelInfo = G::$DB->next_record(MYSQLI_ASSOC);
            $this->ParentLabelID = $this->LabelInfo['ParentID'];
            $this->load_child_labels();
        }
        return $this->LabelInfo;
    }
}

<?

class LabelView {
	public static function render_title($LabelName) { ?>
		<h2>
			<?=display_str($LabelName)?>
		</h2>
<?	}

	public static function render_linkbox($LabelID) {?>
        <div class="linkbox">
<?			View::render_bookmark_link('label', $LabelID); ?>
            <a href="labels.php?action=edit&id=<?=$LabelID?>">[Edit]</a>
        </div>
<?	}

	public static function render_image($Image, $Name) {
		if ($Image) { ?>
			<div class="box box_image">
				<div class="head"><strong><?=$Name?></strong></div>
				<div style="text-align: center; padding: 10px 0px;">
					<img style="max-width: 220px;" src="<?=ImageTools::process($Image, true)?>" alt="<?=$Name?>" onclick="lightbox.init('<?=ImageTools::process($Image)?>', 220);" />
				</div>
			</div>
<?		}
	}

	public static function render_stats($Stats) { ?>
		<div class="box box_info box_statistics_label">
		    <div class="head">
		        <strong>Statistics</strong>
		    </div>
		    <ul class="stats nobullet">
		        <li>Artists: <?=$Stats['Artists']?></li>
		        <li>Groups: <?=$Stats['Groups']?></li>
		        <li>Torrents: <?=$Stats['Torrents']?></li>
		    </ul>
		</div>
<?	}

}

$LabelID = (int) $_GET['id'];
if (!is_number($LabelID)) {
	error(404);
}

$Label = new Label($LabelID);
View::show_header($Label->get_info()['Name'], 'browse,musicbrainz');
?>
<div class="thin">
    <div class="header">
<?
        LabelView::render_title($Label->get_info()['Name']);
        LabelView::render_linkbox($LabelID);
?>
    </div>
    <div class="sidebar">
<?
        LabelView::render_image($Label->get_info()['Image'], $Label->get_info()['Name']);
        LabelView::render_label_tree($LabelID, $Label->get_info()['Name'], $Label->get_parent_label_id(), $Label->get_parent_label_name(), $Label->get_child_labels());
        LabelView::render_add_child($LabelID);
        LabelView::render_stats($Label->get_stats());
?>
    </div>
    <div class="main_column">
<?
        TorrentsView::render_torrent_list($Label->get_torrent_list());
        View::render_info('Label', $Label->get_info()['Body']);
?>
    </div>
</div>
<?
View::show_footer();
?>

EXAMPLES

PHP examples

if ($Foo > 0) {
	$SomeString = "this is a string $DiffString with more text";
} elseif ($Foo == 0) {
	// other things happen
} else {
	// more magic
}

function works_magic($Foo, $Bar) {
	return $Foo;
}

echo ($Foo == true ? 'foo is true' : 'foo is false');

if ($Foo == true) {
	// magic happens
}

/*
 * This is a good, multi-line comment.
 *
 * Please comment your code!
 */

// This is a good, single-line comment.

CSS examples

<a href="foobar.php" style="font-weight: bold;">link text</a>

.spellcheck {
	margin: 10px 0;
	font-size: 1.25em;
	font-weight: bold;
}

.linkbox .brackets:before,
.linkbox .brackets:after,
.top10_quantity_links .brackets:before,
.top10_quantity_links .brackets:after {
	color: #757575;
}

SQL examples

SELECT
	r.ID, e.EditionID, r.Title, r.Year, r.CatalogueNumber,
	l.Name AS Label, r.LabelID, r.Image, r.MusicBrainzID
FROM releases AS r
	JOIN editions AS e ON e.ReleaseID = r.ID
	LEFT JOIN labels AS l ON l.ID = r.LabelID
WHERE r.ID IN ($ReleaseIDsSQL)
ORDER BY e.EditionID, r.Year ASC


SELECT SQL_CALC_FOUND_ROWS c.ID, c.Subject, cu.Unread, cu.Sticky,
	cu.ForwardedTo, cu2.UserID, cu.ReceivedDate AS Date
FROM pm_conversations AS c
	LEFT JOIN pm_conversations_users AS cu ON cu.ConvID = c.ID
		AND cu.UserID = '1'
	LEFT JOIN pm_conversations_users AS cu2 ON cu2.ConvID = c.ID
		AND cu2.UserID != '1'
		AND cu2.ForwardedTo = 0
	LEFT JOIN users_main AS um ON um.ID = cu2.UserID
WHERE um.Username LIKE 'test'
	AND cu.InInbox = '1'
GROUP BY c.ID
ORDER BY cu.Sticky, Date DESC
LIMIT 25


SELECT RequestID AS ID, UserID FROM bookmarks_requests

EOF