====== Ampache Coding Standards ====== ==== Abstract ==== The purpose of this document is to establish a standard for all PHP and HTML code written for Ampache. This document will outline stylistic standards for code such as indentation and spacing. It will also detail naming conventions for functions and files as well as the logical layout of files under the web space. It will also outline some basic coding practices, or standards that all new Ampache PHP code should meet. ===== Indentation and Line Length ===== Use an indent of a standard tab character. It is recommended that you break lines at approximately 75-85 characters. There is no standard rule for the best way to break a line, please use your best judgment. The goal is to make the PHP files easy to read by a third party. ===== Control Structures ===== These include if, for, while, switch, etc. Here is an example if statement, since it can be the most complicated: Control statements should have one space between the control keyword and opening parenthesis, to distinguish them from function calls. Always use curly braces even in situations where they are technically optional. Having them increases readability and decreases the likelihood of logic errors being introduced when new lines are added. Always add %%//%% end in order to increase readability on nested structures. The %%//%% end is not required on single-level control statements, but can still be helpful. **For switch statements:** ===== Function Calls ===== Functions should be called with no spaces between the function name, the opening parenthesis, and the first parameter; spaces between commas and each parameter; and no space between the last parameter, the closing parenthesis, and the semicolon. **Example:** As displayed above, there should be one space on either side of an equals sign used to assign the return value of a function to a variable. In the case of a block of related assignments, more tabbed space can be inserted to promote readability: ===== Variable and Function Definitions ===== ==== Classes ==== Classes should be given descriptive names. Avoid using abbreviations where possible. Class names should always begin with an uppercase letter. The class hierarchy is also reflected in the class name. Classes should be named using CamelCaps. Examples of good class names are: Log NetFinger HtmlUploadError ==== Functions and Methods ==== Functions and methods should be named with all lower case using underscores between words or levels of hierarchy. Functions should in addition have the package name as a prefix, to avoid name collisions between packages when applicable. **Example:** connect(); get_data(); build_some_widget(); xmlrpc_serialize_data(); xmlrpc_unserialize_data(); Private class members (meaning class members that are intended to be used only from within the same class in which they are declared; PHP does not yet support truly-enforceable private name spaces) should be preceded by a single underscore. For example: _sort(); _init_tree(); $this->_status; ===== Constants ===== Constants should always be all-uppercase, with underscores to separate words. Prefix constant names with the uppercased name of the class/package they are used in. For example, the constants used by the DB:: package all begin with DB_. The true, false and null constants are excepted from the all-uppercase rule, and must always be lowercase. ==== Global Variables ==== If your scripts need to define global variables, their names should start with a single underscore followed by the package name and another underscore. For example, the PEAR package uses a global variable called $_PEAR_destructor_object_list. ==== Variable Names ==== In the interest of future readability, code must always use full named variables unless a commonly known acronym can be used in its place. For example /* An Upload Directory */ $upload_directory = '/tmp'; /* XMLRPC result */ $xmlrpc_result = $xmlrpc->get_results(); ===== Code Comments ===== Complete inline documentation comment blocks (docblocks) must be provided. Non-documentation comments are strongly encouraged. A general rule of thumb is that if you look at a section of code and think "Wow, I don't want to try and describe that", you need to comment it before you forget how it works. C style comments (%%/* */%%) and standard C++ comments (%%//%%) are both fine. ===== Including Code ===== Anywhere you are unconditionally including a class file, use require_once '/path/to/file' . Anywhere you are conditionally including a class file (for example, factory methods), use include_once. Either of these will ensure that class files are included only once. They share the same file list, so you don't need to worry about mixing them - a file included with require_once will not be included again by include_once. Only libraries, classes, modules and templates should be included. All includes should whenever possible be placed at the top of the file before any functions, or operations are performed. For readability never include files that contain additional action conditions. **Example of Incorrect Including:** index.php << >> default_action.php << >> **Example of Correct Including:** index.php << >> default_action.php << >> ===== PHP Code Tags ===== Always use to delimit PHP code, not the shorthand. There are no exceptions to compliance of this rule. This is the only way to ensure the PHP code will work on differing operating systems and setups. ===== Naming Conventions ===== Follow the below naming conventions when creating new functions to make your code easy to read. When creating functions use the following prefixes: * print //echo info// * get //get info// * show //include html// * create //create object// * delete //delete object// * update //update object// * clean //verify, data integrity checks/modification// * check //error checking// * has //check if object has property// * exec //run a process// * format //Re-formats existing data// * put //Inserting data into lists,database,files,objects etc// **Examples:** /* Echoes information, doesn't return */ print_foo() /* Returns information */ get_foo() /* Gets information then includes an html template */ show_foo() /* Create new Object */ create_foo() /* Delete Object */ delete_foo() ===== File Formats ===== All scripts written must: * Be stored as ASCII text * Use UTF-8 character encoding There should be no line feeds after the closing PHP tag (?>) to prevent output before headers are sent. ===== File Organization ===== In order to maintain some semblance of organization on the web servers all files should be organized based on their function. refers to the top of the file path for the web server. All libraries should be located in /lib. Any additional modules that are suitable for separate distribution or not written in shop should be put in /modules/. All HTML templates should be put in /templates. All images for the site should be under /images. If a single subsection is going to contain a large number of images although not require it would most likely be a good idea to create a sub-directory under /images. **Example:** /* Contains Documents */ / /* Contains GPL/Changelog & Readme */ /docs /* Contains All library files */ /lib /* Contains All Class Files */ /lib/class /* Contains All Modules not written in house */ /modules/foomodule /* Contains All HTML templates */ /templates /* Contains documents that are not directly accessed, AJAX/XML-RPC and the like */ /server /* Contains All Images */ /images /* Contains All Images for Reservations */ /images/reservation /* Contains All commandline scripts */ /bin When naming files in order to make it easy for others to figure out their purpose they should follow the following naming conventions. All PHP files should end in a .php extension, all lower case. Library files should end in .lib.php. All PHP classes should be in their own distinct files which end in .class.php, except for abstract classes which end in .abstract.php. All HTML files which are conditionally included from other PHP scripts should end in .inc.php. The name of the file should represent what is contained within. Templates should match the function that calls them. See the examples below: /* Example document */ /user.php /* Example User { .... } Class File */ /lib/class/user.class.php /* Example Library File */ /lib/general.lib.php /* Example Template file for show_user() */ /templates/show_user.inc.php ===== User Input ===== Special attention must be paid to user input. The general assumption must be made that every attempt will be made by the user to maliciously disrupt the script, or cause data loss when submitted to a database. User Input includes information pulled from cookies which reside on the user's computer. All user input should be escaped and error checked before being used. ===== Database Connections and Calls ===== In order to not only ensure reliable database connections and data integrity but to account for future changes standardization of SQL statements and database calls is necessary. All SQL statements should use all capital letters for actual SQL commands and lowercase for field and table names when possible. Query statements should also always use the full name for fields for expandability. All user inputted values should be escaped and all field names should be escaped using the correct escape character **Example MySQL Query:** /* Simple Query */ SELECT * FROM `user`; /* Complex Query */ SELECT `user.id`,`preferences.value` FROM `user` LEFT JOIN `preferences` ON `user.id`=`preferences.user` WHERE `user.username` = 'dude' AND `user.pass` = PASSWORD('mypassword') ORDER BY `user.id` LIMIT 5,10; When creating a database connection complete error suppression and checking should be performed. Database connections should, when possible, be centrally managed by a single function or script to ensure easy troubleshooting. For performance reasons the mssql functions should be used in lieu of the odbc functions. If enabled all database connection failures should be logged. On production sites database connection failures should send notification e-mail. **Example MYSQL DB Connect Function:** function dbh($databasename='helpdesk') { /* If we don't have a db connection yet */ if (!is_resource(conf($databasename))) { $hostname = conf('database_hostname'); $username = conf('database_username'); $password = conf('database_password'); $dbh = @mysql_pconnect($hostname, $username, $password); $select_db = @mysql_select_db($databasename, $dbh); if (!is_resource($dbh) || !$select_db) { log_event(' error ',"Error" . mssql_error()); show_header(); echo "



"; echo Database Error:
"; echo "
Error:" . mysql_error() . "
"; show_footer(); exit(); } conf(array($databasename => $dbh),1); } // if no db connection else { $select_db = @mysql_select_db($databasename,conf($databasename)); if (!$select_db) { show_header(); echo "



"; echo "Error:
"; echo "
" . mysql_error() . "
"; show_footer(); exit(); } // if there is an error } // else return conf($databasename); } // dbh Once a database connection has been established it should always be referenced explicitly in order to avoid unexpected results, and account for multiple database handles in the future. When returning results all attempts should be made to minimize database access and memory usage. For example with returning multiple results if you only need an associative array use fetch_assoc rather than fetch_array. All SQL queries must be assigned to a variable, and should never be called directly from a mysql_query function for debugging and expandability. **Example MYSQL Query:** /** * Returns the User * * @package User */ function get_use() { /* Setup Query String */ $sql = "SELECT * FROM user WHERE (user.username = 'theman')" /* Run Query */ $db_results = mysql_query($sql, dbh()); /* Return Results as assoc array */ if ($results = mysql_fetch_assoc($db_results)) { return $results; } // end if /* Else Something bad happened */ return false; } // get_use ===== Version Compatibility ===== All PHP code written should work on any version and almost any configuration of PHP 5.x. If necessary version specific functions and syntax can be used, however it should be avoided whenever possible. All code should be written referencing the superglobals ($_POST, $_GET, and $_SERVER) as if 'register_globals' is off. ===== File and URL Paths ===== All file paths, and URLs should be fully qualified and yet relational. There should be a single point where the file prefix and URL prefix is defined for all scripts. All links and includes should be relational to the prefixes. $PHP_SELF should never be used. **Example:** /* Global Definitions of web path and prefix */ $_PATH_web = "http://example.com/pathtoprogram" $_PATH_file = "/pathtoweb/pathtoprogram" /* Generated URL using Web Path */ $link = $_PATH_web . "/index.php" /* Include Statement using Prefix */ include_once ($_PATH_file . "/templates/cool.inc.php”); ===== HTML Output ===== All HTML written for Ampache should conform to W3C's XHTML 1 Transitional standards. All user input that is to be echoed out must be sanitized before being displayed to the screen.