Code Book, part 4 (Coding Standards)
Written by Chris Graham
« Return to Code Book table of contentsIntroduction
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 (basic_code_formatting.php) | 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 (basic_code_formatting.php) | We work in Unix line-ending format, not Windows. |
20) No trailing whitespace |
Consistency | Product development only | Automated test (basic_code_formatting.php) | 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 (basic_code_formatting.php) | Many programs are not compatible with byte-order-markers. |
22) Use ASCII |
Stability | Composr development + | Automated test (basic_code_formatting.php) |
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 (_override_issues.php) |
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) 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. |
31) 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. |
32) 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. |
33) 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. |
34) 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!). |
35) 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. |
36) 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. |
37) 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. |
38) 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. |
39) 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]. |
40) Never start a file <? |
Portability | Composr development + | Code Quality Checker | PHP short_tags option may be off. |
41) 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. |
42) 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. |
43) 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. |
44) 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 /). |
45) 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. |
46) 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. |
47) 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. |
48) 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. |
49) UPPER_CASE should be used for globals and constants |
Consistency | Composr development + | Code Quality Checker | |
50) Support minimum PHP version |
Portability | Product development only | Code Quality Checker | The version listed in Composr's minimum requirements must be supported. |
51) 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. |
52) 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). |
53) 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. |
54) 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. |
55) 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). |
56) 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). |
57) Document cookies |
GDPR | Composr development + | Document any cookies in a privacy hook, so that the privacy policy may be generated correctly. | |
58) Template GUIDs must be unique |
Themeing | Product development only | Release scripts | Don't copy and paste GUIDs in template calls. If you copy and paste a line with a GUID ('_GUID' => 'blahblahblah'), remove it completely from your new line. Our automated scripts can put one back in later. 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. |
59) Put standard index.html and .htaccess files in new directories |
Security: stops probing/tunnelling | Composr development + | Automated test (standard_dir_files.php) | 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). |
60) 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. |
61) 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). |
62) 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. |
63) Handle errors in failure-prone function calls (e.g. fopen) |
Stability | Product development only | Code Quality Checker, pedantic mode | |
64) 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. |
65) 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. |
66) 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. |
67) 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. |
68) 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. |
69) 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). |
70) 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. |
71) Don't write code that'll output PHP 'notice' errors |
Stability | Composr development + | Composr makes all PHP notices generate stack traces | |
72) Allow initialisation functions to run twice |
Stability | Product development only | Automated test (_override_issues.php) | 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. |
73) 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. |
74) 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. |
75) 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 |
---|---|---|---|---|
76) 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. |
77) 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. |
78) 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. |
79) 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. |
80) 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 |
---|---|---|---|---|
81) 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. |
82) All files must be associated with an addon |
Modularity | Product development only | Automated test (modularisation.php) | 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. |
83) Manage any interdependencies between addons |
Modularity: so addons may be uninstalled freely | Product development only | Manually, Automated test (addon_references.php) helps a bit, Automated test (addon_guards.php) 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). |
84) Hooks should not crash for missing dependencies |
Integrity | Product development only | Automated test (addon_guards.php) | Hooks should not crash for missing dependencies (including the files owner addon), they should just exit cleanly. |
85) Blocks should not crash for missing dependencies |
Integrity | Product development only | Automated test (addon_guards.php) | 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. |
86) Modules should not crash for missing dependencies |
Integrity | Product development only | Automated test (addon_guards.php) | 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. |
87) Entry-point scripts should not crash for missing dependencies |
Integrity | Product development only | Automated test (addon_guards.php) | 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 |
---|---|---|---|---|
88) 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. |
89) Put your code in a function named <filename>_script() |
Consistency | Product development only | Manually | |
90) Call prepare_backend_response |
Internationalisation | Product development only | Manually | This function will do character set conversions, and put out appropriate HTTP headers. |
91) 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 |
---|---|---|---|---|
92) 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. |
93) 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. |
94) 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. |
95) 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. |
96) 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 |
---|---|---|---|---|
97) 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. |
98) 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. |
99) 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. |
100) 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 ://. |
101) 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). |
102) 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. |
103) 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(). |
104) 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). |
105) 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. |
106) 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. |
107) 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 |
---|---|---|---|---|
108) 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. |
109) 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. |
110) 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"!) |
111) 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(). |
112) Use sensible database indices |
Performance | All development | Manually | Create database indexes if you regularly query a table using non-key field(s). |
113) 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). |
114) 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. |
115) Avoid complex SQL |
Portability | Product development only | Automated test (_installer_xml_db.php, database_unsupported_sql.php) | You cannot use SQL syntax that is not widely supported, unless the db_function function supports it. Expressions on aggregate functions are not allowed. |
116) 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). |
117) ]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 (_installer_xml_db.php), table creation code checks | You will have been forced to choose field names that don't require escaping anyway. |
118) In queries use <> rather than != |
Portability | Product development only | Manually | Not all databases support the latter syntax. |
119) Use db_string_equal_to |
Portability: Oracle | Product development only | Automated test (database_unsupported_sql.php) | 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')). |
120) Keep your queries as simple as possible |
Portability | Product development only | Manually | |
121) 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. |
122) 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. |
123) 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. |
124) 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). |
125) 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. |
126) 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. |
127) 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. |
128) 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. | |
129) Define privacy hooks, Manually |
GDPR | Composr development + | Automated test (privacy_hooks.php) | 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 |
---|---|---|---|---|
130) 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. |
131) 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. |
132) 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 |
---|---|---|---|---|
133) 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). |
134) 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. |
135) 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. |
136) 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). |
137) 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 |
---|---|---|---|---|
138) 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. |
139) 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. |
140) 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. |
141) Implement screen previews for new templates and maintain them with template changes |
Testability | Product development only | Automated test (template_previews.php) | Previews allow themers to confirm a theme works on all screens, and allow us to do mass-checks of HTML. |
142) 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. |
143) 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. |
144) Prefix mail templates (Comcode) with MAIL_ |
Easy to relate templates to features | Composr development + | Manually | |
145) 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. |
146) 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 |
---|---|---|---|---|
147) …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. |
148) …COLUMNED_TABLE |
Consistent UI | Composr development + | Manually | Templates for rendering a plain table of something. |
149) …INDEX_SCREEN / INDEX_SCREEN_ENTRY |
Consistent UI | Composr development + | Manually | A screen having a shortish paragraphed sequence of things to choose. |
150) …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. |
151) …CONFIRM_SCREEN |
Consistent UI | Composr development + | Manually | Show a preview and confirm it's okay. |
152) …PAGINATION_* |
Consistent UI | Composr development + | Manually | Very simple browsing through multiple screens. Usually you'll want to use pagination instead. |
153) …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 |
---|---|---|---|---|
154) …pagination function |
Consistent UI | Composr development + | Manually | An interface for pagination. |
155) …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 |
---|---|---|---|---|
156) 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. |
157) 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. |
158) 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. |
159) 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. |
160) 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. |
161) 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 |
---|---|---|---|---|
162) 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). |
163) 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. |
164) 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). |
165) 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 |
---|---|---|---|---|
166) List block parameters |
Usability for webmasters | Product development only | Automated test (blocks.php) |
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 |
---|---|---|---|---|
167) 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. |
168) 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". |
169) 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. |
170) 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 – is technically wrong, as a hyphen is not a dash). |
171) Use terminology with consistency |
Usability | Product development only | Automated test (lang_spelling.php) | 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." . |
172) Don't pollute global.ini |
Technical debt | Product development only | Manually, Automated test to limit .ini file size (lang_ini_size.php) | 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. |
173) Most lang strings support HTML, some do not: be aware |
Stability | Product development only | Automated test (lang_html_safe.php) | 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. |
174) 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. |
175) Don't use the name "Composr", use the phrase "the software" |
Debranding | Product development only | Automated test (lang_spelling.php) | |
176) Use <kbd> within language strings as appropriate |
Usability | Product development only | Manually | Use this for something typed, or for code. |
177) Use our standard terminology |
Consistent UI | Product development only | Manually | Use correct terminology like 'Codename', 'Option', 'Title', 'Description'. |
178) End form field descriptions with a full stop |
Consistent UI | Product development only | Composr development mode | |
179) Encode pluralisation in language strings |
Usability | Composr development + | Automated test (lang_misc.php) |
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. |
180) Encode article+noun vowel rule correctly in language strings |
Usability | Composr development + | Automated test (lang_misc.php) |
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. |
181) Use Oxford Commas |
Usability | Product development only | Manually | Use the Oxford Comma. |
182) Define administrative split |
Internationalisation: ease of | Product development only | Automated test (lang_administrative_split.php) | 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. |
183) 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. |
184) 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. |
Respect the Queen of England , but also do not confuse Americans…
Standard | Reason | Scope | Test method | Notes |
---|---|---|---|---|
185) Do not use American spellings such as color or center or license |
Consistent UI | Product development only | Manually, Automated test in verbose mode (lang_spelling.php) | 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. |
186) 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). |
187) Say "tick (check)" rather than just "tick" or just "check" |
Internationalisation | Product development only | Automated test (lang_spelling.php) | 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)". |
188) 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 |
---|---|---|---|---|
189) For an add button, use the title text ("Add xxx") |
Consistent UI | Product development only | Manually | |
190) 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"). |
191) 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"). |
192) For an intermediary button, use the language string PROCEED ("Proceed") |
Consistent UI | Product development only | Manually | |
193) For sorting, use the language string SORT ("Sort") |
Consistent UI | Product development only | Manually | |
194) 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 |
---|---|---|---|---|
195) Use tabs for HTML code |
Performance | Product development only | .editorconfig / Automated test (basic_code_formatting.php) | 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). |
196) 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}} |
197) 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. |
198) Align Tempcode-guarded parameters consistently |
Consistency | Product development only | Manually |
We write code like: 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. |
199) 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. |
200) Meet WCAG (Web Content Accessibility Guidelines) and ATAG (maintenance status) |
Accessibility | Composr development + | Automated test (template_previews.php) | 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. |
201) No deprecated tags or attributes |
Accessibility | Composr development + | Automated test (template_previews.php) | 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. |
202) Write to XHTML5 |
Accessibility | Product development only | Firefox HTML validator extension / Automated test (template_previews.php) | 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. |
203) Don't make assumptions about word order by coding things like {!GO} {!HERE} |
Internationalisation | Product development only | Manually | Word order in languages may vary. |
204) 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). |
205) Avoid the style attribute ("inline styles") |
Technical debt | Product development only | Automated test (template_previews.php) | Don't use the HTML style attribute except where you need to pass in styles contingent on template parameters. |
206) 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). |
207) 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. |
208) Use {!LINK_NEW_WINDOW} |
Accessibility | Composr development + | Automated test (template_previews.php) | 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. |
209) 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 |
---|---|---|---|---|
210) 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. |
211) 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. |
212) 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 |
---|---|---|---|---|
213) 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. |
214) 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. |
215) 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 |
---|---|---|---|---|
216) Use comment encapsulation |
Tooling: syntax highlighting | Product development only | Automated test (comment_encapsulation.php) | Tempcode directives should use comment encapsulation so that syntax highlighting in text editors works. |
217) 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 |
---|---|---|---|---|
218) 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. |
219) Use hyphens to split words |
Consistency | Product development only | Manually | By mainstream contention we use hyphens to split words, not underscores or camelCase. |
220) Use tabs for CSS code |
Consistency | Product development only | .editorconfig / Automated test (basic_code_formatting.php) | To keep things tidy we use tabs for indentation rather than spaces. |
221) 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. |
222) Use reliable CSS |
Compatibility with user computers, Standards compliance | Product development only | Automated test (template_previews.php) | Only use standards-compliant CSS that works across all supported browsers. Test on all the browsers that we support. |
223) 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. |
224) 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. |
225) 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). |
226) 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). |
227) 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. |
228) 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. |
229) 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). |
230) 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… It works a lot better, is easier to understand, and the way our theme is built, it's necessary. |
231) 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. |
232) Don't leave cruft lying around |
Technical debt, performance | Composr development + | Automated test (css_file.php), or Firefox "Dustme selectors" addon | Don't leave old unused styles in the CSS files. |
233) Don't break the Theme Wizard |
Themeing | Product development only | Automated test (missing_colour_equations.php) |
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. |
234) 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. |
235) Use relative font sizes |
Accessibility | Product development only | Automated test (web_resources.php) | 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). |
236) Front-end designs should work responsively |
Mobile, Compatibility with user computers | Composr development + | Manually, Mobile Usability tests in Google Search Console | |
237) Use BETA_CSS_PROPERTY instead of vendor prefixing |
Future proofing | Composr development + | Automated test (css_beta.php) |
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:
- define jsdoc fully
- minify scripts (Composr does it for you)
- 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 |
---|---|---|---|---|
238) 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. |
239) 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. |
240) 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. |
241) 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. |
242) Do not use inline JavaScript |
Security | Composr development + | Automated test (template_previews.php) | We use CSP to avoid XSS vulnerabilities, so inline JS is blocked. |
243) 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. |
244) Fix any strict warnings |
Standards compliance, future proofing | Product development only | JavaScript files should have use strict; in, automated test (js_strict_mode.php) | Our ESLint rules enforce some type strictness, with the exception that comparison to null is weak and will intentionally also match undefined. |
245) 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. |
246) 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. |
247) 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. |
248) 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. |
249) Reference language strings in a safe way |
Stability | All development | Automated test (js_lang_references.php) | 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. |
250) Be careful with global functions and variables |
Stability | Composr development + | Automated test (web_resources.php) | 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. |
251) 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". |
252) Make images relative |
Stability | Product development only | Automated test (js_standards.php) | 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). |
253) Use relative base URLs |
Stability | Product development only | Automated test (js_standards.php) | 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. |
254) 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. |
255) Do not encode page-links directly in JavaScript files |
Stability | Product development only | Automated test (js_standards.php) | 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). |
256) 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). |
257) 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. |
258) 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. |
259) 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. |
260) 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 |
---|---|---|---|---|
261) 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. |
262) Usually use SVG (or PNG) files |
Performance | Composr development + | Manually, Automated test (svg_quality | 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. |
263) 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. |
264) Organise theme images neatly |
Consistency | Product development only | Manually, Automated test (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. |
265) Icons are multi-purpose |
Themeing | Product development only | Automated test (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. |
266) Compress images and strip gamma from PNG files |
Performance, compatibility | Composr development + | Automated test checks compression ratios (image_compression.php), Automated test checks SVG quality (theme_images.php) | 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. |
267) 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. |
268) 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. |
269) 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 |
---|---|---|---|---|
270) 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 |
---|---|---|---|---|
271) 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). |
272) 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.php), and also announced. |
273) Provide addon screenshots |
Communication | Composr development + | Automated test (addon_screenshots.php) | Every non-bundled addon that is non-development addon must have a screenshot in data_custom/images/addon_screenshots. |
274) Use standardised navigation paths |
Communication: consistency | Product development only | Manually, Automated test (tutorial_nav_paths.php) | 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. |
275) 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 |
---|---|---|---|---|
276) 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. |
277) 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 |
278) 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:
|
279) 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 |
---|---|---|---|---|
280) 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. |
281) 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). |
282) Use temporary branches and commit regularly |
|
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. |
283) Appropriate branch naming |
Consistency | Composr development | Automated test (sensible_git_branches) |
The current stable version branch should be in the master branch, and any other version branch should be in v<version-number>. |
Agency standards
Standard | Reason | Scope | Test method | Notes |
---|---|---|---|---|
284) 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. |
285) 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.