Code Book, part 4 (Coding Standards)

Written by Chris Graham
« Return to Code Book table of contents


Introduction

This document documents many coding standards. Agencies may wish to integrate some of this into their own assessment spreadsheet for projects.

Ideally you should try and learn to apply all these standards naturally, so going through this as a checklist will be a quick final formality when you're finished coding.

You may also wish to read the "Engineering standards and trade-off" section of Code Book, part 3 (Miscellany) and the descriptions of design concerns across the main composr.app website (particularly the Composr Manifesto).

What standards to apply

If you are writing code that you think should be included in future Composr versions, you must meet the full standards.

For client work you can afford to meet only a subset of standards, as you control the environment much more, and a limited set of programmers will need to work with the code. Team members can agree on the standards used on a per-project basis.

We want to maintain high standards, but we also don't want to weigh you down.

The categories:
  • "All development" should be done by any developer using Composr
  • "Composr development +" means it's needed for Composr code (i.e. code for redistribution to end users), but also very advisable to do in general (it's best to try and learn to do it by habit if it's something that doesn't make things slower to do).
  • "Product only" only usually needs considering for bundled Composr code, although you may want to consider it anyway.
  • "Projects" applies only to client projects, not for the normal coding of bundled Composr code.

Automatic application

Any standard not marked in red has some kind of automatic way of being checked, assuming all the recommended tools are used.

Development mode

The mentioned Composr development mode runs automatically if you are working out of a Git repository.

Code Quality Checker

The Code Quality Checker (described in the Code Book, and its readme.txt file) has various options, including pedantic mode. When we refer to pedantic mode here we might actually mean one of the other non-default options: check to see what's available.

Automated tests

Automated tests are implemented as "unit tests" in the testing_platform addon. Technically they are not actually unit tests, they are integration tests, but we aren't pedantic about that.

Almost any rule has exceptions, and thus many of the tests define lists of exceptions internally, which you are invited to amend as required. Use your common sense and good judgement.

Many automated tests support a debug=1 parameter to output additional debug information for why things are failing.

You are invited to improve automated test code as you see fit.

Code formatting within editors

To automatically format code for PSR-12 you can either use the "PHP CodeSniffer" Open Source project, or PhpStorm.

The optimum PHP CodeSniffer command is:

Code (Bash)

phpcbf --parallel=1024 .
 

This is running PHP CodeSniffer in beautification mode and uses the bundled .phpcs.xml file, which defines our standards (this file is also used by our CQC integration of PHP CodeSniffer).

Don't reformat other people's code automatically without checking the changes, as you may cause problems with comment or line alignment.

It is a good idea to use an editor/IDE with support for the .editorconfig standard. Most editors/IDEs have a plugin for it. .editorconfig will help you automatically set correct tab settings (among other things) as you move between different projects.

Standards

General programming

Good programming practices

Standard Reason Scope Test method Notes
1)
DRY (Don't Repeat Yourself)
Technical debt All development Manually Don't copy & paste big chunks of code (exceptions: see below), or the same snippet of code time-and-time-again. If you spot a reusable chunk of code, consider making both usages work via a new shared function. Sometimes if you see the same pattern of code time-and-time-again it makes sense to abstract that too.
2)
Only ever copy & paste if you need to, and use care
Technical debt All development Manually If you do copy & paste a chunk of code, e.g. if implementing a new importer and using a pre-existing one as a base – then make sure you at least adjust your copied code to make sense. Don't leave comments in there that refer to the old context (e.g. refer to the wrong importer in the pasted code).
3)
Know what you are doing in all your code
Technical debt All development Manually Understand all the code you write. Don't just copy some existing pattern into a new context.
4)
Write well structured code
Technical debt All development Code Quality Checker can check function length (pedantic mode) Divide into functions into appropriate units (sensible modules interacting over reasonable interfaces). Where possible create functions and classes that are isolated from specific screens, as these can then have automated tests written for them. Try and make your code flow from top to bottom roughly represent execution order.
5)
Keep your code tidy
Technical debt All development Manually Just generally try and keep your code neat. Don't misspell in comments and variable names. Tidy code encourages a mindset of zero tolerance for messiness in other areas, and increases general team morale such as to keep a high level of standards.
6)
Separate code chunks with blank lines
Technical debt Product development only Manually A chunk of code is usually a few lines long and does a common purpose. Usually the chunk will start with a short comment, and end with a single blank line or a function's closing brace. Make your blank lines mean something (i.e. the end of a code chunk), don't just add them to space things out randomly.
7)
Keep track of things you intend to fix later
Technical debt: stops things being forgotten All development Manually If you have to do some code that really should be cleaner, put a comment by it starting FUDGE. If you haven't finished something put a comment starting TODO (with optional reference to a tracker ID which will suppress it reporting as a test failure), so it is easy for someone to later pick up if you forgot to finish it. If you have an idea put IDEA (with optional reference to a tracker ID).
We do not use HACKHACK or FIXME markers, although the CQC will pick up on these.
8)
Any legacy code must be marked as such
Technical debt Product development only Manually Legacy code, due to supporting data from old Composr versions, must be marked with a LEGACY comment. This will allow us to clean it out in future versions, should we want to break old compatibility.
9)
Don't write cryptic code
Technical debt All development Manually Don't write code that is too "clever" or relies on too deep an understanding, unless there's good reason to. For example, don't assume the programmer knows the precedence order of some of the less common operators so put in explicit parentheses to make things clear.
10)
Use software engineering best practices
Technical debt Composr development + Manually Use software engineering best practices (while also keeping things consistent with how Composr is designed, sometimes unconventional decisions may have been made for a reason).
11)
Use meaningful function / class / variable / file / identifier names
Technical debt Composr development + Manually Use sensible function / class / variable / file / identifier / token names that make full intuitive sense in whatever context they are used in; this is particularly important for global functions (you should have an idea what specifically the function is for without having to look it up).
12)
Comment your code, but not too much
Technical debt Composr development + Code Quality Checker can check comment density (pedantic mode) Write comments for non-obvious code. Don't comment obvious things ("this is a loop", "this runs if $foo equals $bar"). Write comments that explain your algorithms, and comments that give a high-level overview of what chunks of code do. Very roughly one of two comments per 7 lines, but don't feel bound to match that.
13)
Write comments in the standard way
Consistency Product development only Manually Comments typically look like // This is an example. Notice the // commenting method is used, followed by a space, followed by a capital letter. There are many special cases for why you would not do it like this, but generally speaking you should follow this format. This will make the code look neat and tidy, rather than comments looking like rough notes.
14)
Use constants instead of "magic numbers"
Technical debt Composr development + Manually Don't use "magic numbers". For example, instead of using the numbers 1, 2, and 3 with special meanings throughout your code, define constants and reference those instead.
15)
Everything should be configurable from within Composr
Usability for webmasters Product development only Manually No FTP/file-manager should ever be necessary to operate, configure, or extend an installed Composr website.
16)
Use our standard code header
Legal Product development only Manually We have a standard comment format at the top of files (different depending on the file type). Use it consistently.
17)
Support offline
Offline development and Intranets Product development only Manually Unless intrinsically necessary for a particular piece of functionality, do not rely on external URLs such as CDNs. External URLs create a number of problems, including: network firewalls operating with safelist blocking functionality (e.g. in museums), erratic firewall blocking (e.g. in China), full functionality not being available offline (e.g. when developing on an airplane), or full functionality not working in private offline Intranets (e.g. in schools). If users want to make use of CDNs they can make manual changes to achieve it.

Code text formatting

Standard Reason Scope Test method Notes
18)
Blank lines at the end of files
Portability Product development only Automated test async_tests/basic_code_formatting There is a Unix standard that text files should have a terminating line break. As various tools enforce this we standardise on a single line-break for most files. A few exceptions are defined within the automated test for the rare cases where we can't do this.
19)
Unix line endings
Consistency Product development only Automated test async_tests/basic_code_formatting We work in Unix line-ending format, not Windows.
20)
No trailing whitespace
Consistency Product development only Automated test async_tests/basic_code_formatting Unless there's a good reason, we don't want floating white-space on the end of lines of code. It's messy.
21)
No BOM markers
Stability All development Automated test async_tests/basic_code_formatting Many programs are not compatible with byte-order-markers.
22)
Use ASCII
Stability Composr development + Automated test async_tests/basic_code_formatting In order to avoid having to configure IDEs and text editors on what character set to use, and problems with Composr running on different character sets, just make it so your code files don't require any specific character set. Certainly don't have your code outputting non-ASCII characters directly – encode such characters in some smarter way, e.g. using HTML entities.

Back-end programming

Third party code

You do not need to make third-party libraries meet our particular choice of standards. We trust third-party developers to have their own methods for ensuring standards are high. Exclude PHP files from automated testing by putting /*CQC: No check*/ (disables the CQC) and/or /*CQC: No API check*/ (disables the CQC's PHPDoc scanning/compilation) at the top of the file.


General

Standard Reason Scope Test method Notes
23)
Use PSR-12 coding standard
Consistency Composr development + Code Quality Checker, PHP CodeSniffer (integrated with CQC) Use PSR-12, e.g. the bracing style, 4-spaces not tabs. Our only exceptions are relating to:
- class and namespace structure, as we are not an OOP-heavy codebase and not a library
- we allow long lines
- don't split function/method declarations across multiple lines
- we may split function calls (parameters) across multiple lines and include blank lines to space out groups of parameters
24)
Spaces around operators
Consistency Product development only Code Quality Checker Do like $a + $b not $a+$b. This makes the code easier to read and understand for some programmers, and largely consistent with what other projects do.
25)
Use short array syntax
Consistency Composr development + Code Quality Checker We want our code to be concise and clear, so we use the [...] syntax not the array(...) syntax. This syntax is similar to JavaScript, so very recognisable to the modern developer.
26)
Trailing commas for multi-line arrays
Revision control Product development only Code Quality Checker If you have split an array or JavaScript object initialiser across multi-lines, use a trailing comma. This means every array element has a comma after it. The advantages are it looks more symmetrical and it is less likely to conflict in revision control should the array be extended with further elements.
27)
Align functions precisely in code
Modularity: So Composr code-overriding can work on it Composr development + Automated test sync_tests/override_issues Functions must not be indented in code, and methods must be indented by exactly 4 spaces. There must be exactly one space after the function keyword and no tabs. Braces must go on the next line with the same indentation as the function and the closing brace must match too. The contents of the function must be 4 spaces deeper.
These rules are very standard, but if broken then code-overriding may not work correctly because Composr assumes them while doing efficient function-level overriding (there is no full PHP parser involved).
28)
Pass URLs properly
Compatibility with ModSecurity Product development only Manually URLs-within-URLs need to be encoded carefully so as to bypass ModSecurity restrictions (often any embedded URL is blocked). Basically URLs should be read in with a INPUT_FILTER_URL_* flag to *_param_string, and URLs should be placed within URLs/forms with a protect_url_parameter call around their value.
29)
Trim white-space for usernames/passwords/e-mails
User experience Composr development + Manually Look to see how usernames/passwords/e-mails/domain-names/paths etc are input in other parts of the code and do the same. Mainly we trim white-space to reduce the chance of copy & paste errors. Also we don't do other normal manipulations on password fields.
30)
Do not use braces around the file path for require / require_once / include / include_once
Stability Product development only Code Quality Checker Braces are not necessary for these directives. Furthermore, we have noticed some unstable behaviour in PHP when using these directives with braces around the path. This is especially the case when utilising the output to perform error-handling.
31)
Do not use require_once or include_once on dynamic PHP files
Stability Product development only Manually The contents of _config.php, data_custom/rate_limiter.php, and data_custom/errorlog.php might change dynamically. When reading any of these or other dynamic PHP files, you should use include or require, not their *_once versions, to ensure you have the most up-to-date version loaded in.
32)
Use Composr APIs wherever appropriate
Security, stability, consistency Composr development + Manually If is very easy to make mistakes when writing new code. Our functions have been tuned over years. For example, our http_get_contents function works on any server and supports lots of options.
33)
Use the temporal API, not PHP date/time functions
Internationalisation / consistency All development Manually Don't forget about users potentially being in different timezones. Rarely if ever use PHP's inbuilt date/time functions – use Composr's temporal API. The PHP date function doesn't even support non-English strings. Additionally, when dealing with intervals, we have two very-efficient functions in this API that should be used (e.g. instead of the unreliable strtotime): to_epoch_interval_index and from_epoch_interval_index.
34)
Use the type parameter for your modules
URL schemes All development Manually All modules should be coded to use type as the internal specifier of what 'screen' to show. The run function should decide what function to call based on this parameter, and typically also the run_pre function will be there to load metadata for the screen (allows HTML to start streaming while the screen code runs). The default entry point for a module must either not exist, or be named browse. Do not hardcode defaults other than browse as it will interfere with the page-link permission security and the URL scheme support.
35)
Remember to apply sorting wherever appropriate
Usability: so live sites are tidy All development Manually Remember to sort things. Sometimes this needs doing in PHP code, and sometimes it needs doing in your queries (ORDER BY). If doing it in PHP code consider you usually will want to do a "natural" sort, to consider whether case-insensitivity needs enabling, and to consider Unicode-safe sorting (handled by the cms_mb_*sort functions). Typically you should order data records by date, and never by ID (because IDs may not be sequential, especially on exotic database backends). You should never assume that data comes out of the database in the same order as it goes in.
36)
Use content sorting standards when sorting resources
Stability: so obscure sorting works (e.g. the main_multi_content block) All development Manually When sorting resources which have a resource or content hook, use the process_sorting_params function to parse and validate sorting. Relevant content and resource hooks should define all sortable fields not already defined by handle_abstract_sorting (or if they must override the default SQL) in their additional_sort_fields property. These fields should be defined as a map of sortable name to its custom sorting SQL (null: key is the name of the field, and we want to sort directly on that) (direction is suffixed automatically) (field names for the relevant table should start with r.). Value can also be a map of ASC and DESC to define different custom SQL for ASC versus DESC sorting (you will need to explicitly define the direction in the values in this case), or to leave blank if a module handles the sorting itself. One of the values returned from process_sorting_params is the SQL sorting query which can be used directly in a CRUD module's get_entry_rows. If using it directly on a query, and the query does not map the resource table to r, then you will need to do str_replace('r.', '', $sql_sort) on the sorting SQL query when putting it on the end parameter (do not forget 'ORDER BY ' before it!).
37)
Never use SQL queries as names of sortables
Security All development Manually Never use raw SQL queries as the name of sortables (simple field names are allowed). This exposes the query in the URL and is a security risk. Instead, make a custom name and define it, along with your SQL, in the resource or content hook's additional_sort_fields property.
38)
Remember to apply pagination wherever appropriate
Performance All development Manually Don't forget to paginate your browse screens using Composr's pagination or back/next system.
39)
Consider performance
Performance: on real live sites All development Manually Don't do silly things that affect performance like running queries inside a loop/recursion.
40)
Performance over architecture
Performance: on real live sites All development Manually We put performance ahead of what other's may consider strong architecture – we don't create complex or abstractions or heavy code structures for core code to keep our framework's footprint low. Latency and memory usage is very important, and we don't want to make the same overhead mistakes other systems have made.
41)
Be careful with case conversions and case insensitivity checks
Stability Product development only Manually If you use the PHP strtoupper or strtolower functions, it won't work correctly for the letter i/I if the locale is Turkish, and on a threaded shared server environment the locale may be changed by other system users. Similarly, preg_* functions in case insensitivity mode won't work for i/I, so if you really need case insensitivity for that letter then provide both in a character class, [iI].
42)
Never start a file <?
Portability Composr development + Code Quality Checker PHP short_tags option may be off.
43)
Never end a PHP file with ?>
Stability Composr development + Code Quality Checker It is not needed, and if a blank line ends up after it accidentally (e.g. added by FTP) then cookies won't save.
44)
Use unique function and class names
Modularity: you may need to load up both at some point All development Code Quality Checker No two classes or functions may have the same name, regardless of where they are defined or used. It should be possible for the whole set of Composr PHP files to be simultaneously loaded.
45)
Don't rely on "Security through obscurity"
Security All development Manually Always assume that people will try and hack the website, and that they have the full source code, so don't rely on them not guessing URL parameters etc. For example, if you implement some kind of permission scheme for a feature, don't just limit how the link to the feature is shown, also limit the interface and actualiser functions for the feature.
46)
Be careful with: header(), eval(), preg_replace()
Security All development Manually, Code Quality Checker pedantic mode will help If you're putting variables into the header function consider data could contain \r or \n symbols, so escape via the escape_header function. If you're using eval be extremely careful to validated input; there's rarely a reason to use it anyway. If you're using preg_replace make sure to use preg_quote and to manually escape the regular expression encapsulation character (usually # or /).
47)
Avoid using eval, $$, create_function, or preg_replace with /e
Security Product development only Manually It's hackerish and prone to security issues. There's rarely a good reason to do it.
48)
Create a file and use require or require_once instead of using eval or cms_eval when using more than a small snippet of PHP code
Security, performance Product development only Manually Using eval is hackerish and prone to security issues. Additionally, eval code does not utilise PHP's built-in cache engines. When compiling together PHP code to execute, consider creating temporary PHP files in the _compiled directory and using require or require_once on them. Make sure if you do that you use a fast file hash check so you only save when the code has changed. Additionally, you should use file locking both on save and before requiring to prevent compilation dog-piling. Note that Composr's built-in code override system and require_once already does this.
49)
Long running requests should be tasks
Stability, performance All development Manually If a page request may take more than 30 seconds to complete, it should be codified as a task so that it can execute out of the task queue in an orderly manner.
50)
Make your code robust against accessed deleted content
Stability All development Manually Imagine some content was added, then later deleted. Imagine that someone or a web crawler calls up a URL associated with the old content. It should produce an intentional error message (usually our MISSING_RESOURCE error), not a PHP error.
51)
underlining_for_names
Consistency Composr development + Code Quality Checker Use underlining_for_names in PHP variables, PHP function names, PHP arrays, database tables, database fields, and filenames. Not for JavaScript or CSS though.
52)
UPPER_CASE should be used for globals and constants
Consistency Composr development + Code Quality Checker
53)
Support minimum PHP version
Portability Product development only Code Quality Checker The version listed in Composr's minimum requirements must be supported.
54)
Do not use $_GET or $_POST directly
Security: implements security filtering Product development only Code Quality Checker, pedantic mode Instead use one of the set of get_param_*/post_param_* functions. Some exceptions are allowed, e.g. looping over all input to find what input exists.
55)
Use our MVC model as described in the Code Book
Technical debt Composr development + Manually, Code Quality Checker will help Composr enforces a strict separation between content, presentation and code. This is referred to as the MVC (model, view, controller) design pattern. No HTML is allowed in the PHP code except for very select exceptions, such as <option> (for efficiency) and hardcoded emergency error pages. You can usually assume the output will be some kind of HTML though (you don't need to code so abstractly that output could be a speech API, for example).
56)
Don't assume PHP extensions or PEAR are installed
Portability Composr development + Code Quality Checker Never assume that any extensions are installed unless they are listed in Composr's minimum requirements. If you do use something else (necessarily so, and only for auxiliary behaviour) you must cleanly handle the situation where it is not present using either code branching or helpful error messages.
57)
Use only safelisted functions
Portability: PHP disabled_functions option Product development only Code Quality Checker In fact, don't assume any rarely-used function is available unless it is listed in the sources/phpstub.php file (Code Quality Checker will check, no need to manually check). We have avoided many functions where webhosts often block using the PHP disable_functions option. Use our php_function_allowed function.
58)
Do not require cookies to be available on visitor machines
Compatibility with user computers Product development only Manually Cookies must not be required for functionality to work (except session cookies), unless said functionality is not needed to operate the system (i.e. alternatives exist).
59)
If you use cookies, use the Composr cookie API
Portability Composr development + Code Quality Checker, pedantic mode If you do use cookies, use the Composr cookie functions to read/set/delete them, as these are written to respect the webmaster's defined cookie settings (cookie domain, cookie path).
60)
Document cookies
GDPR Composr development + Document any cookies in a privacy hook, so that the privacy policy may be generated correctly.
61)
Template GUIDs must be unique and valid
Themeing Product development only Release scripts Don't copy and paste GUIDs in template calls or re-use them. Generate new, random GUIDs for the _GUID parameter, or delete it and run the "Fix missing / duplicate GUIDs" tool. The reason: these are used to allow different uses of shared templates to be differentiated via Tempcode. The GUID is a unique identifier for a specific template call.
62)
INTERNAL_ERROR do_lang and do_lang_tempcode calls must have a unique and valid GUID as its first parameter
Usability Product development only Release scripts Don't copy and paste GUIDs in language calls to INTERNAL_ERROR; use a unique and valid GUID for the first parameter, or set it as blank and run the "Fix missing / duplicate GUIDs" tool. The reason: when an internal error occurs, the GUID will appear, allowing users to report it and developers to better trace and understand what happened (especially in eval code or when stack traces are otherwise not available).
63)
Put standard index.html and .htaccess files in new directories
Security: stops probing/tunnelling Composr development + Automated test sync_tests/standard_dir_files Almost all directories should contain an empty index.html (to stop directory browsing on web servers that have it enabled). Most directories should also contain the small .htaccess file (copy it from another deep directory) that we use to block direct file access. If you strictly need to block access to a directory for security reasons you should also update web.config to reflect that (for IIS users).
64)
Functions should have proper API documentation
Technical debt, and it allows the Code Quality Checker to check more Product development only Code Quality Checker, function signatures feature Use the "PHPDoc"-style syntax correctly, and never ad-lib on its syntax (it gets parsed). Make sure the data types specified are accurate. Where null is allowed for a value make sure that is specified (via ? before the data type) and its meaning explained. Do not copy and paste comments from elsewhere without changing it to be fully correct for where you've added it.
65)
Remember that a site can have multiple themes/languages
Stability Product development only Manually Remember that a site can have multiple themes and languages – be careful that you do not cache things with implicit assumptions that should not have been made (e.g. instead of caching something in English, you should cache stuff per-language).
66)
Support multi-site-networks (i.e. site db != forum db)
Stability Product development only Composr development mode Remember that Conversr may be run over an M.S.N. (i.e. different URL, not just from the forum zone) and also from a separate database – don't make false assumptions about databases, tables (e.g. using SITE_DB instead of FORUM_DB and hence wrongly assuming the forum and site always share a database) and file-storage/URLs (e.g. using get_base_url instead of get_forum_base_url). Use the correct APIs for referencing Conversr data and files. As a general rule, any table starting f_ is an Conversr table and therefore should be accessed through FORUM_DB. If you are running development mode you will get an error ("Using Conversr queries on the wrong driver") if you use the wrong database object.
67)
Handle errors in failure-prone function calls (e.g. fopen)
Stability Product development only Code Quality Checker, pedantic mode
68)
Always respect/handle character-sets properly
Internationalisation, stability Composr development + Manually Don't assume the character set is utf-8, or ISO-8859-1 for input data or for output data (including files and HTTP API calls). Use appropriate conversions when interfacing with things that come-from/go-to a potentially alien character set. Composr has file APIs that deal with byte-order-marks (BOMs) appropriately.
69)
Don't put loose code (code outside functions) in any PHP file
Security: stops hackers "jumping in" All development Code Quality Checker The only loose code allowed is our standard boot code in the entry point files.
70)
Don't add more than a few lines of code to an "entry script"
Modularity: So Composr code-overriding can work on it Product development only Composr development mode / Code Quality Checker Instead of directly placing code in the entry-point file, put it in a sources file and require it in then call it as a function.
71)
Make choice of GET or POST carefully
Compatibility: So bookmarking works and web accelerators don't break things Composr development + Manually HTTP GET requests should not change database state in any major way (it can cause problems with web spiders, problems with web accelerator systems, and XSS vulnerabilities). POST requests should not be used for screens that might be reasonably bookmarked (e.g. search result screens, or result filtering screens). Composr has specific features in its API to allow you to choose whether forms use GET or POST requests. You absolutely must not make anything that will show up directly on the Sitemap change state in a major way.
72)
REQUEST_METHOD being POST is not a good enough check for a form being submitted
Security Composr development + Manually You may be tempted to distinguish between showing forms and actualising them by looking at REQUEST_METHOD. However, a login screen will generate a POST that relays any existing (if any) POST data along with login POST data to whatever URL the login had to happen from. This means that you cannot relay on REQUEST_METHOD being POST alone for there being a genuine regular form submission to where the user is at. You need to either have some required POST fields, or do detection based on a special hidden POST field being present.
73)
Set appropriate class visibility.
Technical debt Composr development + Manually Class variables and functions that are not accessed from outside the class should generally be declared as protected. This allows subclasses to access them, but not external code. Note a major exception: almost all functions in a module will be public because we want deep external control of module code flow (e.g. by automated tests).
74)
Code as if PHP was "strict typed".
Security, stability Product development only Code Quality Checker / ocProducts PHP version This is hard for many PHP programmers to grasp so is explained in a separate section in our Code Book.
75)
Don't write code that'll output PHP 'notice' errors
Stability Composr development + Composr makes all PHP notices generate stack traces
76)
Allow initialisation functions to run twice
Stability Product development only Automated test sync_tests/override_issues If a developer overrides a .php file then the initialisation function of the original will also run. The person doing the override doesn't usually understand this and accommodate for it, so make sure you check that you are not doing anything that can't run twice. Usually this just involves guarding define calls.
77)
Don't assume about platforms
Portability Product development only Manually Do not assume Composr is running on Linux, or Microsoft Windows, or Apache, or IIS, or Mac OS, or a case-sensitive filesystem, or a case-insensitive filesystem, or anything else unless it is defined in our minimum requirements. This is an important lesson for new programmers who may be used to just making something work for the computer they are in-front of. You need to use universally accepted techniques rather than platform-specific APIs.
78)
Use log_it calls
Usability Composr development + Manually For any non-trivial admin action, do a log_it call for it, and implement good quality metadata in an actionlog hook for that.
79)
Use the correct redirection APIs
Portability Composr development + Manually There are a number of internal functions to do redirects (redirect_screen, redirect_exit, assign_refresh), supporting <meta>-redirect for situations that don't allow HTTP-redirect, and attached-message hand-over. If you must output a Location header manually then comment why you are doing so.

Calling web APIs

Standard Reason Scope Test method Notes
80)
Show good error messages
Stability Composr development + Manually Web API calls must be able to sense the best possible error messages within payloads, or if a payload is missing (!is_array) then an error message from the HTTP message. This implies to call cms_http_request with ignore_http_status as true. We need to do this to help people debug their key configuration, and so people can provide good bug reports.
81)
Log errors
Stability Composr development + Manually Web API calls must log their errors and send an error notification (usually both via cms_error_log), or pass back their errors via exceptions or reference parameters (then the callers must do said logging/notification). This implies to call cms_http_request with trigger_error as false, so that errors don't result in insta-exit (most APIs should be designed to gracefully handle errors rather than exiting anyway). We need to do this because API failure should not go unnoticed.
82)
Do not e-mail the developers
Stability Composr development + Manually Web API errors should not result in anything that would send an error e-mail to the developers (e.g. fatal_exit). We do not want to be spammed when people mess up their key configuration or exceed quota.
83)
Write automated tests
Stability Composr development + Manually Any web API with an API key should have a Health Check to ensure the key continues to function correctly; otherwise at least a testing platform automated test is still probably needed.
84)
Do not assume JSON is intact
Stability Composr development + Manually When calling json_decode use @. Generally also we will parse into PHP arrays, as we tend to use arrays for data structures in Composr.

Addons

Standard Reason Scope Test method Notes
85)
Write well modularised code
Modularity Composr development + Manually Group things into separate files where appropriate. For example, an API acting for a certain addon would be in a number of code files named after that addon. CSS for an addon would be in a file named after that addon.
86)
All files must be associated with an addon
Modularity Product development only Automated test first_tests/modularisation Every file intended to be distributed with Composr must be registered in one addon, installation / uninstallation / integrity-scans. To do this list it in the obvious position within one of the sources/hooks/systems/addon_registry files. If it is a PHP file it should have a matching @package line at the top of the file.
87)
Manage any interdependencies between addons
Modularity: so addons may be uninstalled freely Product development only Manually, Automated test async_tests/addon_references helps a bit, Automated test sync_tests/addon_guards helps a bit Dependencies should be properly handled via hooks, using the get_dependencies function in the addon_registry hook (i.e. say addons only work if some other addon is also there), and via code that uses the addon_installed function to guard off the dependency. For example, if you used the galleries addon in some feature in the downloads addon, you would make sure the downloads addon did not break if the galleries addon was uninstalled (instead you'd write code to turn off the feature if the dependency was not met, or return an error message via the ADDON_MISSING language string).
88)
Hooks should not crash for missing dependencies
Integrity Product development only Automated test sync_tests/addon_guards Hooks should not crash for missing dependencies (including the files owner addon), they should just exit cleanly.
89)
Blocks should not crash for missing dependencies
Integrity Product development only Automated test sync_tests/addon_guards Blocks should not crash for missing dependencies (including the files owner addon), they should just put out an error as the block output. The addon_installed__messaged function may be used for the check on the files owner addon.
90)
Modules should not crash for missing dependencies
Integrity Product development only Automated test sync_tests/addon_guards Modules should not crash for missing dependencies (including the files owner addon), they should just put out an error. The addon_installed__messaged function may be used for the check on the files owner addon.
91)
Entry-point scripts should not crash for missing dependencies
Integrity Product development only Automated test sync_tests/addon_guards Entry-point scripts should not crash for missing dependencies (including the files owner addon), they should just put out an error.

AJAX

Standard Reason Scope Test method Notes
92)
Set KNOWN_UTF in front-end controller
Internationalisation Product development only Manually This will tell Composr how to do a character set conversion – as JavaScript uses utf-8 regardless of your document character set. If you don't feel a front-end controller is needed, you may be able to just code using a snippet hook, and the snippet.php front-end controller.
93)
Put your code in a function named <filename>_script()
Consistency Product development only Manually
94)
Call prepare_backend_response
Internationalisation Product development only Manually This function will do character set conversions, and put out appropriate HTTP headers.
95)
Call exit(); after your code runs
Compatibility Product development only Manually If you do not explicitly exit your code then hosting using auto_append_file could append junk to your XML/JSON, corrupting it. This is common on free webhosting plans.

Files

Standard Reason Scope Test method Notes
96)
Use the fix_permissions function after making a new file
Portability: makes the file deletable via FTP Product development only ocProducts PHP version The cms_file_put_contents_safe function has a flag to do this automatically.
97)
Run the sync_file function after making any file changes
Scalability Product development only ocProducts PHP version The cms_file_put_contents_safe function has a flag to do this automatically.
98)
Be clever about text line-endings
Portability Product development only Manually Multi-line textual input (from the web browser, or from the file-system) should go through the unixify_line_format function, to make sure line-terminations are converted to the correct format that the server operating-system needs.
99)
Generally use binary mode
Internationalisation, portability Product development only Manually We almost always open text files as binary, not text (t mode). This is because we don't benefit from line-ending auto-conversion and we prefer consistency across platforms. We use our unixify_line_format function instead to enforce consistency.
100)
Make sure you use appropriate file read/write locking
Stability Composr development + Manually Files are never locked automatically, and it can cause big problems if you don't manually do locking – writes can mess up each other, or partial files can read in and cache. The cms_file_put_contents_safe will handle locking well.

URLs and file paths

Standard Reason Scope Test method Notes
101)
Use the build_url function and PAGE_LINK symbol
Portability, URL schemes All development Composr development mode Do not hard-code URLs – use build_url() correctly. This function will convert to whatever URL scheme is in place, as well as propagate keep_ fields. Never use relative URLs as a <base> tag can break them, and we also want all URLs to easily be downloadable server-side in utility functions and when debugging.
102)
Use build_url's $skip_keep parameter for e-mailed Comcode
URL schemes All development Composr development mode If you are using a URL in an e-mail, make sure the $skip_keep parameter is set to true, as keep_ parameters should not go out in e-mails.
103)
Use Comcode url or page tags for URLs within notifications
URL schemes All development Manually This ensures the URL is not parsed incorrectly, or that the media system doesn't process the URL as an embed rather than a hyperlink.
104)
Fix broken user input
Usability: Users often miss out http:// All development Manually If you write functionality that accepts URLs into the system via a form interface then use the remove_url_mistakes function to make sure the URLs start http:// (users will often miss it out). It should add that on the front if the URL does not already contain ://.
105)
Avoid relative paths
Portability All development ocProducts PHP version Don't assume any current directory (either URL or filesystem): use full paths (using get_file_base or get_custom_file_base to start off the full path).
106)
Don't assume where a module is located when you link
Flexibility: webmasters may move modules around Product development only Manually Don't assume which zone a module is located in, except in the case of admin modules. Use the get_module_zone function or _SEARCH token to break down such assumptions.
107)
Don't link to screens using non-canonical URLs
SEO: canonical URLs, SEO: avoid duplicate content Product development only Manually For example, the forumview module doesn't pass through a type if it is browse. Most other modules do. Any browse screen doesn't use an ID if the ID would be db_get_first_id().
108)
Don't assume where an entry-point is located when you link
Flexibility: webmasters may move entry-point scripts around Product development only Manually Use the FIND_SCRIPT symbol or find_script function to find zone-based scripts. Put non zone-based scripts in data (or data_custom if for a non-bundled addon).
109)
Use the *_custom filing system properly
Modularity: so the override system can work Product development only Manually Never put custom/user-files/logs in a non-custom directory, and never put code files in a custom directory if they are going to be distributed with Composr. Generally don't put files in places where they don't belong.
110)
Use the get_custom_base_url, get_custom_file_base, get_base_url and get_file_base distinctions properly
Shared installs: multiple sites with one Composr install, GAE, unreliable include paths and default execution paths Product development only Manually Use a custom_* function if referencing custom/user-data, otherwise don't. Core Composr files that can't be changed from inside Composr are not custom, other files are. i.e. non-custom files are those that would be shared between sites.
111)
Use / as a directory separator, even on Microsoft Windows
Simplicity Composr development + Manually If we use multiple directory separators it gets messy. / is safe for all PHP library functions, even on Microsoft platforms.

Databases

Standard Reason Scope Test method Notes
112)
Avoid writing SQL
Security, portability All development Code Quality Checker, pedantic mode Wherever possible just use our APIs, don't write SQL by hand. Use query_insert, query_update and query_delete. The only common cases where you need to write SQL by hand completely are if you need to do OR queries, or do wildcard searches.
113)
When you need to write SQL, write it securely
Security All development Code Quality Checker, pedantic mode Never ever write SQL without thinking about security, particular SQL injection vulnerabilities.
114)
Use db_escape_string not mysql_escape_string / addslashes
Portability All development Manually Avoids assumption about how escaping is done (you might not know this but the MySQL escaping method is non-standard and the Microsoft one is actually "correct"!)
115)
Be type-strict
Portability Composr development + MySQL-strict-mode and XML database driver For example, do not do WHERE validated='1' as validated is an integer field and thus must be referred to as such. Most database are type-strict, MySQL is an exception so it's a big mistake to get things wrong here. We automatically enable MySQL strict mode, unless it is disabled with destrictify().
116)
Use sensible database indices
Performance All development Manually Create database indexes if you regularly query a table using non-key field(s).
117)
Use the most appropriate Composr database field types
Consistency: higher-level meta functions can work All development Manually When you define database schemas via create_table use the closest Composr field type you can. For example, make use of AUTO_LINK and TIME and USER rather than always using INTEGER. Use URLPATH for URLs. This will help Composr understand its own database better (which it does need to do in some situations such as broken URL scanning). If a field needs to support Comcode, define it as SHORT_TRANS__COMCODE or LONG_TRANS__COMCODE (never run comcode_to_tempcode on-the-fly).
118)
Be careful about key fields
Internationalisation, portability All development Manually Most tables will have an AUTO field called id as the key field. However, there are many cases where normal field(s) make sense for use as a key. If you're sure these will be reliable keys, even into the future, then you should usually go for it. However, there is an important special case. If you have a 255-character field then this cannot be used for a utf8mb4 table, because it will exceed the 1000 bytes limit (4*255>1000). For this situation you either need to use an id key, or you need to shorten the maximum length of the field (e.g. ID_TEXT instead of SHORT_TEXT), or you need to accept that Composr will automatically detect the issue and make it a utf8 table (which cannot store emojis). You probably don't need emojis for any field you'd want as a key, but be wary.
119)
Avoid complex SQL
Portability Product development only Automated test cli_tests/_installer_xml_db, Automated test async_tests/database_unsupported_sql You cannot use SQL syntax that is not widely supported, unless the db_function function supports it. Expressions on aggregate functions are not allowed.
120)
Don't use LIMIT manually, use the API
Portability: MS Access Product development only Composr development mode Make use of the from/max parameters to the query functions rather than hard-coding an SQL LIMIT clause (this is required for MS Access compatibility).
121)
]Don't use ` symbols in your SQL and don't use keywords for table/field names
Portability: they are MySQL-specific Product development only Automated test cli_tests/_installer_xml_db, table creation code checks You will have been forced to choose field names that don't require escaping anyway.
122)
In queries use <> rather than !=
Portability Product development only Manually Not all databases support the latter syntax.
123)
Use db_string_equal_to
Portability: Oracle Product development only Automated test async_tests/database_unsupported_sql Do not build string equalities into WHERE clauses directly (e.g. WHERE foo='bar'), use the db_string_equal_to function (e.g. WHERE ' . db_string_equal_to('foo', 'bar')).
124)
Keep your queries as simple as possible
Portability Product development only Manually
125)
Do not assume the first record in a table is 1
Portability Product development only Manually This is the case for MySQL but does not necessarily hold true. Use db_get_first_id() to find the number to use.
126)
Use upper case SQL keywords (e.g. WHERE not where)
Consistency Product development only Manually If you must write your own SQL, write SQL keywords in upper case (e.g. SELECT, FROM, UPDATE, WHERE, LIKE, AND, OR). This makes it easier to scan over them, and makes it look distinct from PHP code.
127)
Use the correct forum API
Forum compatibility layer Product development only Manually Do not assume you are developing for Conversr (unless you are coding Conversr directly), or other forum driver. In other words, use the forum driver API properly and do not assume anything about the forum and member software beyond that.
128)
Use table prefixes
Portability Product development only Manually Do not assume any database table prefix (so don't assume cms_ is at the start of the table names – use db_get_table_prefix to find out what to use).
129)
Avoid processing full row sets
Performance Composr development + Manually For many kinds of table there could be 10's of thousands of rows, and it is very easy to forget this scaling problem when devising algorithms during development.
130)
No null key fields
Portability Product development only Table creation code checks You may not include a field that may be null within a key.
131)
No null strings
Portability Product development only Table creation code checks You may not allow nullability of a string field, as some databases can't distinguish null from the empty string.
132)
Don't assume case (in)sensitivity
Portability Product development only It is up to the database layer whether database string comparisons are case sensitive or not. Usually you don't need to think about it because the sensitivity just maps straight to the application behaviour you'd expect, but there may be occasional issues to consider. The default supported MySQL driver is case insensitive as the default collation used is utf8mb4_general_ci.
133)
Define privacy hooks, Manually
GDPR Composr development + Automated test async_tests/privacy_hooks Any member-ID, IP address, e-mail address, or human-name fields need to be managed for GDPR purposes. This includes support for downloading (serialising), deleting, and anonymising. Serialised content should be understandable without further context, so things like category titles may need to be included (if important) rather than IDs. Database records often should not be deleted without further consideration, so proper deletion API calls should be codified.

Templates and Interfaces

Standard Reason Scope Test method Notes
134)
Use existing conventions and features and re-use templates where possible
Consistent UI, usability, performance, themeing Composr development + Manually Generally try and replicate the interface conventions of existing modules, unless there is a strong argument that you are dealing with a special case. For example, don't design new ways of previewing things, use the standard previewing mechanism. Another example, use the pagination function instead of implementing new previous/next navigation code. There are almost always API functions or templates for you to use, so it's actually easier to use conventions than re-implementing from scratch. Some are listed further down.
135)
Define breadcrumbs in the standard way
Consistent UI, usability for webmasters Composr development + Manually Use breadcrumb_set_self / breadcrumb_set_parents as required to generate appropriate breadcrumb chains. Try to do it at the start of screen code, so it'll be set if an error happens during screen generation.
136)
Inline edit links
Consistent UI, usability for webmasters Composr development + Manually For viewing content: if someone has permission to edit something, then for the template that shows the content, pass in a parameter called EDIT_URL (typically), containing the URL to edit the content. If the user doesn't have edit permission, then pass in EDIT_URL empty. Use the standard STAFF_ACTIONS include method for displaying any actions within the template (see the DOWNLOAD_SCREEN template for an example).

These guidelines apply for admin modules…
Standard Reason Scope Test method Notes
137)
Write CRUD (add/edit/delete) modules
Consistent UI, usability for webmasters Composr development + Manually Write content/data management modules as a Composr CRUD module (add/edit/delete).
138)
Use standard approaches after an action is performed
Consistent UI, usability for webmasters Composr development + Manually CMS/Admin "completion" screens should either be redirects to an appropriate location (e.g. the view screen for what was just added/edited), do-next managers, or very rarely, message screens. Follow the conventions other modules use. If you do a redirect, use the proper API (the redirect_screen function) so that the 'success' messages show up on the destination screen for the redirect.
139)
Give admin modules a do-next manager for the browse screen
Consistent UI, usability for webmasters Product development only Manually A CMS/Admin module's functions should generally be divided by a welcoming do-next manager (i.e. a set of icons). Exceptions include: when adding only involves specifying a single field (in which case you can put add and edit on the same screen), and also when adding is for power-users only (in which case the add form would be hidden behind a button). Follow the conventions other modules use.
140)
Minimise number of interfaces, mark-up jump links consistently
Consistent UI, usability for webmasters Composr development + Manually Don't duplicate interface elements in Composr, unless the duplicate is clearly marked as some kind of shortcut/dupe/jump. For example, if there is a shortcut link between two modules (a duplication in navigation), use the standard Composr convention for informing the user they are jumping between modules (for an example, see how the link from the "Manage themes" screen to the "Zones" screen works).
141)
Populate the helper panel
Consistent UI, usability for webmasters Composr development + Manually Wherever appropriate define helper-panel tutorial links, and inline help, for Admin Zone and CMS Zone modules. There are functions for setting them.

Here are some guidelines for designing/naming templates…
Standard Reason Scope Test method Notes
142)
Templates should be in correct case
Portability Composr development + Composr development mode All template file names should be upper case, except for CSS and JavaScript files which should always be in lower case. Case sensitive file systems mean we want a strong convention. There may be a few exceptions, particularly CSS and JavaScript templates are all lower case.
143)
Included CSS and JavaScript templates should start with an underscore
Consistency Composr development + Manually This makes it clear which templates are includes. Note we don't have this convention for other kinds of template, as sometimes they may serve both as standalone and be included into other templates.
144)
Use _SCREEN templates
Consistent UI, themeing Composr development + Composr development mode All screens should ultimately be wrapped with some kind of template suffixed _SCREEN that includes a {TITLE}. This might be a standard screen template that already exists, such as INDEX_SCREEN or it might be custom one such as DOWNLOAD_SCREEN.
145)
Implement screen previews for new templates and maintain them with template changes
Testability Product development only Automated test sync_tests/_template_previews Previews allow themers to confirm a theme works on all screens, and allow us to do mass-checks of HTML.
146)
Use filename prefixes to group templates
Usability for webmasters Composr development + Manually For example, the download addon's templates mostly start DOWNLOAD_. There's no strict rule, be sensible.
147)
Prefix templates used by blocks with BLOCK_
Easy to relate templates to features Composr development + Manually Do this unless the template is also used by a module too.
148)
Prefix mail templates (Comcode) with MAIL_
Easy to relate templates to features Composr development + Manually
149)
Suffix other templates that are Comcode with _FCOMCODE
Easy to relate templates to features Composr development + Manually Any template that is for Comcode must contain the filename substring _MAIL or _FCOMCODE. Any template named like this must be in Comcode format.
150)
Store templates in the correct directory
Consistency Composr development + Manually XML and text (including Comcode) templates must be in their respective directories.

Here are some specific templates to use where appropriate…
Standard Reason Scope Test method Notes
151)
MAP_TABLE
Consistent UI Composr development + Manually A part of a screen that is for viewing title/value data in a table. There are also some functions to help with map tables.
152)
COLUMNED_TABLE
Consistent UI Composr development + Manually Templates for rendering a plain table of something.
153)
INDEX_SCREEN / INDEX_SCREEN_ENTRY
Consistent UI Composr development + Manually A screen having a shortish paragraphed sequence of things to choose.
154)
INDEX_SCREEN_FANCIER_SCREEN / INDEX_SCREEN_FANCIER_ENTRY
Consistent UI Composr development + Manually A screen having a shortish tabled/described list of things to choose.
155)
CONFIRM_SCREEN
Consistent UI Composr development + Manually Show a preview and confirm it's okay.
156)
PAGINATION_*
Consistent UI Composr development + Manually Very simple browsing through multiple screens. Usually you'll want to use pagination instead.
157)
SCREEN_BUTTON / SCREEN_ITEM_BUTTON
Consistent UI Composr development + Manually Show a standard shaped button on the screen.

Here are some specific interfaces to use where appropriate…
Standard Reason Scope Test method Notes
158)
pagination function
Consistent UI Composr development + Manually An interface for pagination.
159)
results_table function
Consistent UI Composr development + Manually An interface for pagination of a table.

When showing errors use inform / error messaging APIs / conventions, correctly. There are 6 top-level kinds of messaging output…
Standard Reason Scope Test method Notes
160)
Where appropriate, access-denied screens
Consistent UI, usability Composr development + Manually Use the access_denied function as appropriate. It can be passed a language code, or a string. For Composr development, it should be a language code, so that the logs may be internationalised.
161)
Where appropriate, Fatal/Warn/Inform exit/screen
Consistent UI, usability Composr development + Manually For full-screen messaging. Use the whatever_screen or whatever_exit function. Specify the appropriate $type of the message.
162)
Where appropriate, Fatal/Warn/Inform attached message
Consistent UI, usability Composr development + Manually For response-embedded messaging where the code isn't failing per-se, but it's more like a predictable messaging situation occurred. Use the attach_message function for this. Specify the appropriate $type of the message. Logging is optional.
163)
Where appropriate, trigger a PHP-level error
Consistent UI, usability Composr development + Manually For response-embedded messaging of something that really is a kind of unexpected failure. Use the trigger_error function for this. Ultimately Composr will also call attach_message, and log the error.
164)
Where appropriate, include the WARNING_BOX template
Consistent UI, usability Composr development + Manually Places an on-screen warning about the action or content that a screen is providing. More contextualised than attach_message but requires a predetermined place for it within the template you're embedding into.
165)
Where appropriate, inline errors
Consistent UI, usability Composr development + Manually Use one of the standard CSS classes, such as nothing-here or red-alert or inline-wip-message to mark the error using standard Composr styles. Generally this logic will be performed deep within templates or when a block has to terminate with a clean error.

Specific guidance for input forms…
Standard Reason Scope Test method Notes
166)
Use FORM_SCREEN for forms
Consistent UI, usability Composr development + Manually Use a standard form template such as FORM_SCREEN (most cases) or FORM (if the form is just part of a complex screen). These templates provide automatic client-side field validation; server-side field validation is provided automatically also, via the standard field retrieval functions (e.g. post_param_string).
167)
Use the form field API to generate input fields
Consistent UI, usability Composr development + Manually Use our most appropriate standard input field types (e.g. form_input_integer), or creating a new type if you have a special case.
168)
Use the standard method to split up large forms
Consistent UI, usability Composr development + Manually Split complex forms into groups by using the FORM_SCREEN_FIELD_SPACER template to separate them. Hide groups of advanced functionality that is not often needed (often this functionality can fit nearly into a single "Advanced" group).
169)
Use the standard method for showing groups of alternative fields
Consistent UI, usability Composr development + Manually Use the alternate_fields_set__start system to mark alternative fields (i.e. the case where users may enter something in a field, or something in a different field, but never in both fields). Look at examples in existing code to see how it works.

Blocks

Standard Reason Scope Test method Notes
170)
List block parameters
Usability for webmasters Product development only Automated test async_tests/blocks Define all block parameters in the block code itself (in the info function for the block) then document all block parameters via inserting the language strings in language files for the addon the block is a part of (see how it's done for existing blocks for examples).

Front-end programming

Spelling/grammar/lexicon

(examples of what not to do are in italics)

Standard Reason Scope Test method Notes
171)
Don't assume English
Internationalisation Product development only Manually Don't hard-code English text into your code, unless it is only designed for programmers to ever see.
172)
Do Not CAPITALISE words just for the Effect
Usability Composr development + Manually Capitalisation is for proper nouns (e.g. Charlie or Microsoft, but not box), for new sentences, or sometimes for sentence-like standalone phrases. We do not generally capitalise things for effect, e.g. "Add News".
173)
Check readability from an outsider's perspective
Usability Composr development + Manually Triple check everything you write to make sure it is completely clear, as concise as reasonably possible, and cannot be misinterpreted.
174)
Where appropriate use attractive HTML entities, not just ASCII
Usability Product development only Manually Use HTML entities where possible, in particular ndash, mdash, hellip, ldquo, rdquo, lsquo and lrquo. These make the output that much nicer, and grammatically more correct (doing - instead of &ndash; is technically wrong, as a hyphen is not a dash).
175)
Use terminology with consistency
Usability Product development only Automated test async_tests/lang_spelling Apply consistency to your terminology. Sometimes multi-word proper nouns are given hyphens (e.g. "multi-word" ;) , sometimes concatenation is used (e.g. "multiword" ;) , sometimes abbreviation is used by just using the first letters placed together as capitals (e.g. "MW" ;) , and sometimes each word is given in capitals (e.g. "Multi Word" ;) . All this inconsistency is ingrained in English, but for any single term, reference it in a uniform way. We do not usually use the antiquated form of abbreviations using dots (e.g. "M.W." ;) .
176)
Don't pollute global.ini
Technical debt Product development only Manually, Automated test to limit .ini file size (lang_ini_size If phrases are not general to the rest of Composr, then put them in a language file that can be individually included by a module.
177)
Most lang strings support HTML, some do not: be aware
Stability Product development only Automated test async_tests/lang_html_safe You may assume language strings can contain HTML. There are only a few exceptions where this is not true. Theoretically the burden usually lies on code removing HTML formatting that is there and can't be used, rather than the other-way-around. Practically, there are some cases where HTML entities are allowed but not HTML tags (e.g. if a language string is used for a title attribute), and you will just need to use your judgement and test if you change an existing language string to include HTML tags. The flip-side to HTML being allowed is that you must encode things like the ampersand symbol as entities.
178)
Minimise adding new language strings
Internationalisation Product development only Manually Try to reuse strings as much as possible. Don't add new strings unless it is an improvement, as they need to be translated into potentially dozens of languages. To facilitate this, phrases should be made as general as reasonable… i.e. avoid using context where possible (e.g. use Added entry {1} rather than Added download {1}). Don't do this if it creates a usability problem though.
179)
Don't use the name "Composr", use the phrase "the software"
Debranding Product development only Automated test async_tests/lang_spelling
180)
Use <kbd> within language strings as appropriate
Usability Product development only Manually Use this for something typed, or for code.
181)
Use our standard terminology
Consistent UI Product development only Manually Use correct terminology like 'Codename', 'Option', 'Title', 'Description'.
182)
End form field descriptions with a full stop
Consistent UI Product development only Composr development mode
183)
Encode pluralisation in language strings
Usability Composr development + Automated test async_tests/lang_misc Use the There are {1} {1|thing|things} syntax to use the correct word pluralisation form depending on if the language string parameter is a singular number or not.
Non-English languages may invent their own syntax, which is outside the scope of these coding standards.
184)
Encode article+noun vowel rule correctly in language strings
Usability Composr development + Automated test async_tests/lang_misc Use the {1|A|An} {1} syntax to use the correct article word depending on if the language string parameter starts with a vowel or not.
Non-English languages may invent their own syntax, which is outside the scope of these coding standards.
185)
Use Oxford Commas
Usability Product development only Manually Use the Oxford Comma.
186)
Define administrative split
Internationalisation: ease of Product development only Automated test async_tests/lang_administrative_split The sources_custom/string_scan.php lists which language strings are and are not administrative. This is done so that translation can be focused on the non-administrative strings only.
187)
Don't define binary options in negative terms
Usability Composr development + Manually Do not create an option such as "Disable widgets". It is unnecessarily confusing because you can just make an option "Enable widgets" that defaults to on. This coding standard applies universally across the product, including configuration options, form fields, and code. The only exception is hidden configuration options, which are often set to '1' to disable something.
188)
Do not use problematic language
Social equity Composr development + Manually Instead of saying "blacklist", say "blocklist" or "exclusion-list" or "deny-list". Instead of saying "whitelist", say "safelist" or "inclusion-list" or "allow-list". This is to reduce subconcious suggestion that black=bad and white=good.
|-
| 189)
When referring to the site rules, say "rules (Terms of Service)" rather than just "rules" or "Terms of Service"
| Usability
| Product development only
| Automated test async_tests/lang_spelling
| This standard is only required for language string files. The Setup Wizard features a corporate "Terms of Service" template. When selected (or the value of use_tos_lang is set to '1'), Composr should change use of the word "Rules" to "Terms of Service" automatically.

Respect the King of England ;) , but also do not confuse Americans…
Standard Reason Scope Test method Notes
190)
Do not use American spellings such as color or center or license
Consistent UI Product development only Manually, Automated test in verbose mode async_tests/lang_spelling Composr originates from the UK. We have special processing to support American English automatically. We only allow American spellings in a code-context, but this exemption also stops us automatically scanning for violations.
191)
Use British English standard for mixing periods and parentheses
Consistent UI Product development only Manually Periods and parentheses mix so that the period follows the closing parenthesis. For example: example (example).
192)
Say "tick (check)" rather than just "tick" or just "check"
Internationalisation Product development only Automated test async_tests/lang_spelling The word "tick" in British English is "check" in American English (referring to the square 'tickbox'/'checkbox' on HTML forms). Due to this disparity, write both like "tick (check)".
193)
Use American-friendly bracket terminology
Consistent UI Product development only Manually Brackets in US English typically means square brackets, or sometimes some unspecified type of bracketing, while in British English it means parentheses. Parenthesis/parentheses is a word valid in both UK and US English. Therefore never use the word "bracket" when specifically referring to parentheses.

Language for submit buttons…
Standard Reason Scope Test method Notes
194)
For an add button, use the title text ("Add xxx")
Consistent UI Product development only Manually
195)
For an edit button, use the language string SAVE ("Save")
Consistent UI Product development only Manually Only use the language string EDIT ("Edit") when you are distinguishing it against the language string DELETE ("Delete").
196)
For a delete button use the language string DELETE ("Delete")
Consistent UI Product development only Manually Usually these are actually ticks for an edit screen, but sometimes we do have them. If it's more of a removal than a deletion, the language string REMOVE ("Remove").
197)
For an intermediary button, use the language string PROCEED ("Proceed")
Consistent UI Product development only Manually
198)
For sorting, use the language string SORT ("Sort")
Consistent UI Product development only Manually
199)
Use CHANGE and PER_PAGE where appropriate
Consistent UI Product development only Manually For changing the number of items shown, use the language string CHANGE ("Change"), or use the language string PER_PAGE ("Per page") if you are doing it as a sentence.

Markup/Tempcode

Standard Reason Scope Test method Notes
200)
Use tabs for HTML code
Performance Product development only .editorconfig / Automated test async_tests/basic_code_formatting HTML code (e.g. in templates) should be written using tab for indentations rather than spaces, unlike most other file types in Composr. This is to reduce page size slightly (while JavaScript and CSS is minified, HTML is not due to the overhead and risk of doing so).
201)
Take into account that content may not all be filled in
Usability for webmaster All development Manually Remember that if something may be blank (e.g. if it is possible for a field to not be filled in, or for there to be no entries in a list) you need to accommodate for that possibility. Sometimes no changes are required, but often if there are headings or boxes used to contain it then you'll want to wrap around the IF_NON_EMPTY directive so as to avoid empty headings/boxes. For example to stop an empty paragraph element producing an accessibility problem when SOMETHING is blank, guard it with IF_NON_EMPTY as follows:

Code

{+START,IF_NON_EMPTY,{SOMETHING}}
<p>{SOMETHING*}</p>
{+END}
202)
Make templates beautiful
Technical debt Product development only Manually Indent Tempcode HTML neatly. Each multi-line Tempcode directive or HTML tag should have its contents indented an extra level. Indentation is for the purposes of people reading/modifying the templates, not the final HTML, so indent with this in mind (i.e. Tempcode indenting is fine even though it'll make the final HTML look messier). The browser DOM inspector is how people view outputted HTML nowadays and that does its own automatic indentation.
203)
Align Tempcode-guarded parameters consistently
Consistency Product development only Manually We write code like:

Code (HTML)

<div {+START,IF,{FOO}} foo="bar"{+END} a="b"{+START,IF_PASSED,A} x="y"{+END}>Example</div>
 
Note how the space for the attribute comes at the start of the inside of the Tempcode directive. Note also for the first attribute we also have an additional leading space, so that IDE syntax highlighting works well.
204)
Use the Tempcode escaping syntax correctly
Security All development ocProducts PHP version For example, if you are outputting a textual parameter, make sure to reference it as {PARAM*} rather than just {PARAM}. Give big thought to this if you are outputting the variable into JavaScript code: usually you'll want to use the % escaper which locks down data very tightly.
205)
Meet WCAG (Web Content Accessibility Guidelines) and ATAG (maintenance status)
Accessibility Composr development + Automated test sync_tests/_template_previews Meet WCAG (Web Content Accessibility Guidelines) 1 level 3, and WCAG 2 (read them!). Write semantic markup. Always use the <p> element to mark true paragraphs if they are not alone in a table cell, and if they are always going to be just one single paragraph. Images should not be wrapped in paragraphs. Short lines may be wrapped in paragraphs as/if you wish.
206)
No deprecated tags or attributes
Accessibility Composr development + Automated test sync_tests/_template_previews Meet WCAG (Web Content Accessibility Guidelines) 1 level 3, and WCAG 2 (read them!). Write semantic markup. Always use the <p> element to mark true paragraphs if they are not alone in a table cell, and if they are always going to be just one single paragraph. Images should not be wrapped in paragraphs. Short lines may be wrapped in paragraphs as/if you wish.
207)
Write to XHTML5
Accessibility Product development only Firefox HTML validator extension / Automated test sync_tests/_template_previews We don't actually output XHTML mime-type, or write JavaScript as XHTML-compatible, but we do have the actual markup be XHTML-compatible for better readability and tooling-parsability.
208)
Don't make assumptions about word order by coding things like {!GO} {!HERE}
Internationalisation Product development only Manually Word order in languages may vary.
209)
Don't use native language directly in templates or PHP code
Internationalisation Product development only Manually However, colons and so forth may be used in templates in a visual way (even though strictly it is an Englishism).
210)
Avoid the style attribute ("inline styles")
Technical debt Product development only Automated test sync_tests/_template_previews Don't use the HTML style attribute except where you need to pass in styles contingent on template parameters.
211)
Make links accessible
Accessibility Composr development + Manually Any important link (i.e. a link that isn't just duplicating another) must be distinguishable by the contents of that link. Title attributes are not enough because Composr may turn those into JavaScript driven tooltips, which won't automatically come up in the normal link-cycling process a screen-reader will go through. In particular if you have a link around an image, make sure that image has alt-text that describes the link (not the image).
Links should also be distinguished from one another, and you may use the title attribute to do this if you want (as the screen-reader's initial list of links should be able to include that properly, even if we later remove that attribute).
212)
Only forcibly open up links in new windows in certain conditions
Accessibility Product development only Manually Only forcibly open up links in new windows in these conditions:
- The action is strictly auxiliary to an ongoing action (e.g. opening up documentation to read while performing the action, or opening a small pop-up window)
- An ongoing action is being performed that will spawn many followup actions that are not going to flow in sequence
- It is a link to an external website on the main website placed in a prominent position (e.g. link to RSS news story)
To be clear, do not forcibly open up links in these conditions (or any other not mentioned above):
- When it's a link to an external site not in a prominent position on the main website
- The action is arguably auxiliary but often may not be
- When moving between non-strongly related resources (e.g. clicking on a username in the forum-view)
Do not provide a choice of "open in new-window" and "open in same-window" links to the user – their web browser already gives them this choice.
213)
Use {!LINK_NEW_WINDOW}
Accessibility Composr development + Automated test sync_tests/_template_previews When links are sent to new windows, remember to use {!LINK_NEW_WINDOW} in the link title attribute. The purpose of the link should precede {!LINK_NEW_WINDOW}, so that the link title makes sense when read out alone.
214)
Don't use tooltips for critical information
Mobile, Compatibility with user computers Composr development + Manually Touch screen devices can't see them.

Guidance for tables…
Standard Reason Scope Test method Notes
215)
Don't use layout tables
Accessibility Composr development + Manually If you are showing tabular data – e.g. a map table or a columned table, it's always correct to use a table – if it can have sensible th tags, it's a proper table.
216)
Use the results-table CSS class on most tables
Consistent UI Composr development + Manually standard_border is formalised for other kinds of "Map table" or "Columned table", or just about any other kind of table.
217)
Use the th tag properly, and also thead / tbody
Accessibility Composr development + Manually Use the th tag to define headers. If you have a case where there are top-level and secondary-level headers, put the top-level header in thead and also make use of tbody.

Guidance for form autocomplete…
Standard Reason Scope Test method Notes
218)
Never use [autocomplete="off"]
Accessibility Composr development + Manually Browsers don't respect [autocomplete="off"] and it breaks auto-refill upon clicking back. Use the special/invalid value [autocomplete="autocomplete-disabled"] instead, it prevents autofill in most cases.
219)
Use standardised field [name]s when autofill is desired
Consistent UI Composr development + Manually We standardise around these autofill-able field [name]s: username, password, email, firstname, lastname, name, birthday, address1, city, state, postalcode, country, phone, and email. These are the field names most reliably supported for autofill and the form_templates.php API will provide the appropriate autocomplete attribute value for them automatically.
220)
Avoid standardised field names to prevent autofill
Consistent UI Composr development + Manually Avoid the field names mentioned in the row above when autofill is undesirable. For example: avoid any field named 'name' unless it really is a human name; consider 'title'.

CSS and JavaScript

Standard Reason Scope Test method Notes
221)
Use comment encapsulation
Tooling: syntax highlighting Product development only Automated test async_tests/comment_encapsulation Tempcode directives should use comment encapsulation so that syntax highlighting in text editors works.
222)
kebab-case should be used for CSS ID and class names
Consistency Composr development + Manually IDs should use kebab-case EXCEPT when mirroring (or closely mirroring) a name attribute.

CSS

Standard Reason Scope Test method Notes
223)
Follow naming and layout standards
Consistency Product development only Manually Use the same bracing style, use of quotes, and way of layout out attributes, as existing CSS code broadly is using. The way we do it is very mainstream.
224)
Use hyphens to split words
Consistency Product development only Manually By mainstream contention we use hyphens to split words, not underscores or camelCase.
225)
Use tabs for CSS code
Consistency Product development only .editorconfig / Automated test async_tests/basic_code_formatting To keep things tidy we use tabs for indentation rather than spaces.
226)
Keep CSS minimal and simple
Technical debt, performance Composr development + Manually The key to writing good CSS is understanding. Understand your way through how browsers render CSS, and the quirks of particular browsers. Never ever write CSS by adding or hacking with properties until it works.
227)
Use reliable CSS
Compatibility with user computers, Standards compliance Product development only Automated test sync_tests/_template_previews Only use standards-compliant CSS that works across all supported browsers. Test on all the browsers that we support.
228)
Avoid setting heights
Accessibility All development Manually If you set heights then if people increase their font size (or a translation uses longer/more words) then it's likely text will overflow and look awful. There's rarely a true reason to set a height anyway: if you need to clear floats use float-surrounder (described elsewhere in this document). You can also sometimes make use of min-height to meet certain constraints.
229)
If possible avoid setting dimensions
Stability, themeing, internationalisation All development Manually Do not assign widths or heights or min-widths or min-heights unless
necessary (e.g. to make sure you align with a background's size). It makes it very hard to adapt designs later if the spacings change and it makes pixel-level browser bugs cause larger problems.
230)
If you mix percentages and pixels, use box-sizing
Themeing All development Manually An element with 100% width and 2px padding will actually use more than 100% of the available space. Don't hack around this problem by setting percentages to be slightly smaller: instead use box-sizing: border-box to change this behaviour to what you'd expect).
231)
Extra CSS files should only define specific stylings
Stability All development Manually CSS defined outside global.css and nocache.css must not have wide-sweeping effects (it must be modular and target specific elements that would not normally exist on arbitrary screens).
232)
Do not float everything
Stability Product development only Manually Do not use floats unless doing it to make stuff sit next to something else.
Do not float stuff just to make it not overlap with another float.
Use a float-surrounder CSS class like Composr does (i.e. overflow: hidden) around stuff that is floated and what it is floated next to, to contain them without affecting other content. It is a much better stable solution. The only exception is when positioned CSS layers are present, in which case use our clearfix CSS class.
233)
Do not use a CSS reset or overly-broad rules
Themeing All development Manually Do not use a CSS reset (e.g. * { margin: 0; padding: 0 }): Composr does not use a CSS reset in its default templates so you would need to re-theme all Composr screens to re-apply default spacings if you used a CSS reset. If you must use a CSS reset then it will need adjusting to be scoped beneath a particular position(s) in the DOM tree.

Similarly, don't apply overly-broad styles thinking you'll undo them for any case that does not match – any broadly defined style must be a very general one, because you won't want to add dozens of exceptions back in to cater for the pre-coded HTML usage that Composr has in its myriad of templates.

A common example broad styling you should avoid is removing all bullets and spacings from default lists.
234)
Use meaningful naming for CSS classes
Technical debt Product development only Manually Name CSS classes according to what they are for and not what they do (e.g. big_and_red_page_icon is a terrible name, but warning_icon is a good one).
235)
Don't use the clear property
Themeing Product development only Manually We know this is a very common technique, but we don't use it in Composr. Instead, wrap all your floats inside…

Code (HTML)

<div class="clearfix">
(floats)
</div>
 
It works a lot better, is easier to understand, and the way our theme is built, it's necessary.
236)
Put your CSS neatly into the most appropriate CSS file
Consistency, performance Composr development + Manually Don't put all your code into global.css. If you have lots of styles relating to a new feature, give a neat comment formatted exactly the same as other formats in the file.
237)
Don't leave cruft lying around
Technical debt, performance Composr development + Automated test sync_tests/css_file, or Firefox "Dustme selectors" addon Don't leave old unused styles in the CSS files.
238)
Don't break the Theme Wizard
Themeing Product development only Automated test async_tests/missing_colour_equations If colours are added to the CSS then a proper Theme Wizard colour equation should also be added (see how it works for other colours for examples). The best thing to do is usually to re-use an existing colour definition – not only does that make the colour palette consistent, it stops you needing to calculate a new equation. We made our calculations using Photoshop, by adding white/black layers above the seed color and adjusting its opacity.
/*{$,hardcoded_ok}*/ may be put next to a colour for very rare exceptions.
239)
Never allow default border colours
Themeing: the Theme Wizard needs to keep working Product development only Manually The default border colour is black, which doesn't look great on our default theme, but may look far worse on other themes. Always set a colour for borders and follow the above rule so that the Theme Wizard can set the colour. To find a border colour, just grab the code from an existing bit of CSS that performs a similar styling to what you want.
240)
Use relative font sizes
Accessibility Product development only Automated test sync_tests/web_resources We want output to be easily scalable, so write sizes using em. It's really easy: if you want something smaller, use something like 0.85em and something bigger like 1.1em. The advantage to scaling is a themer can easily knock the whole theme's font sizing up, and also it is possible to proportional down-scale content for embed contexts. There is a tool to convert the default theme to px units for designers who prefer it (professional designers have good reason to want to do pixel-perfect designs).
241)
Front-end designs should work responsively
Mobile, Compatibility with user computers Composr development + Manually, Mobile Usability tests in Google Search Console
242)
Use BETA_CSS_PROPERTY instead of vendor prefixing
Future proofing Composr development + Automated test async_tests/css_beta Rather than setting up multiple vendor prefixes for CSS properties that need them, use the {$BETA_CSS_PROPERTY,...} symbol around just one normal call to the CSS property as it would ideally be written in the future. This allows PHP code to automatically add vendor prefixing and make dynamic modifications should there be some kind of standards change in the future. Don't mark stuff beta if it works unprefixed on all supported browsers.

JavaScript

We broadly follow the same kind of conventions other JavaScript developers do, but with a focus on reasonably modern ways of doing things wherever possible.
JavaScript has been historically a very clunky language with a messy history of new innovations that get constantly recycled as the basic platform moves forward or developers fight over approaches. We've designed a framework that moves beyond all this as much as possible. Our framework is broadly post-jQuery (although we do have jQuery bundled for third-party code) and post-preprocessor (because JavaScript is more powerful on its own than it was in the past) and polyfill-charged.

You do not need to:
  1. define jsdoc fully
  2. minify scripts (Composr does it for you)
  3. make third-party libraries meet our particular choice of standards (we trust third-party developers to have their own methods for ensuring standards are high) [exclude JavaScript libraries from automated testing by putting /*{$,parser hint: pure}*/ at the top of the file]

Standard Reason Scope Test method Notes
243)
Stability checks defined for ESLint
Code quality Product development only ESLint The .eslintrc.json file defines various checks for code quality. As ESLint documents all their rules and the reasoning for them, we won't document them again here.
244)
Code formatting
Consistency Product development only Manually Generally code is formatted to ESLint-preferred standards, with spaces around operators, camelCase for identifiers rather than underscores, and we place function opening braces on the same line as the function declaration. For functions assigned to a variable we put a space between the function keyword and opening parenthesis (function (foo)). Rather than us prescribing all the rules we bundle an ESLint configuration.
245)
Follow framework standards
Consistency Composr development + Manually The JavaScript framework defines a way of writing objects, hooking in functionality (via data attributes, views, and behaviors), naming variables, querying the DOM tree, and lots of other helper methods – follow the existing standard of the code.
246)
Use polyfills
Future proofing Composr development + Manually We want to align to future versions of JavaScript. If we want to benefit from future functionality then we typically will add a polyfill for that (loaded up in global.js) rather than adding a compatibility layer in the framework or implementing an alternative. This allows us to easily remove the polyfill later when we can, and also let's us get better performance.
247)
Do not use inline JavaScript
Security Composr development + Automated test sync_tests/_template_previews We use CSP to avoid XSS vulnerabilities, so inline JS is blocked.
248)
Do not use synchronous XmlHttpRequest
Standards compliance All development Browser errors in console Browsers are strongly advocating against using this for performance reasons, so we must use more sophisticated JavaScript-side form submit processing rather than traditional returning of false.
249)
Fix any strict warnings
Standards compliance, future proofing Product development only JavaScript files should have use strict; in, automated test (js_strict_mode Our ESLint rules enforce some type strictness, with the exception that comparison to null is weak and will intentionally also match undefined.
250)
Don't rely on window.focus / window.print / window.scrollTo / window.scrollBy / window.resizeTo / window.resizeBy / window.moveTo / window.moveBy existing
Stability Composr development + Manually Pop-up blockers may remove this functionality, setting them to null. You must catch exceptions over the calls.
251)
It's written JavaScript not Javascript or ECMAScript
Standards compliance Product development only Manually A minor point, but if we capitalise our technology names incorrectly we look less professional. While JavaScript is standardised as ECMAScript, we all still call it JavaScript in practice.
252)
Name DOM nodes variables as el and events as e
Consistency Product development only Manually To avoid confusion don't do it the other way around.
253)
Use standard system for reading function parameters
Consistency Product development only Manually The version of JavaScript we currently code for has no feature for default function parameters (they automatically default to undefined). It also has no simple casting system. We therefore have implemented a standardised way of doing this within the library.
254)
Reference language strings in a safe way
Stability All development Automated test async_tests/js_lang_references Any language strings that are not in either global.ini or critical_error.ini must be referenced using the full {!file:CODENAME} syntax. This is because we cannot assume what language files have already been explicitly loaded before any individual JavaScript file is evaluated.
255)
Be careful with global functions and variables
Stability Composr development + Automated test sync_tests/web_resources Generally do not use globals, but if you must then reference global variables and functions that are defined outside any particular .js file by explicitly prefixing them with window.. This allows JavaScript code scanners to detect undeclared variables because there's no longer an ambiguity.
256)
Use href="#!" for JS-controlled links
Stability Product development only Manually This is a special value used by Composr to indicate "prevent default" that you can make use of. There's a global listener for all clicks which finds such links and calls e.preventDefault() so to not make the screen jump. href="#!" is used because href="#" could have valid uses like "jump to top".
257)
Make images relative
Stability Product development only Automated test async_tests/js_standards Calls to {$IMG} must be wrapped with $util.srl(). This is because they need to be turned from absolute URLs to relative URLs, just in case a JavaScript file is cached under HTTP but used under HTTPS (it would generate a security error).
258)
Use relative base URLs
Stability Product development only Automated test async_tests/js_standards Use the relative version of the base URL ($util.rel($cms.getBaseUrl())), just in case a JavaScript file is cached under HTTP but used under HTTPS (it would generate a security error) – or if the JavaScript is working across multiple domains (another potential security error). When Composr runs on multiple domains all URLs will work on all domains, with the exception of page URLs.
259)
Use relative script URLs
Stability Product development only Manually Use {$FIND_SCRIPT_NOHTTP} not {$FIND_SCRIPT}, just in case a JavaScript file is cached under HTTP but used under HTTPS (it would generate a security error) – or if the JavaScript is working across multiple domains (another potential security error). When Composr runs on multiple domains all URLs will work on all domains, with the exception of page URLs.
260)
Do not encode page-links directly in JavaScript files
Stability Product development only Automated test async_tests/js_standards Use of {$PAGE_LINK} will encode keep_ parameters, including potential session IDs. It must therefore be passed into JavaScript via an HTML data- attribute, or you must generate with the {$PAGE_LINK} parameter to avoid keep_ parameters and manually attach them using $cms.keep() (being very careful on whether the existing URL does or doesn't have a ? in already, which may vary by URL scheme).
261)
Do not break bfcache
Performance Composr development + Manually Do not use JavaScript beforeunload and unload events, as it will turn off the bfcache in browsers that support it (particularly Firefox).
262)
Do form validation on button click not form submission
Framework Composr development + Manually Composr may attach handlers to form submission to change how forms are submitted (e.g. submit via AJAX, or submit with the ModSecurity workaround), while validation is reserved for placing on buttons. Pressing enter on a form field activates the button as per normal JavaScript behaviour, so this is a reliable approach. Where appropriate use the doCheckingCMSFormSubmitChain framework to do basic validation and tie in your own validation.
263)
Access DOM structure using normal properties
Consistency Composr development + Manually The DOM binding is well documented so you do not need to use getAttribute in most cases to access an HTML attribute value. For example, use form.action rather than form.getAttribute('action'). For this to work, you must never name a form element with the same name as a DOM property (e.g. target); the form_reserved_names automated test will verify this.
264)
Access form elements using elements array by name
Consistency Composr development + Manually There are multiple ways to access elements of a known form object. Use form.elements['foo'] not form.foo or form.elements[3] or form.elements.foo. This makes your code easier to understand.
265)
Use modern event handling
Consistency Composr development + Manually Traditional event handlers only allow attaching a single event, e.g. attaching an onclick property in the DOM. For consistency and flexibility, use addEventListener or wrapping functions within the Composr JavaScript framework. An allowed exception is if your JavaScript has complete authority over the HTML the event handler is being attached to. Further, for consistency and clarity, use e.preventDefault(); to cancel an event, rather than returning a boolean.

Images

Standard Reason Scope Test method Notes
266)
Do not refer to theme images directly by URL
Themeing: the webmaster may change image mappings Product development only Manually Bundled images should be referred to via the theme image system, not by direct URL.
267)
Usually use SVG (or PNG) files
Performance Composr development + Manually Use SVG images in most cases, PNG images in rarer cases (e.g. avatars and emoticons), GIF when animation is needed (with a APNG versions too), or JPEG if there is zero chance of the image needing any transparency and never likely to be edited again (i.e. only use it at a post-development optimisation stage) and it compresses better as JPEG.
268)
Specify image dimensions in markup/CSS
Performance, stability Composr development + Manually You should not assume any image will be automatically placed by the web browser at some particular size. Explicitly set sizes using HTML width/height attributes or the CSS background-size property. This both helps performance, and decouples the need for (auto-scalable) SVG files to be saved with a particular default size.
269)
Organise theme images neatly
Consistency Product development only Manually, Automated test async_tests/theme_images Square theme images generally should be classified somewhere under the icons theme image directory. All icons must be square. Generally try and keep a neat directory structure, rather than littering theme images at the top level.
270)
Icons are multi-purpose
Themeing Product development only Automated test first_tests/modularisation) You will likely create new icons for very specific purposes. However, icons (especially SVG icons) are also an excellent resource for webmasters to use in their designs (regardless of what addons they've installed). For this reason every icon must be packaged both for the specific addon it is associated with, and the general purpose core_all_icons addon.
271)
Compress images and strip gamma from PNG files
Performance, compatibility Composr development + Automated test async_tests/image_compression, Automated test async_tests/theme_images Pass PNG images through a compression program such as PNGGauntlet or PNGThing, and strip gamma settings in the process (needed for colour compatibility on some browsers). Strip any unneeded code from SVG files.
272)
Use inlining correctly
Performance Composr development + Manually If an image is referenced in the CSS and used very commonly, consider using {$IMG_INLINE,...} so that it loads without any additional HTTP request.
273)
Supply hi-dpi images as appropriate
Performance Composr development + Manually Wherever appropriate use .svg images. If raster images must be used then supply both normal and hi-dpi ("retina") images. Do this using the HTML srcset attribute.
274)
Good contrast for icons
Design quality Composr development + Manually Icons need to look good when laid over common pale and dark backgrounds, within reason.

Miscellany

Testing

Standard Reason Scope Test method Notes
275)
Design tests first or write automated tests after
Testability, technical debt Composr development + Manually Before writing any code, you could design/document the tests you are going to run to make sure your written code works. Think of how it might break under situations you might have otherwise forgotten about (e.g. what if a guest user is running your module) and add in tests to test that it doesn't break. Use your judgement.

Documentation

If you make a Composr tutorial that doesn't meet the normal rules (e.g. a video tutorial), then you can still add it in. There's a feature on the composr.app website to link it through.

Standard Reason Scope Test method Notes
276)
Provide documentation for new features
Usability Product development only Manually After your code is written and tested make sure that the user documentation for Composr is updated (edit a tut_* file in docs/pages/comcode_custom/EN, or create a new tutorial).
277)
List new features
Communication Product development only Manually If you've added a non-trivial feature it will need to be added to the Composr website's feature-list eventually (cms_homesite_featuretray, and also announced.
278)
Provide addon screenshots
Communication Composr development + Automated test async_tests/addon_screenshots Every non-bundled addon that is non-development addon must have a screenshot in data_custom/images/addon_screenshots.
279)
Use standardised navigation paths
Communication: consistency Product development only Manually, Automated test async_tests/tutorial_nav_paths Navigation paths should be a bit like breadcrumbs starting from the Admin Zone, e.g. "Admin Zone > Setup > Configuration" or "Admin Zone > Content > Downloads"; or breadcrumbs on the default menu written like "Content > Quizzes on the default menus". Don't describe the navigation paths using prose.
280)
Update these standards
Communication Product development only Manually If you have introduced a new coding standard make sure to update this document too!

General guidelines

Standard Reason Scope Test method Notes
281)
Think of Composr as a whole, keep it consistent
Consistency Product development only Manually For example, Chris once found that XMLHttpRequest on Internet Explorer had a caching bug… so instead of just throwing in a solution to where it affected him, he considered the problem architecturally, making sure all AJAX-facing PHP scripts have a common approach to solving the problem. This kind of approach is essential.
282)
Properly index 3rd-party code/APIs
Legal, ethics, maintainability, communication, stability Product development only Manually If you include some third party code/API you must update:
- docs/LICENSE.md (this file is shown when users install)
- sources_custom/third_party_code.php (general list of files)
- phpdoc.dist.xml (if appropriate, PHPDoc generation)
- data/maintenance_sheet.csv (if appropriate, non-maintained systems)
- data_custom/third_party_code.csv (spreadsheet of SDKs/libraries used, except very tiny pieces of third-party code we've fully integrated)
- data_custom/third_party_apis.csv (spreadsheet of APIs used, except those covered by third_party_code.csv)
- .eslintignore (files for ESLint to ignore)
- .phpcs (files for PHP_CodeSniffer to ignore)

Some general advice regarding (re)integrating libraries:
- When getting from GitHub, we generally use the latest 'Release' rather than latest clone, but it varies – and the version number is whatever is given, even if fixes were made since it was changed
- When running Composer to download dependencies: For the "No composer.json in current directory, do you want to use the one at" questions, answer 'N'
- We don't try and force third party libraries to work with ocProducts PHP type strictness, instead we disable/reenable it around library calls
- Often you'll need to open up _tests/index.php?id=unit_tests%2Fstandard_dir_files&debug=1 to generate index.html and .htaccess files, as many projects do not ship these files
- Run find some_dir -type f | sort find the new file list for the addon_registry hook
283)
Update maintenance sheet
Communication Product development only Manually If you are making functionality that will be costly to maintain for some reason (e.g. dependency on third-parties, or fragile code) then it should either:
  1. be added to maintenance_status.csv for responsibility tracking (mandatory for bundled addons)
  2. a Health Check hook (where an API key is involved)
  3. an automated test (where no API key is involved)
  4. have a FRAGILE comment in the code (where automated testing is not possible)
284)
Use text-based file formats
Revision control Composr development + Manually Use text-based file formats rather than binary documents (like xlsx or docx). Revision management and global search and replace works much better with text files.

Use of the Composr Git repository

Standard Reason Scope Test method Notes
285)
Use standardised commit messages
So that the tracker and Git are bound together. Also so that typos don't generally happen in Git commit messages, given all the custom text is in the tracker issue. Composr development Git hooks The message for any commit on a stable branch must start with either "Fixed MANTIS-<tracker-ID>" or "Implementing MANTIS-<tracker-ID>" or "Implemented MANTIS-<tracker-ID>" or "Security fix for MANTIS-<tracker-ID>" or "New build" (for updating metadata files around a new release) or "Merge branch" (for a merge). Multiple commits may reference the same tracker issue. When doing a clone get the hooks working via git config core.hooksPath git-hooks; chmod +x git-hooks/* (Linux/Mac OS – perform whatever equivalent action for Windows). When fixing bugs you can use the "push bugfix" Admin Zone module to automatically create appropriate Git commits, tracker issues, and hotfixes. Note that this guideline does not apply for alpha branches, as commits tend to be more chaotic when major project reworkings are under way, and the commit log is also going to be under less scrutiny.
286)
Granular commits
Changelogs are auto-generated from Git history for new releases. Composr development Manually Any commit on a stable branch should fix one issue only (tip: use the Git patch technique if a file contains changes that should be made across multiple commits).
287)
Use temporary branches and commit regularly
  1. Entanglements occur when multiple developments are happening in parallel (changes to the same files), which is unavoidable when working over an extended period of time. The isolation capabilities of branches makes things much more manageable with only the small overhead of creating and merging these branches.
  2. Being able to make regular commits is useful for many reasons, but if it's not on a branch it looks like a messy chain of consciousness scattered within a wider sea of other work. The aforementioned reasons include getting backups, delineating (logging) clear steps within the development, and being able to share your progress with others at relatively stable stopping points.
Composr development Manually If working on something for extended periods of time (days or longer), use a Git branch for it. That branch may or may not be pushed upstream, this is your choice depending on whether collaboration is required while it is developed as a separate branch. By the way, it's your personal choice whether to use merge or rebase.
288)
Appropriate branch naming
Consistency Composr development Automated test async_tests/sensible_git_branches The current stable version branch should be in the v11 branch, and any other version branch should be in v<version-number>.

Agency standards

Standard Reason Scope Test method Notes
289)
Do not use *_custom dirs for code that will make it into Composr
Ease of upgrading Projects Manually If you make some changes which are being merged back into Composr, put them where they will be in the final Composr. That will make large future upgrades easier.
It will also make patch upgrades harder, but for most client projects you'll be hand-patching only relevant fixes as fixing every minor issue is less of a concern than implementing and maintaining new features.
290)
Add new CSS/templates/images into the default theme *_custom dirs
Deployment, collaboration Projects Manually If you decide to switch themes, or preview from the Admin Zone, you want to know your functionality will still work.

Feedback

Please rate this tutorial:

Have a suggestion? Report an issue on the tracker.