## Framework Version 4

See the [Versioning README](README.md) for more details on framework versions in general.

#### Breaking Changes

- The briefly supported `getSQLInClause()` method has been disallowed in favor of the `addInClause()` method [documented here](../methods/querying.md).
- **Parameterized Queries** - Framework Version 4 requires that all `query()` method calls must include an additional `$parameters` argument.  All queries should be refactored so that all dynamic values are passed as parameters.  Please take a moment to read the [Query Documentation](../methods/querying.md) page to get a feel for parameterized queries in general, then return here for the following specifics on transitioning modules to use them:
  - Parameters are automatically escaped, which means manual escaping methods like `db_escape()` and `db_real_escape_string()` should be removed to avoid double escaping.
  - Updating many queries will be as simple as replacing all variables in the SQL with question marks, then specifying those variables in the `$parameters` argument instead.  Cases that require additional handling are included below.
  - The `db_affected_rows()` method will no longer work.  See the [Query Documentation](../methods/querying.md) page for an alternative.
  - Methods that begin with `mysqli_...` and operate on the MySQL result object will no longer work.  Please use the equivalent `db_...` methods instead.
  - Numeric column values will return as the `int` type in PHP where they previously returned as `string`.  This can cause problems for any type sensitive operations like triple equals checking.  The simplest solution to prevent potential issues without refactoring is to cast the numeric columns in either SQL or PHP.
    - In PHP you can cast all integer columns to strings manually, or by using the following utility method on each fetched row:
      - `$row = $module->convertIntsToStrings($row);`
    - In SQL you can cast values individually.  For example:
      - Before: `select project_id`
      - After: &nbsp;&nbsp;`select cast(project_id as char) as project_id`.
  - Queries using appended SQL strings can still use parameters.  For example:
    - Before: `$module->query("...where value like '{$value}%'");`
    - After: &nbsp;&nbsp;`$module->query("...where value like ?)", [$value . '%']");`
  - Please don't simply pass in an empty or partial parameter array while still manually appending parameters to your query string.  Using prepared statements with parameterized queries is important for security as described by [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html) and security experts spanning many other development communities.
  - This may also be a good time replace any old `db_query()` calls as well.  Manually calling `db_query()` is deprecated in modules, and may be disallowed in the future.  
  - When updating queries to use parameters and/or the `$module->query()` method, there may be cases where a query that previously failed silenty now throws an exception.  The is considered expected behavior because it forces the developer to consider failure cases, rather than continuing script execution in a potentialy unsafe state.
- If skipping framework versions, do not forget to review/address the breaking changes from all prior framework versions.
