Difference between revisions of "Development Policies"

From OpenEMR Project Wiki
 
(84 intermediate revisions by 4 users not shown)
Line 1: Line 1:
= Submitting Patches to Upstream =
= Submitting Patches to Upstream =
== Overview of submitting code ==
== Overview of submitting code ==
* Code patches for review can be submitted in the sourceforge tracker [http://sourceforge.net/tracker/?group_id=60081&atid=1245239 HERE]. We usually can test and give feedback to your patches within 1-2 days. Please derive patches from the most current OpenEMR development version from either of below repositories:
* Code patches for review can be submitted via a Pull Request on github at https://github.com/openemr/openemr/pulls
** The development tip from the CVS repository on sourceforge
:* See here for [[Git_for_dummies#Submit_your_code_for_review|further instructions]].
** The 'master' branch from the git repository on github: http://github.com/openemr/openemr ([[Git for dummies|quick howto documentation here]])
*Please derive patches/code from the most current OpenEMR development version from the following repository:
*** Note you can leverage github to maintain your own fork of code, so can then submit code by simply uploading your custom branches to your fork on github.
:* The 'master' branch from the git repository on github: http://github.com/openemr/openemr ([[Git for dummies|quick howto documentation here]])
** The 'master' branch from the git repository on gitorious: http://gitorious.org/openemr/openemr


== Creating A Patch ==
== Commit Messages ==
Creating patches with diff
Rebase often and write [https://chris.beams.io/posts/git-commit/ good commit messages].


Using diff is simple whether you are working with single files or entire source directories. To create a patch for a single file, use the form:
== Notifying the Development Team ==
When you submit a Pull Request on github at https://github.com/openemr/openemr/pulls , the development team will automatically be notified.
Use [https://github.com/openemr/openemr/issues OpenEMR GitHub Issues] to log bugs and feature requests.
 
== Github Continuous Integration ==
When code is pushed your Github pull request, our [https://travis-ci.org/openemr/openemr Continuous Integration service] will check for programmer errors from PHP v5.4 up to PHP v7.1. Make sure the service status is green and not red!
 
== Carriage Returns / Line Feeds ==
All text files for the project should have Unix-style line endings (i.e. no carriage returns). Developers who use a Windows desktop should also use a suitable text editor that respects this (last checked, EditPad Lite and Notepad++ are free examples). Git can also nicely automatically handle this for Windows users: http://help.github.com/line-endings/
 
== Feature Documentation ==
A known feature is a good feature. If your pull request touches or adds a feature, make sure it is well documented in either a text file or on this wiki. Note that this work can happen after your code is merged.
 
= General Development Best Practices =
 
== Copyright and Licensing ==
Each file in the source tree should begin with a copyright declaration, and information about what license the file is released under.
 
== Testing ==
Smoke test that your code works on the latest versions of Chrome and Firefox. If an operating system function is exercised, please make sure your changes work on both Linux and Windows to ensure cross-platform compatibility.
 
In order to verify that code changes pass certification tests, execute the relevant test cases via the test mapping document. This document is currently being built up (http://open-emr.org/wiki/index.php/Manual_Tests) and lives here: https://github.com/openemr/openemr/blob/master/tests/certification/tests.md
 
== Database Contents ==
 
When placing a string from the database inside of an html attribute, or placing it as the text content of an html object, one should call htmlspecialchars() to provide appropriate escaping.
 
== PHP ==
OpenEMR is moving towards more modern coding practices. While not strictly enforced, please keep the following guides in mind while coding:
* [http://www.php-fig.org/psr/psr-1/ PSR-1: Basic Coding Standard]
* [http://www.php-fig.org/psr/psr-2/ PSR-2: Coding Style Guide]
* [http://www.odi.ch/prog/design/php/guide.php General Practices]


    diff -u original.c new.c > original.patch
Please note that PHP 7.0 is the minimum version required for OpenEMR. This is enforced here: https://github.com/openemr/openemr/blob/master/common/compatibility/checker.php#L30


To create a patch for an entire source tree, make a copy of the tree:
When including a file, make sure to use 'require_once', or use 'include_once' and check the return!


    cp -R original new
All blocks of PHP code should start with '<?php', and end with '; ?>'. for example: '''<?php echo $testvar; ?>'''


Make any changes required in the directory new/. Then create a patch with the following command:
=== Comments ===
All comments should be written in a concise, clear, and useful way. Comments shouldn't answer the "How?" of the code; that's the role of the source code. Comments should answer the "Why?" of the code, what issues are being addressed by this code.


    diff -rupN original/ new/ > original.patch
Inline comments should use sentences with a capital first letter and a full stop if possible. These comments should appear before the code they document or on the same line if the comment is short enough.


That's all you need to get started with diff and patch. For more information use:
Block comments should be used for comments that require multiple lines/more explanation than what an inline comment can afford. Block comments should appear before the code they document. Consider the following section of [https://github.com/php-fig-rectified/fig-rectified-standards/blob/master/PSR-2-R-coding-style-guide-additions.md#tags php-fig-rectified-standards] for advanced tag usage (there is no strict format in place such as PHPDoc or Doxygen, but these style of comments are helpful for programmers).


  man diff
Comments should never be used to "preserve" old code or mark individual changes. This is the job of our version control system.
  man patch


Please go to [[How to Document Your Code Properly]] to get a good idea on how to document properly.


== Notifying the Development Team ==
=== Horizontal Spacing ===
Place the patch in the [http://sourceforge.net/tracker/?group_id=60081&atid=1245239 tracker's 'Code Review' section], with an explanation. Please also place an explanation of the patch in the [http://sourceforge.net/forum/forum.php?forum_id=202506 developer forum] so we know its in the tracker.
The current OpenEMR source is inconsistent about indentation using spaces or tabs.  Changes to existing code should preserve the indentation style of the changed lines.  Changes to existing files should follow the indentation style of the rest of the file for new lines; if the indentation style is inconsistent you may use any style currently in the file.  In new files, try to use a style that already exists in the source code.
 
==== Possible Future Guidelines ====
For most code:
* Use a single tab for an indentation level.  Outside of literal HTML, having code at more than 4 indentation levels is a good indicator the code should be re-factored.
* Use spaces for alignment.  Alignment is only done between two lines with the same indentation level.  Tabs appear before the spaces.  Alignment is rare, but may be used when breaking an overly long line.
* Use a single space for a "half-indent". While not common in PHP code, the "half-indent" is often used for "case $n:" labels in C/C++ and for "public:"/"private:" labels in C++.
* Tabs after spaces should be avoided. Tabs should be represented as "\t" in string literals.
* Unnecessary whitespace at the end of lines should be avoided.


== Carriage Returns / Line Feeds ==
For columnar/tabular data in a fixed-width font, when the width of each column is known or can be determined.
All text files for the project should have Unix-style line endings (i.e. no carriage returns). Developers who use a Windows desktop should also use a suitable text editor that respects this (last checked, EditPad Lite and Notepad++ are free examples).
* Use spaces for left/right padding of data items and headers out to the fixed column width.
* Use a single tab for the gap between columns.


= General Development Best Practices =
The above guidelines allow both you can all other developers of the code to choose any tab stop width greater than the width of a single space character and have the code align itself nicely.  The allows each developer to choose how much horizontal whitespace they need to efficiently read the code.


== Copyright and Licensing ==
=== Vertical Spacing ===
Each file in the source tree should begin with a copywright declaration, and information about what license the file is released under.
Avoid adding unnecessary empty lines.


== PHP ==
<span style="color:red;">Below is WRONG</span>
Many of the practices at http://www.odi.ch/prog/design/php/guide.php appear to be good rules when working with the OpenEMR source.
    echo "</br>\n";
?>


== Javascript ==
== Javascript ==
To ensure consistent interpretation of scripts in HTML and XHTML, any script which might contain the ampersand ('&') or left-angle-bracket ('<') characters must begin with
//<![CDATA[
on a line by itself and must end with
//]]>
on a line by itself.  In addition, the script must not contain the strings "]]>" or "</", even within a Javascript string literal or comment.  Currently, we do not have a function that sanitizes a php value for inclusion as a Javascript value.
When need to encode variables, use the encodeURIComponent() function instead of using the escape() function (to ensure compatibility with utf-8 characters).
When need to encode variables, use the encodeURIComponent() function instead of using the escape() function (to ensure compatibility with utf-8 characters).


== HTML ==
== HTML ==
Each page in OpenEMR should be valid HTML. the validator at http://validator.w3.org/ is useful for ensuring compliance. XHTML 1.1 compliant documents are preferred.
Each page in OpenEMR should be valid HTML 4.0, XHTML 1.0, or XHTML 1.1. the validator at http://validator.w3.org/ is useful for ensuring compliance.
 
For textual data that should not be interpreted as markup, use [http://php.net/manual/en/function.htmlspecialchars.php htmlspecialchars()].  Using a second ("quote_style") argument of ENT_NOQUOTES minimizes the length of the resulting string and is safe for non-markup.
 
For (parts of) attribute values, use htmlspecialchars() and provide a second ("quote_style") argument of ENT_QUOTES.  XHTML requires either single- or double-quote characters around all attribute values, and using ENT_QUOTES allows either of those characters to be used.
 
To ensure (X)HTML validity, element names and attribute names but be drawn from the standards, instead of any outside data source. Because of this no special processing should be required -- just make sure they are spelled correctly in the PHP code.
 
=== Bad  ===
<?php
$user_data = $_REQUEST['foo'];
$db_data = sqlQuery('some SQL statement')[0];
echo "<nowiki><p class='$db_data'>$user_data</p></nowiki>\n";
?>
 
=== Better ===
<?php
$user_data = $_REQUEST['foo'];
$db_data = sqlQuery('some SQL statement')[0];
$para_class = htmlspecialchars($db_data, ENT_QUOTES);
$para_content = htmlspecialchars($user_data, ENT_NOQUOTES);
echo "<nowiki><p class='$para_class'>$para_content</p></nowiki>\n";
?>
 
The basic rule of thumb is to use it on just code that is getting outputted to the screen (in html). So, not a good idea to do it to variables at the top of the file; need to do htmlspecialchars on them when you echo them onto the screen.
 
For example:
 
CORRECT:
  $date = $POST_['date']
  ...
  echo htmlspecialchars($date)
 
INCORRECT:
  $date = htmlspecialchars($POST_['date'])
  ...
  echo $date
 
Although sometimes you will build a variable just for output, then it's ok to hemlspecialchars that variable:
<pre>$string = "<b>".htmlspecialchars($string)."</b>"
echo $string</pre>
 
The key is to realize that htmlspecialchars() function is altering that string specifically for html output, thus using this altered string in other ways (such as php, mysql, pdf output, network exchange) can break things.


== CSS ==
== CSS ==
Line 82: Line 173:
:::<pre>alert("Please type letters only");</pre>
:::<pre>alert("Please type letters only");</pre>
::Should be:
::Should be:
:::<pre>alert("<?php xl('Please type letters only','e'); ?>");</pre>
:::<pre>alert("<?php echo xl('Please type letters only'); ?>");</pre>
 
:*Use the following pattern to introduce internationalization into your plain JavaScript files:
 
<!-- sorry for the margin-left hack... not sure how to indent blocks of code in MediaWiki :( -->
<div style="margin-left: 6em;"><pre>
<script type="text/javascript">
  var fooControllerTranslations = <?php echo json_encode(array(
    'keyA' => xla('Some OpenEMR translation'),
    'keyB' => xla('Another OpenEMR translation')
  ));?>;
</script>
 
<!-- note that  fooController uses the now the exposed fooControllerTranslations.keyA & fooControllerTranslations.keyB -->
<script type="text/javascript" src="<?php echo $webroot ?>/interface/foo/foo_controller.js"></script>
</pre></div>


:*Do not use the text or values of your buttons or controls in your coding algorithm. For example, if you have a 'submit' button and use the 'submit' string(the 'value' of the button) in your algorithm, then it will not work if it's translated to another word.
:*Do not use the text or values of your buttons or controls in your coding algorithm. For example, if you have a 'submit' button and use the 'submit' string(the 'value' of the button) in your algorithm, then it will not work if it's translated to another word.
:*xl() returns data that has been pulled from the database and then further mangled.  However, it has not gone through the necessary [[#HTML|HTML]], [[#Javascript|Javascript]], or other escaping that is required as part of input/output sanitizing; treat it like user-provided data pulled from the database.


For good examples, look through the code. If any questions don't hesitate to ask them on the sourceforge developer forums.
For good examples, look through the code. If any questions don't hesitate to ask them on the sourceforge developer forums.


== Input Collection ==
== Input Collection ==
A relatively new set of functions ([http://openemr.cvs.sourceforge.net/viewvc/openemr/openemr/library/formdata.inc.php openemr/library/formdata.inc.php]) have been created for generalized input validation/cleaning (deal with magic quotes) and preparing for database insertion (escaping data). The goal of this it to put in place a central mechanism to avoid sql-injection attacks and to get OpenEMR ready for PHP6.


This is an active project. Check out [[Active_Projects#Clean_up_magic_quotes.2C_prevent_sql-injection.2C_and_prepare_for_PHP6 |this link]] for progress and examples of it's use.  If any questions don't hesitate to ask them on the sourceforge developer forums.
[[Codebase_Security#Plan|'''See here for new method for doing this here.''']]
<br>


List and objectives of the openemr/library/formdata.inc.php functions:
== SQL Table Name Conventions ==


:'''formData()''' - This function will remove escapes (if magic quotes is set), and then places database specific escapes to ensure safe database insertion of variable. Input accepts POST, GET, or REQUEST variables, and there is an option to trim the input.
:Creating new SQL tables names with uppercase letters is NOT allowed. This is because these table names are difficult to support in both Windows and Linux. There are times, however, when this needs to be supported in the the following settings:
:'''formDataCore()''' - This function will remove escapes (if magic quotes is set), and then places database specific escapes to ensure safe database insertion of variable. Input accepts any variable, and there is an option to trim the input.
:#Older code (aka the form_CAMOS module)
:'''strip_escape_custom()''' - This function will remove escapes (if magic quotes is set). Input accepts any variable
:#Code that is beyond OpenEMR's control (for example, the RxNORM tables that are imported into OpenEMR).
:'''add_escape_custom()''' - This functions places database specific escapes to ensure safe database insertion of variable. Input accepts any variable
:There is a method to support this (as of OpenEMR 4.1.2), so that these table are supported in both Linux and Windows. In order for this to work, you need to surround the table name with the mitigateSqlTableUpperCase() function. For example, it will look like this:
$query1 = "select id, category from ".mitigateSqlTableUpperCase("form_CAMOS_category");
:(You may wonder, what the heck does this do; this function will actually look up the table in mysql in a case insensitive manner and return the table name from mysql)


== Access Control Objects ==
== Access Control Objects ==
Line 107: Line 217:


== Email ==
== Email ==
We are in process of standardizing this. Please see here for details : http://www.openmedsoftware.org/wiki/Sending_Email
We have a standardized method for sending emails. Please see here for details : [[Sending_Email|Sending Email]]
 
== Database Table Changes ==
Changes to the database schema are now versioned.  Please increment the value of $v_database in version.php, so that we can know automatically when a database upgrade is required. This is checked in admin.php and if the versions differ, a manual upgrade is required.


== Upgrade SQL META Language ==
Updates also need to be included in the database.sql and ####-to-####-upgrades.sql META files. See below for notes on using these files to support upgrades.


Comment Meta Language for sql upgrades:
=== Upgrade SQL META Language ===


Each section within an upgrade sql file is enveloped with an #If*/#EndIf block.  At first glance, these appear to be standard mysql comments meant to be cryptic hints to other developers about the sql goodness contained therein.  However, were you to rely on such basic premises, you would find yourself grossly deceived.  Indeed, without the knowledge that these comments are, in fact a sneakily embedded meta language derived for a purpose none-other than to aid in the protection of the database during upgrades, you would no doubt be subject to much ridicule and public beratement at the hands of the very developers who envisioned such a crafty use of comments. -jwallace
Comment Meta Language for sql upgrades (via the sql_upgrade.php script):


While these lines are as enigmatic as they are functional, there is a method to the madness.  Let's take a moment to briefly go over proper comment meta language use.
While these lines are as enigmatic as they are functional, there is a method to the madness.  Let's take a moment to briefly go over proper comment meta language use.  The best way to learn this is to look at examples in current/previous scripts.


<pre>  
<pre>  
Line 147: Line 260:
--    arguments: table_name colname value colname2 value2
--    arguments: table_name colname value colname2 value2
--    behavior:  If the table table_name does not have a row where colname = value AND colname2 = value2, the block will be executed.
--    behavior:  If the table table_name does not have a row where colname = value AND colname2 = value2, the block will be executed.
--  #IfNotRow3D
--    arguments: table_name colname value colname2 value2 colname3 value3
--    behavior:  If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3, the block will be executed.
--  #IfNotRow4D
--    arguments: table_name colname value colname2 value2 colname3 value3 colname4 value4
--    behavior:  If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3 AND colname4 = value4, the block will be executed.
--  #IfNotRow2Dx2
--    desc:      This is a very specialized function to allow adding items to the list_options table to avoid both redundant option_id and title in each element.
--    arguments: table_name colname value colname2 value2 colname3 value3
--    behavior:  The block will be executed if both statements below are true:
--              1) The table table_name does not have a row where colname = value AND colname2 = value2.
--              2) The table table_name does not have a row where colname = value AND colname3 = value3.


--  #EndIf
--  #EndIf
Line 187: Line 315:
== Creating a global configuration setting ==
== Creating a global configuration setting ==
As of version 4.0, these can be added to library/globals.inc.php . Then the variable can be called in code as so : $GLOBALS['yourVariableName'] . It can also then be configured by the user in Adminstration->Globals screen.
As of version 4.0, these can be added to library/globals.inc.php . Then the variable can be called in code as so : $GLOBALS['yourVariableName'] . It can also then be configured by the user in Adminstration->Globals screen.
[[Category:Developer Guide]]

Latest revision as of 08:42, 2 January 2019

Submitting Patches to Upstream

Overview of submitting code

  • Please derive patches/code from the most current OpenEMR development version from the following repository:

Commit Messages

Rebase often and write good commit messages.

Notifying the Development Team

When you submit a Pull Request on github at https://github.com/openemr/openemr/pulls , the development team will automatically be notified. Use OpenEMR GitHub Issues to log bugs and feature requests.

Github Continuous Integration

When code is pushed your Github pull request, our Continuous Integration service will check for programmer errors from PHP v5.4 up to PHP v7.1. Make sure the service status is green and not red!

Carriage Returns / Line Feeds

All text files for the project should have Unix-style line endings (i.e. no carriage returns). Developers who use a Windows desktop should also use a suitable text editor that respects this (last checked, EditPad Lite and Notepad++ are free examples). Git can also nicely automatically handle this for Windows users: http://help.github.com/line-endings/

Feature Documentation

A known feature is a good feature. If your pull request touches or adds a feature, make sure it is well documented in either a text file or on this wiki. Note that this work can happen after your code is merged.

General Development Best Practices

Copyright and Licensing

Each file in the source tree should begin with a copyright declaration, and information about what license the file is released under.

Testing

Smoke test that your code works on the latest versions of Chrome and Firefox. If an operating system function is exercised, please make sure your changes work on both Linux and Windows to ensure cross-platform compatibility.

In order to verify that code changes pass certification tests, execute the relevant test cases via the test mapping document. This document is currently being built up (http://open-emr.org/wiki/index.php/Manual_Tests) and lives here: https://github.com/openemr/openemr/blob/master/tests/certification/tests.md

Database Contents

When placing a string from the database inside of an html attribute, or placing it as the text content of an html object, one should call htmlspecialchars() to provide appropriate escaping.

PHP

OpenEMR is moving towards more modern coding practices. While not strictly enforced, please keep the following guides in mind while coding:

Please note that PHP 7.0 is the minimum version required for OpenEMR. This is enforced here: https://github.com/openemr/openemr/blob/master/common/compatibility/checker.php#L30

When including a file, make sure to use 'require_once', or use 'include_once' and check the return!

All blocks of PHP code should start with '<?php', and end with '; ?>'. for example: <?php echo $testvar; ?>

Comments

All comments should be written in a concise, clear, and useful way. Comments shouldn't answer the "How?" of the code; that's the role of the source code. Comments should answer the "Why?" of the code, what issues are being addressed by this code.

Inline comments should use sentences with a capital first letter and a full stop if possible. These comments should appear before the code they document or on the same line if the comment is short enough.

Block comments should be used for comments that require multiple lines/more explanation than what an inline comment can afford. Block comments should appear before the code they document. Consider the following section of php-fig-rectified-standards for advanced tag usage (there is no strict format in place such as PHPDoc or Doxygen, but these style of comments are helpful for programmers).

Comments should never be used to "preserve" old code or mark individual changes. This is the job of our version control system.

Please go to How to Document Your Code Properly to get a good idea on how to document properly.

Horizontal Spacing

The current OpenEMR source is inconsistent about indentation using spaces or tabs. Changes to existing code should preserve the indentation style of the changed lines. Changes to existing files should follow the indentation style of the rest of the file for new lines; if the indentation style is inconsistent you may use any style currently in the file. In new files, try to use a style that already exists in the source code.

Possible Future Guidelines

For most code:

  • Use a single tab for an indentation level. Outside of literal HTML, having code at more than 4 indentation levels is a good indicator the code should be re-factored.
  • Use spaces for alignment. Alignment is only done between two lines with the same indentation level. Tabs appear before the spaces. Alignment is rare, but may be used when breaking an overly long line.
  • Use a single space for a "half-indent". While not common in PHP code, the "half-indent" is often used for "case $n:" labels in C/C++ and for "public:"/"private:" labels in C++.
  • Tabs after spaces should be avoided. Tabs should be represented as "\t" in string literals.
  • Unnecessary whitespace at the end of lines should be avoided.

For columnar/tabular data in a fixed-width font, when the width of each column is known or can be determined.

  • Use spaces for left/right padding of data items and headers out to the fixed column width.
  • Use a single tab for the gap between columns.

The above guidelines allow both you can all other developers of the code to choose any tab stop width greater than the width of a single space character and have the code align itself nicely. The allows each developer to choose how much horizontal whitespace they need to efficiently read the code.

Vertical Spacing

Avoid adding unnecessary empty lines.

Below is WRONG

    echo "
\n"; ?>

Javascript

To ensure consistent interpretation of scripts in HTML and XHTML, any script which might contain the ampersand ('&') or left-angle-bracket ('<') characters must begin with

//<![CDATA[

on a line by itself and must end with

//]]>

on a line by itself. In addition, the script must not contain the strings "]]>" or "</", even within a Javascript string literal or comment. Currently, we do not have a function that sanitizes a php value for inclusion as a Javascript value.

When need to encode variables, use the encodeURIComponent() function instead of using the escape() function (to ensure compatibility with utf-8 characters).

HTML

Each page in OpenEMR should be valid HTML 4.0, XHTML 1.0, or XHTML 1.1. the validator at http://validator.w3.org/ is useful for ensuring compliance.

For textual data that should not be interpreted as markup, use htmlspecialchars(). Using a second ("quote_style") argument of ENT_NOQUOTES minimizes the length of the resulting string and is safe for non-markup.

For (parts of) attribute values, use htmlspecialchars() and provide a second ("quote_style") argument of ENT_QUOTES. XHTML requires either single- or double-quote characters around all attribute values, and using ENT_QUOTES allows either of those characters to be used.

To ensure (X)HTML validity, element names and attribute names but be drawn from the standards, instead of any outside data source. Because of this no special processing should be required -- just make sure they are spelled correctly in the PHP code.

Bad

<?php
$user_data = $_REQUEST['foo'];
$db_data = sqlQuery('some SQL statement')[0];
echo "<p class='$db_data'>$user_data</p>\n";
?>

Better

<?php
$user_data = $_REQUEST['foo'];
$db_data = sqlQuery('some SQL statement')[0];
$para_class = htmlspecialchars($db_data, ENT_QUOTES);
$para_content = htmlspecialchars($user_data, ENT_NOQUOTES);
echo "<p class='$para_class'>$para_content</p>\n";
?>

The basic rule of thumb is to use it on just code that is getting outputted to the screen (in html). So, not a good idea to do it to variables at the top of the file; need to do htmlspecialchars on them when you echo them onto the screen.

For example:

CORRECT:

 $date = $POST_['date']
 ...
 echo htmlspecialchars($date)

INCORRECT:

 $date = htmlspecialchars($POST_['date'])
 ...
 echo $date

Although sometimes you will build a variable just for output, then it's ok to hemlspecialchars that variable:

$string = "<b>".htmlspecialchars($string)."</b>"
echo $string

The key is to realize that htmlspecialchars() function is altering that string specifically for html output, thus using this altered string in other ways (such as php, mysql, pdf output, network exchange) can break things.

CSS

it is preferred that CSS stylesheets contain all style related contents of our html forms. css stylesheets should also validate.

OpenEMR Specific Development Best Practices

MySQL connections

All of your MySQL calls need to go through openemr/library/sql.inc or you will break the encoding (utf8). See that file for details and the large amount of example throughout code. NEVER, NEVER, NEVER use the native mysql_* calls.

PHP Sessions and Browser Windows

You must include a JavaScript call to top.restoreSession() wherever you invoke a PHP script that requires current session data (which is most of them). How to do this is discussed in more detail in the architecture discussion wiki page.

Internationalization

  • The main php function used for translation is xl(), basically all of labels and messages have to go through this function. To learn about this function definition, parameters, and general use, please read this wiki page, and ensure you understand it.
  • These things are what I consider the tenets of the xl() function:
For coding new xl functions:
1. No trailing or leading whitespace.
Below is WRONG
xl('Demographics ');
Below is CORRECT
xl('Demographics') . ' ';
2. No variables.
Below is WRONG
xl('please type $name here');
Below is CORRECT
xl('please type') . ' ' . $name . ' ' . xl('here');
For previously coded xl functions:
  1. To be safe, just leave them be (the above rules do not apply).
  • Don't forget about javascript strings. As long as the javascript is in a .php or .inc file it will be translated.
For example:
alert("Please type letters only");
Should be:
alert("<?php echo xl('Please type letters only'); ?>");
  • Use the following pattern to introduce internationalization into your plain JavaScript files:
<script type="text/javascript">
  var fooControllerTranslations = <?php echo json_encode(array(
    'keyA' => xla('Some OpenEMR translation'),
    'keyB' => xla('Another OpenEMR translation')
  ));?>;
</script>

<!-- note that  fooController uses the now the exposed fooControllerTranslations.keyA & fooControllerTranslations.keyB -->
<script type="text/javascript" src="<?php echo $webroot ?>/interface/foo/foo_controller.js"></script>
  • Do not use the text or values of your buttons or controls in your coding algorithm. For example, if you have a 'submit' button and use the 'submit' string(the 'value' of the button) in your algorithm, then it will not work if it's translated to another word.
  • xl() returns data that has been pulled from the database and then further mangled. However, it has not gone through the necessary HTML, Javascript, or other escaping that is required as part of input/output sanitizing; treat it like user-provided data pulled from the database.

For good examples, look through the code. If any questions don't hesitate to ask them on the sourceforge developer forums.

Input Collection

See here for new method for doing this here.

SQL Table Name Conventions

Creating new SQL tables names with uppercase letters is NOT allowed. This is because these table names are difficult to support in both Windows and Linux. There are times, however, when this needs to be supported in the the following settings:
  1. Older code (aka the form_CAMOS module)
  2. Code that is beyond OpenEMR's control (for example, the RxNORM tables that are imported into OpenEMR).
There is a method to support this (as of OpenEMR 4.1.2), so that these table are supported in both Linux and Windows. In order for this to work, you need to surround the table name with the mitigateSqlTableUpperCase() function. For example, it will look like this:
$query1 = "select id, category from ".mitigateSqlTableUpperCase("form_CAMOS_category");
(You may wonder, what the heck does this do; this function will actually look up the table in mysql in a case insensitive manner and return the table name from mysql)

Access Control Objects

If you add a new Access Control Object to the OpenEMR codebase, then also add it to the following three sites:

  1. Header notes of the library/acl.inc file
  2. acl_setup.php file
  3. acl_upgrade.php file

Email

We have a standardized method for sending emails. Please see here for details : Sending Email

Database Table Changes

Changes to the database schema are now versioned. Please increment the value of $v_database in version.php, so that we can know automatically when a database upgrade is required. This is checked in admin.php and if the versions differ, a manual upgrade is required.

Updates also need to be included in the database.sql and ####-to-####-upgrades.sql META files. See below for notes on using these files to support upgrades.

Upgrade SQL META Language

Comment Meta Language for sql upgrades (via the sql_upgrade.php script):

While these lines are as enigmatic as they are functional, there is a method to the madness. Let's take a moment to briefly go over proper comment meta language use. The best way to learn this is to look at examples in current/previous scripts.

 
--  The #If* sections have the behavior of functions and come complete with arguments supplied command-line style 
--
--  Your Comment meta language lines cannot contain any other comment styles such as the nefarious double dashes "--" lest your lines be skipped and the blocks automatcially executed with out regard to the existing database state.   
--
--  Comment Meta Language Constructs:
-- 
--  #IfNotTable
--    argument: table_name
--    behavior: if the table_name does not exist,  the block will be executed

--  #IfTable
--    argument: table_name
--    behavior: if the table_name does exist, the block will be executed

--  #IfMissingColumn
--    arguments: table_name colname
--    behavior:  if the colname in the table_name table does not exist,  the block will be executed

--  #IfNotColumnType
--    arguments: table_name colname value
--    behavior:  If the table table_name does not have a column colname with a data type equal to value, then the block will be executed

--  #IfNotRow
--    arguments: table_name colname value
--    behavior:  If the table table_name does not have a row where colname = value, the block will be executed.

--  #IfNotRow2D
--    arguments: table_name colname value colname2 value2
--    behavior:  If the table table_name does not have a row where colname = value AND colname2 = value2, the block will be executed.

--  #IfNotRow3D
--    arguments: table_name colname value colname2 value2 colname3 value3
--    behavior:  If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3, the block will be executed.

--  #IfNotRow4D
--    arguments: table_name colname value colname2 value2 colname3 value3 colname4 value4
--    behavior:  If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3 AND colname4 = value4, the block will be executed.

--  #IfNotRow2Dx2
--    desc:      This is a very specialized function to allow adding items to the list_options table to avoid both redundant option_id and title in each element.
--    arguments: table_name colname value colname2 value2 colname3 value3
--    behavior:  The block will be executed if both statements below are true:
--               1) The table table_name does not have a row where colname = value AND colname2 = value2.
--               2) The table table_name does not have a row where colname = value AND colname3 = value3.

--  #EndIf
--    all blocks are terminated with and #EndIF statement. 

Creating a list in list_options

Lists are a nice way to build customizable lists that support standardized output formatting and internationalization. See Administration->Lists for details and check out the list_options mysql table.


Example of placement of new list in database.sql:
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('lists','proc_specimen','Procedure Specimen Types', 1,0);
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('proc_specimen','blood' ,'Blood' ,10,0);
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('proc_specimen','saliva','Saliva',20,0);
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('proc_specimen','urine' ,'Urine' ,30,0);
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('proc_specimen','oth'   ,'Other' ,90,0);


Example of placement of new list in the upgrade sql file:
#IfNotRow2D list_options list_id lists option_id proc_specimen
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('lists','proc_specimen','Procedure Specimen Types', 1,0);
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('proc_specimen','blood' ,'Blood' ,10,0);
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('proc_specimen','saliva','Saliva',20,0);
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('proc_specimen','urine' ,'Urine' ,30,0);
INSERT INTO list_options ( list_id, option_id, title, seq, is_default ) VALUES ('proc_specimen','oth'   ,'Other' ,90,0);
#EndIf

Note as demonstrated above that the seq entries increment in 10's, and that the option_id entry should not contain any blanks or caps.


These lists can be used to configuration of layouts in Administration->layouts, and can also use the functions in options.inc.php within scripts.

Example of generating a item selector using the list(note the input variable will be $POST['form_proc_specimen']):
generate_form_field(array('data_type'=>1,'field_id'=>'proc_specimen','list_id'=>'proc_specimen'), $defaultProcSpecimen);
Example of generating the title of the list from the option_id:
generate_display_field(array('data_type'=>'1','list_id'=>'proc_specimen'),$chosen_proc_specimen);

Creating a global configuration setting

As of version 4.0, these can be added to library/globals.inc.php . Then the variable can be called in code as so : $GLOBALS['yourVariableName'] . It can also then be configured by the user in Adminstration->Globals screen.