# Coding Standards

## Coding Standards

Due to the fact that the Elentra code base has been continually evolving, some of the existing source code does not necessarily adhere to the Elentra Platform Coding Standards defined below. If you encounter something that does not follow the coding standards, it is our hope that you (as a developer and contributor) will update and test the files accordingly.

{% hint style="warning" %}
**Important Note** If you do not like something about our coding standards, please don't be passively adoptive. We are certainly willing to listen and potentially adjust providing that:

1. You are willing and able to clearly explain and defend your well thought out position.
2. You are willing to do something to help implementing your ideas.
3. You can convince the majority of community to agree with your position.

Thank you for helping to make the Elentra Platform development experience a good one.
{% endhint %}

## PHP Standards

Our direction is to adopt the wildly accepted PSR standards by the [PHP Framework Interop Group](https://www.php-fig.org):

* [PSR-1: Basic Coding Standard](http://www.php-fig.org/psr/psr-1/)
* [PSR-12: Extended Coding Style Guide](https://www.php-fig.org/psr/psr-12)

### Source Code Documentation

All source code documentation blocks ("docblocks") must be compatible with the phpDocumentor format. Describing the phpDocumentor format is beyond the scope of this document, but for more information visit [http://phpdoc.org](http://phpdoc.org/).

All source code files written for Elentra or that operate within the platform **must** contain a "file-level" docblock at the top of each file, as well as a "class-level" docblock immediately above each class.

Below are examples of such docblocks.

{% hint style="info" %}
**Please Note** The sharp, `#`, character should never be used to start source code comments, instead use: `//` or `/* */` format.
{% endhint %}

### **Files**

Every file that contains Elentra PHP code must have a "file-level" docblock at the top of the file that contains these phpDocumentor tags at a minimum:

```
/**
 * Elentra ME [https://elentra.org]
 *
 * Copyright 2020 Queen's University or its assignee ("Queen's"). All Rights Reserved.
 *
 * This work is subject to Community Licenses ("CL(s)") between Queen's and its various licensee's,
 * respectively, and may only be viewed, accessed, used, reproduced, compiled, modified, copied or
 * exploited (together "Used") in accordance with a CL. Only Queen's or its licensees and their
 * respective Authorized Developers may Use this work in accordance with a CL. If you are not an
 * Authorized Developer, please contact Queen's University (at cla@elentra.org) or its applicable
 * licensee to review the rights and obligations under the applicable CL and become an Authorized
 * Developer before Using this work.
 *
 * General description of this file.
 * 
 * @author Organization: Your Organization Here
 * @author Developer: Developer Name Here <developer@emailaddress.here>
 * 
 */
```

### **Classes**

Every class must have a "class-level" docblock that contains these phpDocumentor tags at a minimum:

```
/**
 * Class Name or Title
 *
 * General description for this class.
 *
 * @author Organization: Your Organization Here
 * @author Developer: Developer Name Here <developer@emailaddress.here>
 */
```

### **Functions**

Every function, including object methods, must have a docblock that contains at a minimum:

* A description of the function
* All of the arguments
* All of the possible return values
* If a function/method may throw an exception, use "@throws"

  ```
  /**
   * This method does something interesting.
   *
   * @param Place $where Where something interesting takes place
   * @param int $repeat How many times something interesting should happen
   * @return bool
   */
  public function doSomethingInteresting(Place $where, $repeat = 1) {
      return true;
  }

  /**
   * This function (public static method) returns whatever it is passed.
   *
   * @param bool $state
   * @return bool
   */
  public static function another_interesting_thing($state = true) {
      return $state;
  }
  ```

### Code Formatting and General Rules

**General Formatting**

* All indenting is set to 4 spaces (no tabs).
* Keep indenting at the level of the HTML indent when mixing PHP and HTML, and vice versa.

  ```
  <div>
  <?php
  if ($foo) {
      ...
  }
  ?>
  </div>
  ```
* Do not increase or decrease indent for `<?php` or `?>` tags.
* Do not use PHP short tags (`<?` or `<?=`), instead use the full opening tag (`<?php` and `<?php echo`).

**Variables and Constants**

* Constants should always be declared in upper case

  ```
  define("CLERKSHIP_TOP_CATEGORY_ID", 49);
  ```
* Variables which are used between more than one file (what we call minor global variables) are also in all upper case

  ```
  $CLERKSHIP_REQUIRED_WEEKS = 14;
  ```
* Variables which are used within only one file should be in all lower case

  ```
  $title_suffix = " All Faculty";
  ```
* Variables and Constants with multi-word names should have each word separated with an underscore

  ```
  $clinical_presentations_list = false;
  ```
* Always validate `$_GET` and `$_POST` variables and place them in a `$PROCESSED` array for creating or editing records in databases or in regular variables for program logic

  ```
  /**
   * Required field "course_name" / Course Name.
   */
  if (isset($_POST["course_name"]) && ($course_name = clean_input($_POST["course_name"], ["notags", "trim"]))) {
      $PROCESSED["course_name"] = $course_name;
  } else {
      add_error("The <strong>" . $module_singular_name .  "Name</strong> field is required.");
  }
  ```

**If, Foreach and Switch statements**

* For `if`, `foreach`, and `switch` statements, include a space after the initial statement and after the closing parenthesis.
* For `if` statements with an `else` branch, include a space after the first closing brace bracket and after the `else` statement.
* For `if` statements with an `elseif` branch, include a space after the first closing brace bracket and after the `elseif` statement, and after the parenthesis.
* For `if`, `foreach`, and `switch` statements, include brace brackets, whether the code inside the statement is one line or more.
* Use `&&` and `||` in logic instead of `AND` and `OR`
* Layout `foreach` and `if` statements on at least 3 lines:
  * The statement, parameters, and open brace-bracket
  * A new line with code to be executed
  * One more line with a closing brace bracket.
* Layout `switch` statements with at least 5 lines:
  * same as above plus:
  * at least one `case "parameter" :` line before the code to be executed.
  * a `break;` line after the code to be executed.
* For `case` and `default` lines, include a space before the colon.
* End all `case` and `default` sections in `switch` statements with a `break;` line which is indented one tab less than the code to be executed.
* Indent all code within brace brackets by one additional tab (four spaces).
* Indent all code after a `case` or `default` line in a `switch` statement by one additional tab (four spaces).

  ```
  if ((($variable > 20) && ($variable < 50)) || $boolean) {
      echo "this code executed.";
  } elseif (($variable > 15) && ($variable < 60)) {
      echo "that code almost executed.";
  } else {
      echo "that code didn't execute.";
  }

  foreach ($names as $name) {
      echo $name;
  }

  switch ($status) {
      case "on" :
          turn_light_on();
      break;
      case "off" :
          turn_light_off();
      break;
      default :
          continue;
      break;
  }
  ```

**Functions and Methods**

* Create new global functions as `public static` methods in `core/library/Entrada/Utilities.php`.
  * Historical global functions are declared in `core/includes/functions.inc.php`.
* Separate multi-word function names with underscores.
* Define functions primarily used only in one module as methods of that module's class (e.g., `Entrada_Events::filter_events()` in `core/library/Entrada/Events.php`).
* Group like functions / methods together in the same file where possible.
* Include a space after the closing parenthesis and before the opening brace bracket in function declarations.

  ```
  /**
   * Determines whether or not a PHP session is available.
   *
   * @return bool
   */
  public static function is_session_started() {
      if ( php_sapi_name() !== "cli" ) {
          if ( version_compare(phpversion(), "5.4.0", ">=") ) {
              return session_status() === PHP_SESSION_ACTIVE ? true : false;
          } else {
              return session_id() === "" ? false : true;
          }
      }

      return false;
  }
  ```

## SQL Standards

* If the query you are writing is over 120 characters long you should add line breaks and align the query so `JOIN`, `WHERE`, `ON`, `AND`, `ORDER BY` and `GROUP BY` keywords start a new line and are indented to line up with the first statement of the query.
* All of the query except the field, table and database names should be in uppercase.
* To prevent SQL injection vulnerabilities, use prepared statements whenever possible, otherwise use ADOdb's qstr() method `".$db->qstr($variable)."` to add variables into queries.
* Avoid writing your own `INSERT` and `UPDATE` queries. Instead, use ADOdb's `AutoExecute()` method.
* Include SQL queries within models only, not controllers or views.
* Include back-ticks around field names in queries to prevent possible naming collisions.

### Using prepared statements

```
$query = "SELECT a.*, b.`group`, b.`role`
            FROM `".AUTH_DATABASE."`.`user_data` AS a
            LEFT JOIN `".AUTH_DATABASE."`.`user_access` AS b
            ON b.`user_id` = a.`id`
            WHERE b.`group` = ? 
            AND b.`role` = ? 
            ORDER BY a.`lastname`, a.`firstname` ASC";
$results = $db->GetAll($query, [$group, $role]);
```

### Using ADOdb qstr()

```
$query = "SELECT a.*, b.`group`, b.`role`
            FROM `" . AUTH_DATABASE . "`.`user_data` AS a
            LEFT JOIN `" . AUTH_DATABASE . "`.`user_access` AS b
            ON b.`user_id` = a.`id`
            WHERE b.`group` = " . $db->qstr($group) . " 
            AND b.`role` = `" . $db->qstr($role) . " 
            ORDER BY a.`lastname`, a.`firstname` ASC";
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.elentra.org/technical/developers/contributions/coding-standards.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
