<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.limesurvey.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Olle</id>
	<title>LimeSurvey Manual - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://www.limesurvey.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Olle"/>
	<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/Special:Contributions/Olle"/>
	<updated>2026-08-10T12:59:03Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.9</generator>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163615</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163615"/>
		<updated>2021-05-19T15:04:47Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* question_themes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
====question_themes====&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable zebra&amp;quot;&lt;br /&gt;
! Name || Type || Null || Key || Default || Extra || Description&lt;br /&gt;
|-&lt;br /&gt;
| id            || int(11)      || NO   || PRI || NULL    || auto_increment ||&lt;br /&gt;
|-&lt;br /&gt;
| name          || varchar(150) || NO   || MUL || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| visible       || varchar(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| xml_path      || varchar(255) || YES  ||     || NULL    ||                || Relative path to the folder which contains config.xml. Relative to different paths depending on theme type (core, user).&lt;br /&gt;
|-&lt;br /&gt;
| image_path    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| title         || varchar(100) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| creation_date || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author        || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_email  || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_url    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| copyright     || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| license       || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| version       || varchar(45)  || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| api_version   || varchar(45)  || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| description   || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| last_update   || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| owner_id      || int(11)      || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| theme_type    || varchar(150) || YES  ||     || NULL    ||                || Always &amp;quot;question_theme&amp;quot;?&lt;br /&gt;
|-&lt;br /&gt;
| question_type || varchar(150) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| core_theme    || tinyint(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| extends       || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| group         || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| settings      || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163614</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163614"/>
		<updated>2021-05-19T14:03:30Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* question_themes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
====question_themes====&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable zebra&amp;quot;&lt;br /&gt;
! Name || Type || Null || Key || Default || Extra || Description&lt;br /&gt;
|-&lt;br /&gt;
| id            || int(11)      || NO   || PRI || NULL    || auto_increment ||&lt;br /&gt;
|-&lt;br /&gt;
| name          || varchar(150) || NO   || MUL || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| visible       || varchar(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| xml_path      || varchar(255) || YES  ||     || NULL    ||                || Relative path to the folder which contains config.xml. Relative to different paths depending on theme type (core, user).&lt;br /&gt;
|-&lt;br /&gt;
| image_path    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| title         || varchar(100) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| creation_date || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author        || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_email  || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_url    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| copyright     || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| license       || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| version       || varchar(45)  || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| api_version   || varchar(45)  || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| description   || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| last_update   || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| owner_id      || int(11)      || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| theme_type    || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| question_type || varchar(150) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| core_theme    || tinyint(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| extends       || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| group         || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| settings      || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163613</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163613"/>
		<updated>2021-05-19T13:59:51Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* question_themes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
====question_themes====&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable zebra&amp;quot;&lt;br /&gt;
! Name || Type || Null || Key || Default || Extra || Description&lt;br /&gt;
|-&lt;br /&gt;
| id            || int(11)      || NO   || PRI || NULL    || auto_increment ||&lt;br /&gt;
|-&lt;br /&gt;
| name          || varchar(150) || NO   || MUL || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| visible       || varchar(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| xml_path      || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| image_path    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| title         || varchar(100) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| creation_date || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author        || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_email  || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_url    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| copyright     || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| license       || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| version       || varchar(45)  || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| api_version   || varchar(45)  || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| description   || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| last_update   || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| owner_id      || int(11)      || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| theme_type    || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| question_type || varchar(150) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| core_theme    || tinyint(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| extends       || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| group         || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| settings      || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163612</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163612"/>
		<updated>2021-05-19T13:56:08Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* question_themes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
====question_themes====&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable stripe&amp;quot;&lt;br /&gt;
! Name || Type || Null || Key || Default || Extra || Description&lt;br /&gt;
|-&lt;br /&gt;
| id            || int(11)      || NO   || PRI || NULL    || auto_increment ||&lt;br /&gt;
|-&lt;br /&gt;
| name          || varchar(150) || NO   || MUL || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| visible       || varchar(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| xml_path      || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| image_path    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| title         || varchar(100) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| creation_date || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author        || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_email  || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_url    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| copyright     || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| license       || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| version       || varchar(45)  || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| api_version   || varchar(45)  || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| description   || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| last_update   || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| owner_id      || int(11)      || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| theme_type    || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| question_type || varchar(150) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| core_theme    || tinyint(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| extends       || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| group         || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| settings      || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163611</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163611"/>
		<updated>2021-05-19T13:55:09Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* question_themes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
====question_themes====&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Name || Type || Null || Key || Default || Extra || Description&lt;br /&gt;
|-&lt;br /&gt;
| id            || int(11)      || NO   || PRI || NULL    || auto_increment ||&lt;br /&gt;
|-&lt;br /&gt;
| name          || varchar(150) || NO   || MUL || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| visible       || varchar(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| xml_path      || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| image_path    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| title         || varchar(100) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| creation_date || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author        || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_email  || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_url    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| copyright     || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| license       || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| version       || varchar(45)  || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| api_version   || varchar(45)  || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| description   || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| last_update   || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| owner_id      || int(11)      || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| theme_type    || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| question_type || varchar(150) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| core_theme    || tinyint(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| extends       || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| group         || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| settings      || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163610</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163610"/>
		<updated>2021-05-19T13:54:41Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* question_themes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
====question_themes====&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Name | Type | Null | Key | Default | Extra | Description&lt;br /&gt;
|-&lt;br /&gt;
| id            || int(11)      || NO   || PRI || NULL    || auto_increment ||&lt;br /&gt;
|-&lt;br /&gt;
| name          || varchar(150) || NO   || MUL || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| visible       || varchar(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| xml_path      || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| image_path    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| title         || varchar(100) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| creation_date || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author        || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_email  || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| author_url    || varchar(255) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| copyright     || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| license       || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| version       || varchar(45)  || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| api_version   || varchar(45)  || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| description   || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| last_update   || datetime     || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| owner_id      || int(11)      || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| theme_type    || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| question_type || varchar(150) || NO   ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| core_theme    || tinyint(1)   || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| extends       || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| group         || varchar(150) || YES  ||     || NULL    ||                ||&lt;br /&gt;
|-&lt;br /&gt;
| settings      || text         || YES  ||     || NULL    ||                ||&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163609</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163609"/>
		<updated>2021-05-19T13:51:22Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
====question_themes====&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Name ! Type ! Null ! Key ! Default ! Extra ! Description&lt;br /&gt;
|-&lt;br /&gt;
| id            | int(11)      | NO   | PRI | NULL    | auto_increment |&lt;br /&gt;
|-&lt;br /&gt;
| name          | varchar(150) | NO   | MUL | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| visible       | varchar(1)   | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| xml_path      | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| image_path    | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| title         | varchar(100) | NO   |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| creation_date | datetime     | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| author        | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| author_email  | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| author_url    | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| copyright     | text         | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| license       | text         | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| version       | varchar(45)  | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| api_version   | varchar(45)  | NO   |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| description   | text         | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| last_update   | datetime     | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| owner_id      | int(11)      | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| theme_type    | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| question_type | varchar(150) | NO   |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| core_theme    | tinyint(1)   | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| extends       | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| group         | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| settings      | text         | YES  |     | NULL    |                |&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163608</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163608"/>
		<updated>2021-05-19T13:50:34Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;question_themes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Name ! Type ! Null ! Key ! Default ! Extra ! Description&lt;br /&gt;
|-&lt;br /&gt;
| id            | int(11)      | NO   | PRI | NULL    | auto_increment |&lt;br /&gt;
|-&lt;br /&gt;
| name          | varchar(150) | NO   | MUL | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| visible       | varchar(1)   | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| xml_path      | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| image_path    | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| title         | varchar(100) | NO   |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| creation_date | datetime     | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| author        | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| author_email  | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| author_url    | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| copyright     | text         | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| license       | text         | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| version       | varchar(45)  | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| api_version   | varchar(45)  | NO   |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| description   | text         | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| last_update   | datetime     | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| owner_id      | int(11)      | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| theme_type    | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| question_type | varchar(150) | NO   |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| core_theme    | tinyint(1)   | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| extends       | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| group         | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| settings      | text         | YES  |     | NULL    |                |&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163607</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163607"/>
		<updated>2021-05-19T13:49:22Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;question_themes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Columns:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Name ! Type ! Null ! Key ! Default ! Extra ! Description&lt;br /&gt;
|-&lt;br /&gt;
| id            | int(11)      | NO   | PRI | NULL    | auto_increment |&lt;br /&gt;
|-&lt;br /&gt;
| name          | varchar(150) | NO   | MUL | NULL    |                |&lt;br /&gt;
|-&lt;br /&gt;
| visible       | varchar(1)   | YES  |     | NULL    |                |&lt;br /&gt;
| xml_path      | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
| image_path    | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
| title         | varchar(100) | NO   |     | NULL    |                |&lt;br /&gt;
| creation_date | datetime     | YES  |     | NULL    |                |&lt;br /&gt;
| author        | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
| author_email  | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
| author_url    | varchar(255) | YES  |     | NULL    |                |&lt;br /&gt;
| copyright     | text         | YES  |     | NULL    |                |&lt;br /&gt;
| license       | text         | YES  |     | NULL    |                |&lt;br /&gt;
| version       | varchar(45)  | YES  |     | NULL    |                |&lt;br /&gt;
| api_version   | varchar(45)  | NO   |     | NULL    |                |&lt;br /&gt;
| description   | text         | YES  |     | NULL    |                |&lt;br /&gt;
| last_update   | datetime     | YES  |     | NULL    |                |&lt;br /&gt;
| owner_id      | int(11)      | YES  |     | NULL    |                |&lt;br /&gt;
| theme_type    | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
| question_type | varchar(150) | NO   |     | NULL    |                |&lt;br /&gt;
| core_theme    | tinyint(1)   | YES  |     | NULL    |                |&lt;br /&gt;
| extends       | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
| group         | varchar(150) | YES  |     | NULL    |                |&lt;br /&gt;
| settings      | text         | YES  |     | NULL    |                |&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163606</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163606"/>
		<updated>2021-05-19T13:42:48Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
 answer_l10ns                     &lt;br /&gt;
 answers                          &lt;br /&gt;
 archived_table_settings          &lt;br /&gt;
 assessments                      &lt;br /&gt;
 asset_version                    &lt;br /&gt;
 boxes                            &lt;br /&gt;
 conditions                       &lt;br /&gt;
 defaultvalue_l10ns               &lt;br /&gt;
 defaultvalues                    &lt;br /&gt;
 expression_errors                &lt;br /&gt;
 failed_login_attempts            &lt;br /&gt;
 group_l10ns                      &lt;br /&gt;
 groups                           &lt;br /&gt;
 label_l10ns                      &lt;br /&gt;
 labels                           &lt;br /&gt;
 labelsets                        &lt;br /&gt;
 map_tutorial_users               &lt;br /&gt;
 notifications                    &lt;br /&gt;
 old_survey_882234_20210512154558 &lt;br /&gt;
 old_tokens_882234_20210512154558 &lt;br /&gt;
 participant_attribute            &lt;br /&gt;
 participant_attribute_names      &lt;br /&gt;
 participant_attribute_names_lang &lt;br /&gt;
 participant_attribute_values     &lt;br /&gt;
 participant_shares               &lt;br /&gt;
 participants                     &lt;br /&gt;
 permissions                      &lt;br /&gt;
 permissiontemplates              &lt;br /&gt;
 plugin_settings                  &lt;br /&gt;
 plugins                          &lt;br /&gt;
 question_attributes              &lt;br /&gt;
 question_l10ns                   &lt;br /&gt;
 question_themes                  &lt;br /&gt;
 questions                        &lt;br /&gt;
 quota                            &lt;br /&gt;
 quota_languagesettings           &lt;br /&gt;
 quota_members                    &lt;br /&gt;
 saved_control                    &lt;br /&gt;
 sessions                         &lt;br /&gt;
 settings_global                  &lt;br /&gt;
 settings_user                    &lt;br /&gt;
 survey_882234                    &lt;br /&gt;
 survey_links                     &lt;br /&gt;
 survey_url_parameters            &lt;br /&gt;
 surveymenu                       &lt;br /&gt;
 surveymenu_entries               &lt;br /&gt;
 surveys                          &lt;br /&gt;
 surveys_groups                   &lt;br /&gt;
 surveys_groupsettings            &lt;br /&gt;
 surveys_languagesettings         &lt;br /&gt;
 template_configuration           &lt;br /&gt;
 templates                        &lt;br /&gt;
 tutorial_entries                 &lt;br /&gt;
 tutorial_entry_relation          &lt;br /&gt;
 tutorials                        &lt;br /&gt;
 user_groups                      &lt;br /&gt;
 user_in_groups                   &lt;br /&gt;
 user_in_permissionrole           &lt;br /&gt;
 users&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163605</id>
		<title>Database documentation</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Database_documentation&amp;diff=163605"/>
		<updated>2021-05-19T13:42:20Z</updated>

		<summary type="html">&lt;p&gt;Olle: Created page with &amp;quot;This page contains documentation for all database tables and their functionality.  Without prefix, this is all tables, as of 2021-05-19:  answer_l10ns                      ans...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains documentation for all database tables and their functionality.&lt;br /&gt;
&lt;br /&gt;
Without prefix, this is all tables, as of 2021-05-19:&lt;br /&gt;
&lt;br /&gt;
answer_l10ns                     &lt;br /&gt;
answers                          &lt;br /&gt;
archived_table_settings          &lt;br /&gt;
assessments                      &lt;br /&gt;
asset_version                    &lt;br /&gt;
boxes                            &lt;br /&gt;
conditions                       &lt;br /&gt;
defaultvalue_l10ns               &lt;br /&gt;
defaultvalues                    &lt;br /&gt;
expression_errors                &lt;br /&gt;
failed_login_attempts            &lt;br /&gt;
group_l10ns                      &lt;br /&gt;
groups                           &lt;br /&gt;
label_l10ns                      &lt;br /&gt;
labels                           &lt;br /&gt;
labelsets                        &lt;br /&gt;
map_tutorial_users               &lt;br /&gt;
notifications                    &lt;br /&gt;
old_survey_882234_20210512154558 &lt;br /&gt;
old_tokens_882234_20210512154558 &lt;br /&gt;
participant_attribute            &lt;br /&gt;
participant_attribute_names      &lt;br /&gt;
participant_attribute_names_lang &lt;br /&gt;
participant_attribute_values     &lt;br /&gt;
participant_shares               &lt;br /&gt;
participants                     &lt;br /&gt;
permissions                      &lt;br /&gt;
permissiontemplates              &lt;br /&gt;
plugin_settings                  &lt;br /&gt;
plugins                          &lt;br /&gt;
question_attributes              &lt;br /&gt;
question_l10ns                   &lt;br /&gt;
question_themes                  &lt;br /&gt;
questions                        &lt;br /&gt;
quota                            &lt;br /&gt;
quota_languagesettings           &lt;br /&gt;
quota_members                    &lt;br /&gt;
saved_control                    &lt;br /&gt;
sessions                         &lt;br /&gt;
settings_global                  &lt;br /&gt;
settings_user                    &lt;br /&gt;
survey_882234                    &lt;br /&gt;
survey_links                     &lt;br /&gt;
survey_url_parameters            &lt;br /&gt;
surveymenu                       &lt;br /&gt;
surveymenu_entries               &lt;br /&gt;
surveys                          &lt;br /&gt;
surveys_groups                   &lt;br /&gt;
surveys_groupsettings            &lt;br /&gt;
surveys_languagesettings         &lt;br /&gt;
template_configuration           &lt;br /&gt;
templates                        &lt;br /&gt;
tutorial_entries                 &lt;br /&gt;
tutorial_entry_relation          &lt;br /&gt;
tutorials                        &lt;br /&gt;
user_groups                      &lt;br /&gt;
user_in_groups                   &lt;br /&gt;
user_in_permissionrole           &lt;br /&gt;
users&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Development_overview&amp;diff=163604</id>
		<title>Development overview</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Development_overview&amp;diff=163604"/>
		<updated>2021-05-19T13:40:33Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Table of contents:&lt;br /&gt;
&lt;br /&gt;
*[[LimeSurvey roadmap]]&lt;br /&gt;
*[[How to join the LimeSurvey project team]]&lt;br /&gt;
**[[How to become a LimeSurvey Developer]]&lt;br /&gt;
*[[Accessing the source code]]&lt;br /&gt;
**[[Switch from Subversion to Git]]&lt;br /&gt;
*[[Coding guidelines]]&lt;br /&gt;
*[[How to document your source code]]&lt;br /&gt;
*[[Standard for Git commit messages]]&lt;br /&gt;
*[[Help us: Tasks]]&lt;br /&gt;
*[[How can I support LimeSurvey?]]&lt;br /&gt;
*[[How to contribute new features]]&lt;br /&gt;
*[[Database versioning]]&lt;br /&gt;
*[[Database documentation]]&lt;br /&gt;
*[[The bugtracker and git]]&lt;br /&gt;
*[[Plugin system architecture]]&lt;br /&gt;
**[[Authentication plugin development]]&lt;br /&gt;
**[[Question plugins]]&lt;br /&gt;
***[[Creating a question plugin]]&lt;br /&gt;
****[[Question signature]]&lt;br /&gt;
**[[Defining attributes]]&lt;br /&gt;
*[[HTML editor : needed functionality]]&lt;br /&gt;
**[[Update ckeditor]]&lt;br /&gt;
**[[redactor]]&lt;br /&gt;
**[[summernote]]&lt;br /&gt;
**[[tinyMCE]]&lt;br /&gt;
*[[VueJS]]&lt;br /&gt;
*[[Replace_jqGrid]]&lt;br /&gt;
*[[Accessibity]]&lt;br /&gt;
*[[Templating]]&lt;br /&gt;
*[[Regression and unit tests]]&lt;br /&gt;
*[[Survey settings inheritance]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163599</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163599"/>
		<updated>2021-05-19T10:20:13Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Modules */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Why a risk-based approach? Waterfall vs agile, code driven.&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Ambiguity vs uncertainty.&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
risk based on knowledge, evidence, P(A|K)&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Back-end (non-survey specific pages, like label sets, CPDB)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
* At the moment, we do not have an exception hierarchy in LimeSurvey&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
Partial application, pipe&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163598</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163598"/>
		<updated>2021-05-19T10:17:25Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Risk */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Why a risk-based approach? Waterfall vs agile, code driven.&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Ambiguity vs uncertainty.&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
risk based on knowledge, evidence, P(A|K)&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
* At the moment, we do not have an exception hierarchy in LimeSurvey&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
Partial application, pipe&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163597</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163597"/>
		<updated>2021-05-19T10:16:56Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Risk */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Why a risk-based approach? Waterfall vs agile, code driven.&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
risk based on knowledge, evidence, P(A|K)&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
* At the moment, we do not have an exception hierarchy in LimeSurvey&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
Partial application, pipe&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163596</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163596"/>
		<updated>2021-05-19T10:16:42Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Risk */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Why a risk-based approach?&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
risk based on knowledge, evidence, P(A|K)&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
* At the moment, we do not have an exception hierarchy in LimeSurvey&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
Partial application, pipe&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163595</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163595"/>
		<updated>2021-05-19T10:14:35Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Risk */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
risk based on knowledge, evidence, P(A|K)&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
* At the moment, we do not have an exception hierarchy in LimeSurvey&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
Partial application, pipe&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163594</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163594"/>
		<updated>2021-05-19T10:13:57Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* The PHP of tomorrow */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
* At the moment, we do not have an exception hierarchy in LimeSurvey&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
Partial application, pipe&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163593</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163593"/>
		<updated>2021-05-19T10:13:43Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Defensive programming and error handling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
* At the moment, we do not have an exception hierarchy in LimeSurvey&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163592</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163592"/>
		<updated>2021-05-19T10:12:48Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Exceptions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
* At the moment, we do not have an exception hierarchy in LimeSurvey&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163591</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163591"/>
		<updated>2021-05-19T10:11:55Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Software evolution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
Architecture design roundtrip, based on risk.&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163590</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163590"/>
		<updated>2021-05-19T10:10:00Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Shape design problem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Imagine you have three shapes: Point, rectangle and circle, with the following properties:&lt;br /&gt;
&lt;br /&gt;
* Point: int x, int y&lt;br /&gt;
* Rectangle: Point bottomLeft, Point topRight&lt;br /&gt;
* Circle: Point center, int radius&lt;br /&gt;
&lt;br /&gt;
Add two behaviours: area(), draw(surface).&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
Example pseudo-code:&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163589</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163589"/>
		<updated>2021-05-19T10:07:59Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Modular coupling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Point (x, y), rectangle (bottomLeft, topRight), circle (point, radius (int))&lt;br /&gt;
&lt;br /&gt;
Area, draw&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
Clearer boundaries between classes and modules make it easier to isolate change.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=File:Coupling.png&amp;diff=163588</id>
		<title>File:Coupling.png</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=File:Coupling.png&amp;diff=163588"/>
		<updated>2021-05-19T10:07:40Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163587</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163587"/>
		<updated>2021-05-19T10:07:29Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Modular coupling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Point (x, y), rectangle (bottomLeft, topRight), circle (point, radius (int))&lt;br /&gt;
&lt;br /&gt;
Area, draw&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
[[File:coupling.png]]&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=File:Cohesion.png&amp;diff=163586</id>
		<title>File:Cohesion.png</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=File:Cohesion.png&amp;diff=163586"/>
		<updated>2021-05-19T10:05:24Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163585</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163585"/>
		<updated>2021-05-19T10:05:14Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Class cohesion */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Point (x, y), rectangle (bottomLeft, topRight), circle (point, radius (int))&lt;br /&gt;
&lt;br /&gt;
Area, draw&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
[[File:cohesion.png]]&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163584</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163584"/>
		<updated>2021-05-19T09:53:39Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Shape design problem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Point (x, y), rectangle (bottomLeft, topRight), circle (point, radius (int))&lt;br /&gt;
&lt;br /&gt;
Area, draw&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
* Which classes &#039;&#039;change together&#039;&#039;?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
LCOM&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163583</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163583"/>
		<updated>2021-05-19T09:48:53Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Shape design problem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Couple of different definition of risk:&lt;br /&gt;
&lt;br /&gt;
* Tuple of probability and severity&lt;br /&gt;
* Triple of consequence, uncertainty and knowledge base&lt;br /&gt;
&lt;br /&gt;
Possible risks related to LimeSurvey (technical or business):&lt;br /&gt;
&lt;br /&gt;
* Data loss&lt;br /&gt;
* Regressions&lt;br /&gt;
* Bad UI (higher churn-rate for users)&lt;br /&gt;
* Hard to maintain (reduced sprint velocity)&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
How know which is the biggest risk? How many risks can be approached at once?&lt;br /&gt;
&lt;br /&gt;
Connection to architecture. Risk --&amp;gt; quality attributes --&amp;gt; architectural decisions.&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords where some but not all models support encryption.&lt;br /&gt;
&lt;br /&gt;
==== Shape design problem ====&lt;br /&gt;
&lt;br /&gt;
Point (x, y), rectangle (bottomLeft, topRight), circle (point, radius (int))&lt;br /&gt;
&lt;br /&gt;
Area, draw&lt;br /&gt;
&lt;br /&gt;
* Cohesion: A class (or module) should be responsible for one thing only&lt;br /&gt;
* Coupling: You should be able to &#039;&#039;isolate change&#039;&#039; in the system&lt;br /&gt;
* Encapsulation: Classes shouldn’t share internal representation&lt;br /&gt;
* Polymorphism: The design should be based on interfaces&lt;br /&gt;
&lt;br /&gt;
Questions to consider:&lt;br /&gt;
&lt;br /&gt;
* How easy would it be to move to a 3D representation?&lt;br /&gt;
* How easy would it be to add a new shape, say, triangle?&lt;br /&gt;
* How easy would it be to add a new behaviour to all shapes, like save/load from a SQL database?&lt;br /&gt;
&lt;br /&gt;
Keep polymorphism and encapsulation - all properties are private.&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do shape.draw(surface)&lt;br /&gt;
&lt;br /&gt;
 forall shapes as shape, do surface.draw(shape) (where shape.getDrawData(), or shape.getPath())&lt;br /&gt;
&lt;br /&gt;
=== Software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
==== Grime and rot ====&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
==== Class cohesion ====&lt;br /&gt;
&lt;br /&gt;
LCOM&lt;br /&gt;
&lt;br /&gt;
==== Modular coupling ====&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Architectural Decision Records ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
https://adr.github.io/&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Regression_and_unit_tests&amp;diff=163318</id>
		<title>Regression and unit tests</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Regression_and_unit_tests&amp;diff=163318"/>
		<updated>2021-05-10T09:49:16Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Alert|title=Warning|text=NEVER run tests on a production system. The tests WILL delete and modify the &amp;lt;code&amp;gt;limesurvey&amp;lt;/code&amp;gt; database as well as the &amp;lt;code&amp;gt;config.php&amp;lt;/code&amp;gt; file. Always do a backup of important data before running the tests.}}&lt;br /&gt;
&lt;br /&gt;
{{Hint|Text=Always make sure new code follows the PSR-12 standard for PHP. Use [https://github.com/squizlabs/PHP_CodeSniffer codesniffer] and [https://phpmd.org/ mess detector] to catch common bugs and style fixes in your code.}}&lt;br /&gt;
{{Hint|Text=Every time you discover a new bug, you should add a regression test to make sure it does not appear again.}}&lt;br /&gt;
&lt;br /&gt;
Since LimeSurvey 2.65.6 you have the possibility to add unit tests to LimeSurvey.&lt;br /&gt;
&lt;br /&gt;
Since LImeSurvey 3.0.0, we use Selenium, headless Firefox (geckodriver) and Facebook web driver to test with the browser from PHPUnit.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
An extensive test suite will give the programmer a better sense of security, and allow him/her to make bigger re-factorizations without risking the soundness of the codebase. In an ideal world, a regression test is added every time a new bug is discovered. That way you make sure you never have to fix the same bug twice.&lt;br /&gt;
&lt;br /&gt;
You can run the tests locally if you want, or you can setup a docker container that mimics the Travis CI environment. Alternatively, you can visit Travis CI after you&#039;ve pushed to github to make sure your commits didn&#039;t break anything. If you think Travis is falsely reporting a bug, you should contact the person who constructed the test.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
(All commands below are executed in the LimeSurvey web root folder.)&lt;br /&gt;
&lt;br /&gt;
The browser tests uses Firefox, so you have to install it before running.&lt;br /&gt;
&lt;br /&gt;
# A good idea is to clear the database and make a fresh install using the command-line (backup any data you want to save first):&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;DROP DATABASE limesurvey; CREATE DATABASE limesurvey&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;$ DBENGINE=INNODB php application/commands/console.php install admin password TravisLS no@email.com verbose&amp;lt;/syntaxhighlight&amp;gt;Make sure your config.php has the correct database credentials.&lt;br /&gt;
# To enable unit testing, you have to issue the command&amp;lt;br/&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ touch enabletests&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# To install Facebook web driver and PHPUnit, run&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;$ composer install&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# &amp;lt;strike&amp;gt;Install [https://nodejs.org/en/ node] (needed for EM tests).&amp;lt;/strike&amp;gt; Not used right now.&lt;br /&gt;
# Download Selenium and the geckodriver:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;$ wget &amp;quot;https://selenium-release.storage.googleapis.com/3.7/selenium-server-standalone-3.7.1.jar&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;$ wget &amp;quot;https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;$ tar xvzf geckodriver-v0.23.0-linux64.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Make sure you have Java installed, and start Selenium in a separate terminal:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;$ java -jar selenium-server-standalone-3.7.1.jar -enablePassThrough false&amp;lt;/syntaxhighlight&amp;gt;If you want to use the headless browser, prepend with:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;MOZ_HEADLESS=1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Run tests with &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;$ DOMAIN=&amp;lt;local domain&amp;gt; ./third_party/bin/phpunit &amp;lt;/syntaxhighlight&amp;gt;where &amp;quot;&amp;lt;local domain&amp;gt;&amp;quot; is replaced with your localhost domain, e.g. &amp;quot;localhost&amp;quot; or &amp;quot;localhost/limesurvey&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
==== Vagrant Box Setup ====&lt;br /&gt;
# Install [https://www.virtualbox.org/ Virtualbox]&lt;br /&gt;
# Install [https://www.vagrantup.com/downloads.html Vagrant]&lt;br /&gt;
# If you have used Docker before and/or have Hyper-V activated you have to disable it, Virtualbox and Hyper-V can not run simultaneously. You can do this by executing following in powershell. &amp;lt;syntaxhighlight&amp;gt;bcdedit /set hypervisorlaunchtype off&amp;lt;/syntaxhighlight&amp;gt; If the command is denied make sure you run your powershell as system administrator: Press Windows+R to open the “Run” box. Type “powershell” into the box and then press Ctrl+Shift+Enter to run the command as an administrator.&lt;br /&gt;
# Clone the Vagrant Box into your desired folder, the Vagrant Box can be found [https://github.com/LimeSurvey/LimeSurvey-Vagrant here]&amp;lt;syntaxhighlight lang=&amp;quot;cmd&amp;quot;&amp;gt;git clone https://github.com/LimeSurvey/LimeSurvey-Vagrant.git YOURFOLDERNAME&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Modify the file &#039;Vagrantfile&#039; in the root folder and edit the IP address if it is already taken and set the PHP version to your requirements (usually 5.6 or 7.4). If you need other versions, then please look at the README file in the [https://github.com/LimeSurvey/LimeSurvey-Vagrant Repository]. There are other settings like the SQL and Xdebug settings that can be customized to your needs.&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#Custom variable for installation&lt;br /&gt;
ip                = &amp;quot;10.0.0.10&amp;quot;&lt;br /&gt;
#php setup&lt;br /&gt;
phpversion        = &amp;quot;7.4&amp;quot; &lt;br /&gt;
#mysql setup&lt;br /&gt;
mysqlRootPass     = &amp;quot;password&amp;quot;&lt;br /&gt;
mysqlDBName       = &amp;quot;limesurvey&amp;quot;&lt;br /&gt;
mySqlDBUser       = &amp;quot;limesurvey&amp;quot;&lt;br /&gt;
mySqlDBPassword   = &amp;quot;password&amp;quot;&lt;br /&gt;
#postgres setup&lt;br /&gt;
postgresDB        = &amp;quot;limesurvey&amp;quot;&lt;br /&gt;
postgresPassword  = &amp;quot;password&amp;quot;&lt;br /&gt;
#limesurvey setup&lt;br /&gt;
limeSurveyBranch  = &amp;quot;master&amp;quot;&lt;br /&gt;
#IDE KEY for Xdebug&lt;br /&gt;
ideKey  = &amp;quot;VSCODE&amp;quot;&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
# Remove the # before&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#enable this line on windows machine (Optional).&lt;br /&gt;
#vb.customize [&amp;quot;modifyvm&amp;quot;, :id, &amp;quot;--natdnshostresolver1&amp;quot;, &amp;quot;on&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
vb.customize [&amp;quot;modifyvm&amp;quot;, :id, &amp;quot;--natdnshostresolver1&amp;quot;, &amp;quot;on&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Set the branch you to checkout during Setup, by default it is set to the &amp;quot;master&amp;quot; branch &amp;lt;syntaxhighlight&amp;gt;limeSurveyBranch  = &amp;quot;master&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Start vagrant in the root Folder where the Vagranfile is located:&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
vagrant up&amp;lt;/syntaxhighlight&amp;gt;Be patient, this will take 10-15 minutes on the first start because various resources need to be downloaded and installed.&lt;br /&gt;
# You can now access your Installation by typing the hostname set in the Vagrantfile or using the ip address and appending a &amp;quot;/&amp;quot;. By default this is set to &amp;quot;limesurvey&amp;quot;. Follow the the normal Limesurvey Installation process. The Database username is &amp;quot;limesurvey&amp;quot; and password is &amp;quot;password&amp;quot; unless changed.&lt;br /&gt;
# Install your desired PHP version on your PC, not in the Vagrant Box&lt;br /&gt;
# Run composer installation inside your local Installation Folder in Windows &amp;quot;VAGRANTFOLDER/INSTALLATIONFOLDER&amp;quot; &amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
composer install&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Download [https://selenium-release.storage.googleapis.com/3.7/selenium-server-standalone-3.7.1.jar Selenium] and the [https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-win64.zip geckodriver]. Extract the geckodriver.exe and place it inside the installation folder and place the selenium jar file at the same location.&lt;br /&gt;
# Create a file named &amp;quot;enabletests&amp;quot; in the installation root folder.&lt;br /&gt;
# Make sure you have Java installed, and start Selenium in a separate terminal:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;java -jar selenium-server-standalone-3.7.1.jar -enablePassThrough false&amp;lt;/syntaxhighlight&amp;gt;If you want to use the headless browser make an environment variable called &amp;quot;MOZ_HEADLESS&amp;quot; and set it to 1, in the powershell:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;$Env:MOZ_HEADLESS = 1&amp;lt;/syntaxhighlight&amp;gt; This will only be set in the current powershell window. If you want to set it permanently, open the environment variables in windows and create the variable. You will have to reload the powershell window for the settings to take effect.&lt;br /&gt;
# To run tests log in to your Vagrantbox from the vagrant folder and navigate to the installation folder &amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
vagrant ssh&lt;br /&gt;
# by default the installation folder is mapped to /var/www&lt;br /&gt;
cd /var/www/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Now you can run tests! All tests are located inside the &amp;quot;tests&amp;quot; folder. To run a specific test:&amp;lt;syntaxhighlight&amp;gt;DOMAIN=localhost ./third_party/bin/phpunit ./tests/PATH/TO/THE/FILE&amp;lt;/syntaxhighlight&amp;gt;Example: &amp;lt;syntaxhighlight&amp;gt;DOMAIN=localhost ./third_party/bin/phpunit ./tests/functional/backend/AdminViewsTest.php&amp;lt;/syntaxhighlight&amp;gt; If you should have permission issues set the synced_folder fmode option in the Vagrantfile to &amp;quot;fmode=777&amp;quot;. For the new permission setting to work you must shutdown the vagrant machine and start it up again. Type &amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
sudo shutdown now&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; in the SSH console and after the machine has shut down, start it up again using &amp;lt;syntaxhighlight&amp;gt;vagrant up&amp;lt;/syntaxhighlight&amp;gt; as shown above.&lt;br /&gt;
&lt;br /&gt;
=== Docker Setup ===&lt;br /&gt;
Install [https://docs.docker.com/docker-for-windows/install/ docker on windows].&lt;br /&gt;
&lt;br /&gt;
Open your command prompt and enter&lt;br /&gt;
&lt;br /&gt;
 $ docker run --name travis-debug -dit travisci/ci-garnet:packer-1512502276-986baf0 /sbin/init&lt;br /&gt;
&lt;br /&gt;
[[Using PHP from docker]]&lt;br /&gt;
&lt;br /&gt;
=== Mac ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
&lt;br /&gt;
After all steps for the installation are completed, you should be able to write&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
$ PASSWORD=adminpassword DOMAIN=your_limesurvey_domain phpunit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in the LimeSurvey root folder to run the tests. &lt;br /&gt;
&lt;br /&gt;
If LimeSurvey is installed on your web root, use &amp;quot;DOMAIN=localhost&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This is an example of the output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
 $ phpunit&lt;br /&gt;
 PHPUnit 5.6.2 by Sebastian Bergmann and contributors.&lt;br /&gt;
&lt;br /&gt;
 ....                                                                4 / 4 (100%)&lt;br /&gt;
&lt;br /&gt;
Time: 713 ms, Memory: 14.00MB&lt;br /&gt;
&lt;br /&gt;
OK (4 tests, 24 assertions)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can get some more information if you use the switch &amp;lt;code lang=&amp;quot;html&amp;quot;&amp;gt;--testdox&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
$ phpunit --testdox&lt;br /&gt;
PHPUnit 5.6.2 by Sebastian Bergmann and contributors.&lt;br /&gt;
&lt;br /&gt;
ls\tests\DateTimeForwardBack&lt;br /&gt;
 [x] Q 1&lt;br /&gt;
&lt;br /&gt;
ls\tests\DateTime&lt;br /&gt;
 [x] Wrong input&lt;br /&gt;
 [x] Correct date format&lt;br /&gt;
 [x] Q 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
During testing, you might want to use &amp;lt;code lang=&amp;quot;html&amp;quot;&amp;gt;--group &amp;lt;test group&amp;gt;&amp;lt;/code&amp;gt; and/or &amp;lt;code lang=&amp;quot;html&amp;quot;&amp;gt;--stop-on-failure&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== What can be tested? ==&lt;br /&gt;
&lt;br /&gt;
Right now, there are tests for the expression manager, qanda and updatedb helper. The Selenium driver makes it also possible to test the full browser experience, including JavaScript.&lt;br /&gt;
&lt;br /&gt;
== What should be tested? ==&lt;br /&gt;
&lt;br /&gt;
Everything that can be reported as a bug should be tested, but especially critical is of course the relation between user input and database, that is, that the correct answer is saved.&lt;br /&gt;
&lt;br /&gt;
== Adding a test ==&lt;br /&gt;
&lt;br /&gt;
First of all, make sure to head over to the [https://phpunit.de/ phpunit] web site and read the introduction.&lt;br /&gt;
&lt;br /&gt;
When done with that, I will assume you want to add a new test for a question type. Tests for questions are stored in folder &amp;lt;code lang=&amp;quot;html&amp;quot;&amp;gt;tests/questions&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
You should create a minimal survey to reproduce your bug. Export it as an lss file and put it in &amp;lt;code lang=&amp;quot;html&amp;quot;&amp;gt;tests/data/surveys&amp;lt;/code&amp;gt;. Then create a class for your test. Name the file the same as the class, like &amp;quot;DateTimeForwardBackTest.php&amp;quot;. The class name must end with &amp;quot;Test&amp;quot;, so phpunit can find it.&lt;br /&gt;
&lt;br /&gt;
Inside the class you might want to add code to import and delete the survey. I usually do this class-wide, which means that the survey gets imported when the class is loaded and then deleted when all tests in the class is done (whether failed or successfully).&lt;br /&gt;
&lt;br /&gt;
The static methods &amp;lt;code lang=&amp;quot;html&amp;quot;&amp;gt;setupBeforeClass&amp;lt;/code&amp;gt; and &amp;lt;code lang=&amp;quot;html&amp;quot;&amp;gt;teardownAfterClass&amp;lt;/code&amp;gt; is used to setup and teardown the test fixture, respectively. You can read more about fixtures in the [https://phpunit.de/manual/current/en/fixtures.html phpunit manual].&lt;br /&gt;
&lt;br /&gt;
Let&#039;s list what we know so far:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
namespace ls\tests;&lt;br /&gt;
&lt;br /&gt;
use PHPUnit\Framework\TestCase;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @since 2017-06-16&lt;br /&gt;
 * @author Olle Haerstedt &lt;br /&gt;
*/&lt;br /&gt;
class DateTimeDefaultAnswerExpressionTest extends \TestCase&lt;br /&gt;
{&lt;br /&gt;
  public static function setupBeforeClass()&lt;br /&gt;
  {&lt;br /&gt;
    // Init stuff&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public static function teardownAfterClass()&lt;br /&gt;
  {&lt;br /&gt;
    // Tear down what you setup before.&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test with Selenium and the Facebook web driver, check out the [https://github.com/facebook/php-webdriver/wiki wiki page] on github.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
== Travis ==&lt;br /&gt;
&lt;br /&gt;
All tests are run automatically after each commit using Travis continuous integration. https://travis-ci.org/LimeSurvey/LimeSurvey/&lt;br /&gt;
&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163076</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163076"/>
		<updated>2021-05-05T09:20:01Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Types of classes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
 &lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object (similar to resource)&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163075</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163075"/>
		<updated>2021-05-05T09:18:46Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Types of classes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Repository classes (database interaction, like splitting ActiveRecord in two parts: Entity class and database interaction)&lt;br /&gt;
* DAO, data access object&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163074</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163074"/>
		<updated>2021-05-05T09:15:20Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Types of classes in LimeSurvey */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers (Yii)&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163073</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163073"/>
		<updated>2021-05-05T09:13:25Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Types of classes in LimeSurvey */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions (not classes, yet...), should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163072</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=163072"/>
		<updated>2021-05-05T09:09:09Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Types of classes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth should be kept reasonable shallow to avoid conflicts between methods, spooky action at a distance. Compare with Yii 3, where controllers no longer inherit anything.&lt;br /&gt;
&lt;br /&gt;
Some people advocate for composition instead of inheritance. Lots of blog posts available for more info.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits in PHP. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
design pattern rot --&amp;gt; lose behaviour relevant for pattern (active record without db connection)&lt;br /&gt;
&lt;br /&gt;
design patter grime --&amp;gt; add behaviour irrelevant for pattern (active record that&#039;s also a singleton)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Heuristics to consider but not follow slavishly.&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
Classes with state should not have dependencies with IO (it&#039;s OK to depend on classes in the same application layer, e.g. other DTO classes), and vice versa. ActiveRecord is an example which violates this principle.&lt;br /&gt;
&lt;br /&gt;
    class Survey&lt;br /&gt;
      // Confusion - first params are domain state, last a resource&lt;br /&gt;
      __construct(id, language, dbConnection)&lt;br /&gt;
&lt;br /&gt;
If a method does not call &amp;quot;this&amp;quot;, should it instead be outside the class? A separate class or function, maybe.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Entity (domain class; has state and validation behaviour)&lt;br /&gt;
* Resource (database connection, file, curl, logger; state is in the resource (open, closed))&lt;br /&gt;
* Builders (e.g. query builder, HTML message builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email address to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic (also CLI controllers)&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* HTML helpers&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation? Command-query (function core, imperative shell) is one type of separation. Other separation between layers, e.g. logic layer and data persistency layer (similar in thought).&lt;br /&gt;
&lt;br /&gt;
Other separation, that one class should have a clear purpose. A design pattern should only implement &#039;&#039;one&#039;&#039; pattern, not multiple, and not half a pattern.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a problem with &amp;quot;helper&amp;quot; or &amp;quot;manager&amp;quot; classes that they can grow indefinitely, but might be better than nothing. Example when moving business logic out of ActiveRecord class to a helper class it can sometimes hard to be more specific than &amp;quot;helper&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Command classes are named after what they &#039;&#039;do&#039;&#039; and imperatively, e.g. CreateSurvey&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
* Class properties of ActiveRecord classes will follow the naming of the database, using underscore instead of camel case (and ActiveRecord classes should have &#039;&#039;no&#039;&#039; other properties than those (excluding the database connection injected by the framework))&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162388</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162388"/>
		<updated>2021-04-30T11:46:10Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Assertions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
Moving side-effects up makes sure we can have a healthy ratio between integrity tests with scripted browser, and unit-tests; maybe around 20-80.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost (since PHP 7).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s a list of items: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough (for lists, not for associative arrays - see below)&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://www.approxion.com/capital-offenses-how-to-handle-abbreviations-in-camelcase/ How to Handle Abbreviations in CamelCase]&lt;br /&gt;
&lt;br /&gt;
[https://stackoverflow.com/questions/15526107/acronyms-in-camelcase Acronyms in CamelCase]&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Why do I have to refactor code that works?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Sadly, it&#039;s not enough for the code to live up to the current specification and requirements, it must also be protected against future changes, regressions. Adding tests is the best way for this. Lots of people might read and change the LimeSurvey code the next 5-10 years. The test suite creates a security layer to make sure the specification is fulfilled even as the code changes.&lt;br /&gt;
&lt;br /&gt;
Tests are not a magical solution, however. It does not protect us against other degradation of the code quality, like classes gaining too much responsibility (too many methods, unrelated methods, too many properties).&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;What&#039;s the correct ratio between integrity tests and unit tests?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
todo, Don&#039;t know. Different pros and cons.&lt;br /&gt;
&lt;br /&gt;
* Unit-test can be run in parallel and are faster&lt;br /&gt;
* Unit-tests don&#039;t need setup and teardown&lt;br /&gt;
&lt;br /&gt;
Cons of unit-tests is that they need a bigger effort on the side of the developer to make them possible, for example injecting the dependencies properly to enable mocking, or lifting side-effects up in the stack trace.&lt;br /&gt;
&lt;br /&gt;
=== How to disagree ===&lt;br /&gt;
&lt;br /&gt;
All rules can be broken. To break a rule, you should reason in terms of risk and the assumptions used in this guide.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Assumptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Regressions are the biggest technical risk for LimeSurvey the last five years (2021)&lt;br /&gt;
# Automated tests are the best risk mitigation technique against regressions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Possible disagreement:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# A certain piece of code has very low &#039;&#039;probability&#039;&#039; of regressions (maybe we &amp;quot;know&amp;quot; no one else will every touch or change it?)&lt;br /&gt;
# Regression in a certain piece of code would have very low &#039;&#039;impact&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Installation_-_LimeSurvey_CE&amp;diff=162324</id>
		<title>Installation - LimeSurvey CE</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Installation_-_LimeSurvey_CE&amp;diff=162324"/>
		<updated>2021-04-23T13:44:50Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;languages /&amp;gt;&lt;br /&gt;
&amp;lt;translate&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:2--&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:52--&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;simplebox&amp;quot;&amp;gt; Note: These installation instructions are for version 2.0 or later. Older instructions for 1.92 can be found [[Installation Version 1.92 or older|here]] and update instructions can be found [[Upgrading from a previous version|here]]. &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=General= &amp;lt;!--T:3--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:4--&amp;gt;&lt;br /&gt;
The LimeSurvey project provides two ways to install LimeSurvey:&lt;br /&gt;
*On your webserver:&amp;lt;br /&amp;gt;In this case, just follow the instructions on this page starting with step 1.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:5--&amp;gt;&lt;br /&gt;
*On your local Windows PC:&amp;lt;br /&amp;gt;You usually only want to install LimeSurvey on your local PC if you want to create surveys locally (mainly for speed or development/test reasons) and upload them later to your live LimeSurvey installation on a web server. For this purpose, we have prepared a special LimeSurvey package which includes the web server, the database, the LimeSurvey application, and [[Installation of the LimeSurvey XAMPP package|instructions how to install it]].&lt;br /&gt;
&lt;br /&gt;
=Make sure you can use LimeSurvey on your website= &amp;lt;!--T:7--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:8--&amp;gt;&lt;br /&gt;
Make sure your server meets the following additional requirements.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:51--&amp;gt;&lt;br /&gt;
{{Note|The minimum set of requirements will also be checked during the installation process!}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:9--&amp;gt;&lt;br /&gt;
The minimum set of requirements:&lt;br /&gt;
* Minimum 250 MB disk space.&lt;br /&gt;
* MySQL 5.5.3 or later &#039;&#039;&#039;OR&#039;&#039;&#039; Microsoft SQL Server 2005 or later &#039;&#039;&#039;OR&#039;&#039;&#039; Postgres 9 or later.&lt;br /&gt;
* Minimum PHP 7.0 up to PHP 7.4 (PHP 8 is not yet supported, but work is ongoing); however, we recommend PHP 7.4 with the following modules/libraries enabled:&lt;br /&gt;
** [[Installation FAQ#Requirements page#What is the mbstring (Multibyte String Functions) library|mbstring (Multibyte String Functions)]] extension library.&lt;br /&gt;
** PDO database driver for MySQL (pdo_mysql or pdo_mysqli) or Postgres (pdo_pgsql) or MSSQL ([https://php.net/manual/en/ref.pdo-sqlsrv.php pdo_sqlsrv] for Windows and [http://www.php.net/manual/en/ref.pdo-dblib.php pdo_dblib] for Linux).&lt;br /&gt;
** Also, we assume in general that all PHP default libraries are enabled &lt;br /&gt;
*** hash&lt;br /&gt;
*** session&lt;br /&gt;
*** openssl or mcrypt&lt;br /&gt;
*** fileinfo&lt;br /&gt;
*** etc …&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:47--&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;simplebox&amp;quot;&amp;gt; Note: If you are connecting from Linux to a MSSQL database on a Windows server using the dblib PDO driver, then the underlying FreeTDS needs to be set up correctly. Check out the [[Troubleshooting]] page for more information.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:11--&amp;gt;&lt;br /&gt;
Optional PHP Extensions:&lt;br /&gt;
* &#039;&#039;&#039;GD-Library&#039;&#039;&#039; &#039;&#039;&#039;with FreeType support&#039;&#039;&#039; installed is needed for captchas, nice charts in statistics or to upload images via the HTML editor - see [https://php.net/manual/image.setup.php PHP GD-Library Extension documentation]&lt;br /&gt;
* &#039;&#039;&#039;IMAP&#039;&#039;&#039; (pretty standard) is needed for the [[Email bounce tracking system|email bounce tracking system]] - see [https://php.net/manual/imap.setup.php PHP IMAP Extension documentation]&lt;br /&gt;
* &#039;&#039;&#039;LDAP&#039;&#039;&#039; installed is needed to import survey participants using LDAP - see [https://php.net/manual/en/book.ldap.php PHP LDAP Documentation]&lt;br /&gt;
* &#039;&#039;&#039;Zip&#039;&#039;&#039; (pretty standard) is needed for template upload, import of .zip archived resources, and excel export - see [https://php.net/manual/zip.setup.php PHP Zip Extension documentation]&lt;br /&gt;
* &#039;&#039;&#039;Zlib&#039;&#039;&#039; (pretty standard) is needed for the [[ComfortUpdate]] - see [https://php.net/manual/zlib.setup.php PHP Zlib Extension documentation]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:12--&amp;gt;&lt;br /&gt;
In most cases, your web server provider or system administrator will be able to help you out with these optional components.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:14--&amp;gt;&lt;br /&gt;
If you want to run Microsoft&#039;s IIS server, taking a look at [[Installation on XP with IIS]] might also help.&lt;br /&gt;
&lt;br /&gt;
==Browser compatibility== &amp;lt;!--T:46--&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;For the survey-taking side&#039;&#039;&#039;, we currently support IE11 (intranet-mode excluded) and all newer comparable browsers like Edge, FF, Chrome, Opera etc. with activated JavaScript. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:53--&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;For the administration part&#039;&#039;&#039;, we currently support Edge and all newer comparable browsers like FF, Chrome, Opera etc. with activated JavaScript. &amp;lt;u&amp;gt;We do not support any Internet Explorer versions in the administration part anymore.&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Screen sizes=== &amp;lt;!--T:48--&amp;gt;&lt;br /&gt;
We support any reasonable screen size for survey taking (360px x 640px and up).&lt;br /&gt;
The minimum supported screen resolution for the LimeSurvey administration page is 1280px x 1024px.&lt;br /&gt;
&lt;br /&gt;
==Hosting for LimeSurvey== &amp;lt;!--T:15--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:16--&amp;gt;&lt;br /&gt;
LimeSurvey should work with most hosting companies that provide PHP and MySQL. If you want a ready-installed version of LimeSurvey, you could try [https://www.limesurvey.org/editions-and-prices/limesurvey-pro/editions-and-prices-professional LimeSurvey Cloud], else check out our [[LimeSurvey-compatible hosting companies|list of LimeSurvey-compatible hosting companies]].&lt;br /&gt;
&lt;br /&gt;
=Download the LimeSurvey package= &amp;lt;!--T:17--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:18--&amp;gt;&lt;br /&gt;
[https://www.limesurvey.org/en/downloads/category/25-latest-stable-release Download] the latest stable LimeSurvey zip package (there are also gzip, bz2 and 7zip packages available for your convenience) and save it to your local disk.&lt;br /&gt;
&lt;br /&gt;
=Unpack the LimeSurvey package= &amp;lt;!--T:19--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:20--&amp;gt;&lt;br /&gt;
Uncompress the zip file into a dedicated directory/folder using your preferred compression software. When you uncompress the file make sure that you uncompress the directory structure with the files (this is default behavior for most compression programs). The path name for the installation directory should not include &#039;(&#039; or &#039;)&#039; special characters, but it may include the &#039;&#039;&#039;space&#039;&#039;&#039; character.&lt;br /&gt;
&lt;br /&gt;
=Collect information about your server= &amp;lt;!--T:21--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:22--&amp;gt;&lt;br /&gt;
You will need to know the following things to install LimeSurvey on your web server correctly:&lt;br /&gt;
*Your intended website URL where your scripts will reside (e.g. &#039;&#039;http://my.domain.com/limesurvey&#039;&#039;).&lt;br /&gt;
*The physical disk location on your server where your scripts will reside (e.g. &#039;&#039;/home/usr/htdocs/limesurvey&#039;&#039;).&lt;br /&gt;
*The IP/net location of your database server (i.e. &#039;&#039;localhost&#039;&#039;).&lt;br /&gt;
*If your database server uses a non-standard port, find out which port it is.&lt;br /&gt;
*Your user name and password for your database server.&lt;br /&gt;
*With most providers you can&#039;t let the script create the database, but have to do it yourself.  If you are using such a provider, please create a database (for example &#039;&#039;limesurvey&#039;&#039;) yourself.&lt;br /&gt;
&lt;br /&gt;
=Upload the files to your web server= &amp;lt;!--T:23--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:24--&amp;gt;&lt;br /&gt;
Using your FTP program (e.g. FileZilla), connect to your web server and create a directory to store your scripts. Then upload the files using the directory structure they are in. Make sure you &#039;&#039;&#039;upload the files in binary mode&#039;&#039;&#039; (check the settings of your FTP program). Please note that uploading in ASCII mode can lead to uncommon errors (e.g. &#039;class not found&#039; error during installation), also images may not be uploaded correctly.&lt;br /&gt;
&lt;br /&gt;
=Set directory permissions= &amp;lt;!--T:25--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:26--&amp;gt;&lt;br /&gt;
For the script to work properly it needs certain access to some directories - this applies especially to Linux/*nix systems.&lt;br /&gt;
*The &#039;&#039;&#039;&amp;quot;/limesurvey/tmp&amp;quot;&#039;&#039;&#039; directory (and all its subdirectories and files) are used for imports &amp;amp; uploads and should be set to &#039;&#039;Read &amp;amp; Write&#039;&#039; for your webserver.&lt;br /&gt;
*The &#039;&#039;&#039;&amp;quot;/limesurvey/upload/&amp;quot;&#039;&#039;&#039; directory (and all its subdirectories and files) must also have &#039;&#039;Read &amp;amp; Write&#039;&#039; for your webserver in order to enable picture and media files upload.&lt;br /&gt;
*The &#039;&#039;&#039;&amp;quot;/limesurvey/application/config/&amp;quot;&#039;&#039;&#039; directory also needs &#039;&#039;Read &amp;amp; Write&#039;&#039; permissions for your webserver.&lt;br /&gt;
*The other directories can be set to &#039;&#039;Read Only&#039;&#039;. You may wish in Linux/Unix to set the permissions on each file within the /limesurvey/admin directory to &#039;&#039;Read Only&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:27--&amp;gt;&lt;br /&gt;
{{Alert|If you are using Linux, then setting permissions depends on your webserver configuration. For example, if you use Apache HTTPD on CentOS, type &#039;&#039;&#039;chmod -R 755 &amp;lt;directory&amp;gt; ; chown -R apache &amp;lt;directory&amp;gt;&#039;&#039;&#039;. This will give everyone access to read, and your webserver will have the permission to write. Check the [https://www.linux.com/learn/tutorials/309527-understanding-linux-file-permissions following link] for more information on Linux permissions.}}&lt;br /&gt;
&lt;br /&gt;
=Create a database user= &amp;lt;!--T:28--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:29--&amp;gt;&lt;br /&gt;
LimeSurvey will create a database and the tables inside it. To be able to do this, it will need the user name and password of a database user. We generally recommend creating a database user with the following permissions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:30--&amp;gt;&lt;br /&gt;
- MySQL: SELECT, CREATE, INSERT, UPDATE, DELETE, ALTER, DROP, INDEX&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:31--&amp;gt;&lt;br /&gt;
- PostgreSQL: SELECT, INSERT, UPDATE, DELETE, TRUNCATE, TRIGGER&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:32--&amp;gt;&lt;br /&gt;
- Microsoft SQL Server: &#039;&#039;Unknown - if you can find out this information, please complete it&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=Run the installation script= &amp;lt;!--T:33--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:34--&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Go to &amp;quot;example.org/limesurvey/admin&amp;quot;&#039;&#039;&#039;. If you configured everything correctly, the LimeSurvey installer should be starting - just follow the instructions. You will be asked whether you wish to create the database and/or create the tables inside the database. LimeSurvey will then create the needed tables in the database.&lt;br /&gt;
&lt;br /&gt;
=Connect to the administration script for the first time= &amp;lt;!--T:35--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:36--&amp;gt;&lt;br /&gt;
You are done when the installation finishes! Open your browser and enter the URL to the LimeSurvey administration. Assuming you used &#039;limesurvey&#039; as the directory name to store the files in, this will be something like &amp;quot;http://www.example.com/limesurvey/admin&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:37--&amp;gt;&lt;br /&gt;
You should get a login screen. The default login credentials (if they have not been changed during the installation) are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:38--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&#039;html&#039; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:49--&amp;gt;&lt;br /&gt;
User: admin&lt;br /&gt;
Password: password&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:50--&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:41--&amp;gt;&lt;br /&gt;
You will be prompted to change the default password after you logged in. Have fun!&lt;br /&gt;
&lt;br /&gt;
=Further configuration= &amp;lt;!--T:54--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Caching== &amp;lt;!--T:55--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:56--&amp;gt;&lt;br /&gt;
LimeSurvey uses the Yii file cache by default, but it can be configured to use other cache engines. Possible other caches are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:57--&amp;gt;&lt;br /&gt;
* Memcached&lt;br /&gt;
* Redis&lt;br /&gt;
* WinCache&lt;br /&gt;
* XCache&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:58--&amp;gt;&lt;br /&gt;
Please see the [https://www.yiiframework.com/doc/api/1.1/CCache Yii documentation] for full description.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:59--&amp;gt;&lt;br /&gt;
Since LimeSurvey 4.0.0 you also have the possibility to cache calculations in the ExpressionScript. This can provide a significant speed-up depending on survey complexity. To enable the emcache, add this in your config.php file (components array):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:60--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&#039;emcache&#039; =&amp;gt; array(&#039;class&#039; =&amp;gt; &#039;CFileCache&#039;),&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:61--&amp;gt;&lt;br /&gt;
As with the main Yii cache, other cache systems can be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:62--&amp;gt;&lt;br /&gt;
{{Note| The emcache is not compatible with randomized surveys, and will be automatically turned off in such cases.}}&lt;br /&gt;
&lt;br /&gt;
=What if I have problems...= &amp;lt;!--T:42--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:43--&amp;gt;&lt;br /&gt;
Like all computer programs, most of the time things will work just like the instructions say, but sometimes they just won&#039;t. There are too many possible reasons for things not going according to plan to describe here. If you have trouble, first check out the [[Installation FAQ]]. If you don&#039;t find your answer there, please post your problem and any error messages in the [https://www.limesurvey.org/forum LimeSurvey forums], open a [https://www.limesurvey.org/contact-us support ticket], or join the [irc://irc.freenode.net/limesurvey|LimeSurvey IRC channel]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:44--&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;simplebox&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&#039;&#039;&#039;[https://www.limesurvey.org/community-services/live-chat Direct link to the official LimeSurvey IRC Channel]&#039;&#039;&#039;&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/translate&amp;gt;&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162323</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162323"/>
		<updated>2021-04-23T12:15:54Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Types of classes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
todo, pure vs effectful classes&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162322</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162322"/>
		<updated>2021-04-23T12:10:27Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Types of classes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
todo: dependencies between layers, dependencies to the framework&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;! Better: rmdir($recursive = false), rmdir or removeFolder.&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Controversial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;? Still hard to read for someone who just started? But Yii uses t() too? Other frameworks use double underscore (Wordpress, Laravel) as function name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162299</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162299"/>
		<updated>2021-04-21T15:24:20Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Function filename and namespace */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies or side-effects, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162298</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162298"/>
		<updated>2021-04-21T13:08:04Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Function filename and namespace */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock them.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162297</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162297"/>
		<updated>2021-04-21T13:07:49Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Function filename and namespace */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that functions outside classes cannot be mocked, so only use them when you &#039;&#039;know&#039;&#039; you won&#039;t need to mock it.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162296</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162296"/>
		<updated>2021-04-21T13:07:13Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Function name */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function filename and namespace ===&lt;br /&gt;
&lt;br /&gt;
If your function has dependencies, it should be put into a class instead, in which the dependencies can be injected. Another alternative is to put the dependencies as argument in the function, although this muffles the relationship between input and output. Again, this is to increase testability.&lt;br /&gt;
&lt;br /&gt;
todo, autoloading of functions and folder location&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162295</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162295"/>
		<updated>2021-04-21T13:03:30Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Class name */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;lime_users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162294</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=162294"/>
		<updated>2021-04-21T12:51:42Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* &amp;quot;Where do I cut?&amp;quot; */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique among others.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured? Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise, objective and suggest solutions.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
data loss as most severe risk for LS&lt;br /&gt;
&lt;br /&gt;
usability a business risk - hard to use or understand, people will choose another tool&lt;br /&gt;
&lt;br /&gt;
risks relevant when fixing bugs&lt;br /&gt;
&lt;br /&gt;
risks relevant when adding new features (lack of testing, wrong requirements, scope creep)&lt;br /&gt;
&lt;br /&gt;
risks relevant when refactoring (regressions)&lt;br /&gt;
&lt;br /&gt;
unit tests and QA as risk mitigation&lt;br /&gt;
&lt;br /&gt;
partial deployment, alpha/beta/rc, risk communication&lt;br /&gt;
&lt;br /&gt;
software rot, design pattern rot/grime, software evolution&lt;br /&gt;
&lt;br /&gt;
rot = non-pattern related code&lt;br /&gt;
&lt;br /&gt;
testing by community, bug fixing by community&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system. Libraries can be seen as a module system.&lt;br /&gt;
&lt;br /&gt;
Examples of implicit modules in LimeSurvey:&lt;br /&gt;
&lt;br /&gt;
* Front-end (survey taking)&lt;br /&gt;
* Back-end (survey design)&lt;br /&gt;
* Survey theme editing&lt;br /&gt;
* Expression manager&lt;br /&gt;
* Plugin system&lt;br /&gt;
&lt;br /&gt;
In the future, they need to be separated and implemented more clearly as whatever Yii 3 module system is in use.&lt;br /&gt;
&lt;br /&gt;
Some data models are common for all modules, e.g. surveys and users. Can have a &amp;quot;Common&amp;quot; module for such data.&lt;br /&gt;
&lt;br /&gt;
todo, relation to Yii config system&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* Avoid empty OOP ceremony, like defining getters and setters for all properties. Might as well make them public, &#039;&#039;unless&#039;&#039; they have invariants that need to be upheld, e.g. &amp;quot;this number is always between 1 and 10&amp;quot; or &amp;quot;this string is maximum length 20&amp;quot;.&lt;br /&gt;
* Almost always avoid static methods. They cannot be mocked. One exception is (possibly) factory methods (Foo::make(...)).&lt;br /&gt;
* Inject all dependencies. This will make sure the class can be unit-tested (unit-tests have several benefits over integrity tests - they can be run in parallel and don&#039;t require any setup and teardown)&lt;br /&gt;
* A second way to achieve testability is to factor out the methods that access the dependencies&lt;br /&gt;
&lt;br /&gt;
todo, state vs behaviour, state vs dependencies&lt;br /&gt;
&lt;br /&gt;
=== OOP design ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Start from top-down, with domain, behaviour, interaction between objects, database design, pseudo-code if needed. Can also include scenarios, use-cases and UML diagrams.&lt;br /&gt;
&lt;br /&gt;
Object reification - not only domain models, but also domain actions, relations and activities can be objects, e.g. a class ActivateSurvey or ExportSurvey.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
Inheritance vs composition.&lt;br /&gt;
&lt;br /&gt;
Multiple inheritance is done with traits. Use-case: ActiveRecords that support encryption, but not all.&lt;br /&gt;
&lt;br /&gt;
=== Design pattern grime and software evolution ===&lt;br /&gt;
&lt;br /&gt;
Design rot after time - how to keep it tight? (Grime.)&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
Keep a clear separation between classes with state and classes with no state.&lt;br /&gt;
&lt;br /&gt;
todo, elaborate&lt;br /&gt;
&lt;br /&gt;
=== Types of classes ===&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders (e.g. query builder)&lt;br /&gt;
* Data-transfer object (no behaviour; pass to views to have better documentation and intent than associative arrays)&lt;br /&gt;
* Value classes (e.g. Email to wrap email string around)&lt;br /&gt;
* Command object (e.g. SurveyActivator)&lt;br /&gt;
* Helper (no state; e.g. QuestionAttributeHelper to bake attributes into different formats used by the views)&lt;br /&gt;
* Wrapper (also no state; e.g IOWrapper, to be able to mock file access)&lt;br /&gt;
&lt;br /&gt;
todo, immutability, possible with private properties that are set in the constructor,&lt;br /&gt;
&lt;br /&gt;
todo: command-query separation for classes?&lt;br /&gt;
&lt;br /&gt;
=== Types of classes in LimeSurvey ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord, used to communicate with the database, and should not do &#039;&#039;anything&#039;&#039; else&lt;br /&gt;
* Controllers, used for glue code; should not contain business-logic&lt;br /&gt;
* Commands, same as controllers but from CLI&lt;br /&gt;
* Helper functions, should be short and obey command-query separation (TODO: Long functions should be separated into command objects or service classes); todo: should not have any dependencies?&lt;br /&gt;
* Helper classes, similar to helper functions but have dependencies that need to be mocked during tests; should not have any state&lt;br /&gt;
* Service classes, contains logic related to a clear and separate task, like create or import survey&lt;br /&gt;
* Question render classes, contains logic related to how questions are rendered (TODO: Need better separation between state and logic and more clear design); similar to widgets&lt;br /&gt;
* Plugin classes, using the plugin event system&lt;br /&gt;
* Widgets, containing HTML logic for custom HTML widgets&lt;br /&gt;
* Customizations to the Yii framework by inheritance&lt;br /&gt;
* Unit and functional test classes&lt;br /&gt;
&lt;br /&gt;
A couple of classes contain only data, like LsDefaultDataSet.&lt;br /&gt;
&lt;br /&gt;
todo, no difference between state property and dependency property in OOP&lt;br /&gt;
&lt;br /&gt;
=== What is &amp;quot;glue code&amp;quot;? ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Putting the pieces together, e.g. service class A need data from model B, fetch it and run.&lt;br /&gt;
&lt;br /&gt;
todo, add example&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Where do I cut?&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Separation of concerns, but which concerns and which type of separation?&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
A class should have a single and clear purpose; compare with [https://en.wikipedia.org/wiki/SOLID SOLID].&lt;br /&gt;
&lt;br /&gt;
=== Class name ===&lt;br /&gt;
&lt;br /&gt;
* ActiveRecord classes are named after the database table they are bound to, but with singular instead of plural. E.g. table &amp;quot;users&amp;quot; have model &amp;quot;User&amp;quot;.&lt;br /&gt;
* Services classes are named after what they &#039;&#039;do&#039;&#039;, e.g. SurveyCreator&lt;br /&gt;
* Helper classes should probably be named as specific as possible when possible, to avoid them from growing too big&lt;br /&gt;
* Follow the naming tradition of the framework with regard to controllers, e.g. UserController or UserAdministrationController.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be &#039;&#039;&#039;verb [+ adjective] + noun&#039;&#039;&#039;, like &amp;quot;createFieldmap&amp;quot;, &amp;quot;quoteText&amp;quot;, &amp;quot;getRelativePath&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ((empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute}))&lt;br /&gt;
    || (&lt;br /&gt;
        !empty($instance-&amp;gt;oOptions-&amp;gt;{$attribute})&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
            $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;inherit&#039;&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $instance-&amp;gt;oOptions-&amp;gt;{$attribute} == &#039;-1&#039;&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
) {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be shortened by factoring out method &amp;lt;code&amp;gt;isInherit&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (empty($instance-&amp;gt;options-&amp;gt;{$attribute})&lt;br /&gt;
    || $this-&amp;gt;isInherit($instance-options-&amp;gt;{$attribute})) {&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Returns true if $value is set to inherit&lt;br /&gt;
 * @param mixed|null $value&lt;br /&gt;
 * @return boolean&lt;br /&gt;
 */&lt;br /&gt;
public function isInherit($value) {&lt;br /&gt;
    return !empty($value)&lt;br /&gt;
        &amp;amp;&amp;amp; (&lt;br /&gt;
               $value === &#039;inherit&#039;&lt;br /&gt;
            || $value === &#039;I&#039;&lt;br /&gt;
            // NB: Do NOT use === here, it won&#039;t work with Postgresql.&lt;br /&gt;
            || $value == &#039;-1&#039;&lt;br /&gt;
        );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And one more time by using a variable for the value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$value = $instance-&amp;gt;options-&amp;gt;{$attribute};&lt;br /&gt;
if (empty($value) || $this-&amp;gt;isInherit($value)) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More clear, right? &amp;quot;If it&#039;s empty or set to inherit, do this and that.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Follow PSR-12 - no underscore, use camelCaseLikeThis&lt;br /&gt;
* Variables of ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
* Never include &amp;quot;array&amp;quot; or &amp;quot;list&amp;quot; in variable names - using plural is enough&lt;br /&gt;
* The &#039;&#039;context&#039;&#039; of the function helps with variable naming, e..g &amp;quot;$name&amp;quot; might be fine in a function about questions; this is extra helpful when functions are short!&lt;br /&gt;
* When a function is short, it&#039;s much easier to figure out variable meaning based on context&lt;br /&gt;
* $qid, $gid, and $sid are so established in the code-base that they could be considered OK&lt;br /&gt;
* The -data suffix means &amp;quot;blob of stuff&amp;quot;, only use it when needed, like with $viewData&lt;br /&gt;
* Use $result when baking a return value; add its exact type as @var annotation (worst possible name, acceptable but anything with more meaning is better)&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Do not use Hungarian notation (prefix with &amp;quot;type&amp;quot;), but instead &amp;lt;code&amp;gt;/** @var */&amp;lt;/code&amp;gt; annotations - these can be checked automatically and will then be enforced to be correct, unlike Hungarian notation which can &amp;quot;rot&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** string[] An array of strings with 0-n elements */&lt;br /&gt;
$allFields = [];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psalm can infer types if the function documentation is enough. The strictness can be adjusted. Not all variables will need @var annotation, the tool will warn you when it&#039;s confused.&lt;br /&gt;
&lt;br /&gt;
* Data that is sent to views as associative arrays will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array Can be basically anything :( Better would be data-transfer objects or more precise notation */&lt;br /&gt;
$data = [];&lt;br /&gt;
// Maybe more clear as $viewData? Discuss.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can be more precise when you specify which type of array you&#039;re writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var array&amp;lt;string, string&amp;gt; This means the array key is string, and the array value is also string */&lt;br /&gt;
$data = [];&lt;br /&gt;
$data[&#039;name&#039;] = &#039;Olle&#039;;&lt;br /&gt;
$data[&#039;occupation&#039;] = &#039;Developer&#039;;&lt;br /&gt;
$data[&#039;age&#039;] = 38;  // ERROR! Value must be string.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more info about array type notation, see the [https://psalm.dev/docs/annotating_code/type_syntax/array_types/ Psalm documentation].&lt;br /&gt;
&lt;br /&gt;
Some smaller examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$theme = App()-&amp;gt;request-&amp;gt;getPost(&#039;theme&#039;);  // Should be $themeType - $theme looks like a model/AR&lt;br /&gt;
$gridid === &#039;questionthemes-grid&#039;;          // Should be $gridId with capital &amp;quot;I&amp;quot;, since it&#039;s two words in English&lt;br /&gt;
$delete = Yii::app()-&amp;gt;request-&amp;gt;getParam(&#039;delete&#039;);  // Tuff one - $delete is ok? Thoughts?&lt;br /&gt;
$files = json_decode(stripslashes($sJSON), true);  // $file is a resource, $files an array of resources, this is $fileData perhaps?&lt;br /&gt;
$valid_extensions_array = explode(&amp;quot;,&amp;quot;, $aAttributes[&#039;allowed_filetypes&#039;]); // Should be $validExtensionTypes&lt;br /&gt;
$language = Yii::app()-&amp;gt;session[&#039;survey_&#039; . $surveyid][&#039;s_lang&#039;]; // TODO: Should this be a value class? Then Language::make(...);&lt;br /&gt;
$surveyInfo = getSurveyInfo($surveyId, $language);  // OK, BUT should be a separate DTO!&lt;br /&gt;
$fieldMap = createFieldMap($survey, &#039;short&#039;, false, false, $language);  // Same, make a DTO&lt;br /&gt;
$cquestions = array();  // What&#039;s &amp;quot;c&amp;quot;? Method name is getChildQuestions and $questions is an argument - better with $childQuestions?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
* The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the variable naming from plural to singular.&lt;br /&gt;
&lt;br /&gt;
* Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo, UML, scenarios, use-cases&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
classes, functions, components, tests&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Workarounds:_Question_design,_layout_and_theme-ing_-_LimeSurvey_3.0%2B&amp;diff=162138</id>
		<title>Workarounds: Question design, layout and theme-ing - LimeSurvey 3.0+</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Workarounds:_Question_design,_layout_and_theme-ing_-_LimeSurvey_3.0%2B&amp;diff=162138"/>
		<updated>2021-04-19T13:39:08Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This section more or less deals with styling your survey, adjusting the layout and setting up special question types. You can find &#039;&#039;&#039;similar information&#039;&#039;&#039; at these pages:&lt;br /&gt;
&lt;br /&gt;
* [[Theme editor|The theme editor - LimeSurvey 3.0+]]&lt;br /&gt;
* [[Workarounds: Manipulating a survey at runtime using Javascript - LimeSurvey 3.0+]]&lt;br /&gt;
* [[Workarounds: Survey behaviour - LimeSurvey 3.0+]]&lt;br /&gt;
* [[Workarounds: Further solutions provided by LimeSurvey users - LimeSurvey 3.0+]]&lt;br /&gt;
* [[Question themes]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;simplebox&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&amp;lt;br /&amp;gt;&#039;&#039;&#039;Please keep in mind that these workarounds are not official LimeSurvey extensions - they are solutions that users have created for themselves. &amp;lt;span style=&#039;color:#EE0000&#039;&amp;gt;Therefore LimeSurvey can&#039;t offer guarantees or support for these solutions.&amp;lt;/span&amp;gt;&amp;lt;br /&amp;gt;If you have questions, please contact the users that, thankfully, shared their solutions with the community.&#039;&#039;&#039;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Note|&#039;&#039;&#039;If you would like to receive survey design support, please contact one of the authorized LimeSurvey partners listed here:&#039;&#039;&#039; https://limesurvey.com .}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Alternate background colour of questions=&lt;br /&gt;
&lt;br /&gt;
To alternate the background colour of questions, open the questions one by one and use CSS to edit each question&#039;s background. For example, if you wish to change the background colour of a question thas has the ID 6, use the below code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
.question6 {&lt;br /&gt;
background-color: #E0EBF8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check the ID of a question, create and save it into a question group, and then access again the question editor. You will see above the question code the ID of the question:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:ID_location_question.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Card sorting question=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A custom question theme for Slider Control Buttons for LimeSurvey 3.x can be found here - https://github.com/tpartner/LimeSurvey-Card-Sorting-3.x&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Create MaxDiff question type=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tested with: LimeSurvey 3.17.3&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This workaround uses JavaScript to convert an &#039;&#039;&#039;&#039;&#039;Array (flexible labels) by column&#039;&#039;&#039;&#039;&#039; question into a [http://en.wikipedia.org/wiki/MaxDiff MaxDiff] question type.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:MaxDiff_question.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
* [[Media:MaxDiff_question.zip]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPLEMENTATION&#039;&#039;&#039;&lt;br /&gt;
#[[Workarounds: Manipulating a survey at runtime using Javascript#How to use Script (eg. JavaScript etc.) in LimeSurvey? - LimeSurvey 3.0+|Set up your survey to use JavaScript]].&lt;br /&gt;
#Create an [[Question_type_-_Array_by_column|Array (flexible labels) by column question]] with two subquestions (representing the left and right columns, respectively).&lt;br /&gt;
#Add answers to the array - (representing the rows).&lt;br /&gt;
#Create a copy the default theme (extend the default theme).&lt;br /&gt;
#Add the following to the end of &#039;&#039;&#039;&#039;&#039;custom.js&#039;&#039;&#039;&#039;&#039; in your new template:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;function maxDiff(qID, randomize) {&lt;br /&gt;
 &lt;br /&gt;
	// Identify some elements&lt;br /&gt;
	var thisQuestion = $(&#039;#question&#039;+qID);&lt;br /&gt;
	var thisTable = $(&#039;table.subquestion-list:eq(0)&#039;, thisQuestion);&lt;br /&gt;
 &lt;br /&gt;
	// Assign a new question class&lt;br /&gt;
	$(thisQuestion).addClass(&#039;max-diff-array&#039;);&lt;br /&gt;
 &lt;br /&gt;
	// Move the columns&lt;br /&gt;
	$(&#039;thead tr:eq(0)&#039;, thisTable).prepend($(&#039;thead tr:eq(0) th:eq(1)&#039;, thisTable));&lt;br /&gt;
	$(&#039;tr.answers-list&#039;, thisTable).each(function(i){&lt;br /&gt;
		$(&#039;td.answer-item:eq(0)&#039;, this).prependTo(this);&lt;br /&gt;
	});&lt;br /&gt;
 &lt;br /&gt;
	// Random rows&lt;br /&gt;
	if(randomize) {&lt;br /&gt;
		var rowsArr = [];&lt;br /&gt;
		$(&#039;tr.answers-list&#039;, thisTable).each(function(i){&lt;br /&gt;
			$(this).attr(&#039;data-index&#039;, i);&lt;br /&gt;
			rowsArr.push(i);&lt;br /&gt;
		});&lt;br /&gt;
		shuffleArray(rowsArr);&lt;br /&gt;
		$(rowsArr).each(function(i){&lt;br /&gt;
			$(&#039;tbody&#039;, thisTable).append($(&#039;tr[data-index=&amp;quot;&#039;+this+&#039;&amp;quot;]&#039;, thisTable));&lt;br /&gt;
		});&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	// Prevent clicking twice in the same row&lt;br /&gt;
	$(&#039;input:radio&#039;, thisQuestion).on(&#039;click&#039;, function () {&lt;br /&gt;
 &lt;br /&gt;
		$(&#039;input:radio&#039;, thisQuestion).prop(&#039;disabled&#039;, false);&lt;br /&gt;
		$(&#039;input:radio:checked&#039;, thisQuestion).each(function(i) {&lt;br /&gt;
			var thisRow = $(this).closest(&#039;tr.answers-list&#039;);&lt;br /&gt;
			$(&#039;input:radio&#039;, thisRow).not(this).prop(&#039;disabled&#039;, true);&lt;br /&gt;
		});&lt;br /&gt;
	});	&lt;br /&gt;
 &lt;br /&gt;
	// Fix up the row classes&lt;br /&gt;
	var rowClass = 1;&lt;br /&gt;
	$(&#039;tr.answers-list&#039;, thisTable).each(function(i) {&lt;br /&gt;
		$(this).addClass(&#039;array&#039;+(2-(i%2)));&lt;br /&gt;
	});&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
function shuffleArray(array) {&lt;br /&gt;
	for (var i = array.length - 1; i &amp;gt; 0; i--) {&lt;br /&gt;
		var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
		var temp = array[i];&lt;br /&gt;
		array[i] = array[j];&lt;br /&gt;
		array[j] = temp;&lt;br /&gt;
	}&lt;br /&gt;
	return array;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#Add the following to the end of &#039;&#039;&#039;&#039;&#039;custom.css&#039;&#039;&#039;&#039;&#039; in your new template:&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;.max-diff-array th.answertext { &lt;br /&gt;
	text-align: center;&lt;br /&gt;
	border-right: 3px solid #FFFFFF;&lt;br /&gt;
	border-left: 3px solid #FFFFFF;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
/* Override the responsive &amp;quot;no-more-tables&amp;quot; stuff */&lt;br /&gt;
@media only screen and (max-width: 801px) {&lt;br /&gt;
 &lt;br /&gt;
	.max-diff-array .no-more-tables table {&lt;br /&gt;
		display: table;&lt;br /&gt;
		border-collapse: inherit;&lt;br /&gt;
	}	&lt;br /&gt;
 &lt;br /&gt;
	.max-diff-array .no-more-tables thead, &lt;br /&gt;
	.max-diff-array .no-more-tables tbody {&lt;br /&gt;
		display: table-row-group;&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	.max-diff-array .no-more-tables tr {&lt;br /&gt;
		left: auto;&lt;br /&gt;
		position: relative;&lt;br /&gt;
		top: auto;&lt;br /&gt;
		display: table-row;&lt;br /&gt;
		border: 0 none;&lt;br /&gt;
	}	&lt;br /&gt;
 &lt;br /&gt;
	.max-diff-array .no-more-tables th, &lt;br /&gt;
	.max-diff-array .no-more-tables td {&lt;br /&gt;
		display: table-cell;&lt;br /&gt;
		text-align: center !important;&lt;br /&gt;
		color: #2c3e50;&lt;br /&gt;
		font-size: 15px;&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	.max-diff-array th.answertext { &lt;br /&gt;
		border-right: 3px solid #FFFFFF;&lt;br /&gt;
		border-left: 3px solid #FFFFFF;&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	.max-diff-array .answer-item label span {&lt;br /&gt;
		display: none !important;&lt;br /&gt;
	}	&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#Add a script like this to the question source of the Array (flexible labels) question:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot; charset=&amp;quot;utf-8&amp;quot;&amp;gt;	   &lt;br /&gt;
	$(document).ready(function(){&lt;br /&gt;
		// Call the maxDiff() function&lt;br /&gt;
		// Set the second parameter to true for randomized rows&lt;br /&gt;
		maxDiff({QID}, true);&lt;br /&gt;
	});&lt;br /&gt;
&amp;lt;/script&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Likert Scale=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To create a likert scale, you have to choose either Array or Array (5 point choice) as question type to part the answer by using a pipe char &amp;quot;|&amp;quot; - which answer should be on the left side and which one on the right side of the array. For example, If you want to have &#039;Good&#039; on the left side of the row and &#039;Evil&#039; on the right side of the row, just write &#039;Good|Evil&#039; into the answer field: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Likert_array_subquestions.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:H_SD.jpg]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039; [[Media:Question type - Array - Semantic Differential Question Type.zip]]&lt;br /&gt;
&lt;br /&gt;
=Dropdown responses for Array=&lt;br /&gt;
&lt;br /&gt;
Link tpartner forum&lt;br /&gt;
&lt;br /&gt;
Download example link&lt;br /&gt;
&lt;br /&gt;
Screenshot&lt;br /&gt;
&lt;br /&gt;
=Embedding audio in questions=&lt;br /&gt;
&lt;br /&gt;
To embed the audio clips, you have to use a code similar to the one listed below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=html&amp;gt;&lt;br /&gt;
&amp;lt;audio controls=&amp;quot;controls&amp;quot;&amp;gt;&amp;lt;source src=&amp;quot; /upload/surveys/336775/images/MP3_file_name.mp3&lt;br /&gt;
&amp;quot; type=&amp;quot;audio/mp3&amp;quot; /&amp;gt; Your browser does not support this audio&amp;lt;/audio&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once embedded, the question would look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Embed_audio_example.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To introduce audio clips as subquestions and/or answers, click on the green pencil to open the HTML editor and attach the corresponding code (while being in the source mode).&lt;br /&gt;
&lt;br /&gt;
{{Alert|title=Attention|text=You may need to turn off the XSS filter - see the documentation for [[Global settings#Security|global settings]].}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Evaluative Space Grid (ESG)=&lt;br /&gt;
&lt;br /&gt;
Check the old wiki section -&amp;gt; working&lt;br /&gt;
&lt;br /&gt;
=Hiding inputs of a &amp;quot;Multiple options with comments&amp;quot; question=&lt;br /&gt;
&lt;br /&gt;
See example + forum post&lt;br /&gt;
&lt;br /&gt;
=How to hide the asterisk= &amp;lt;!--T:95--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:96--&amp;gt;&lt;br /&gt;
The instructions are similar to the ones presented above for LimeSurvey 2.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:97--&amp;gt;&lt;br /&gt;
* Access the desired theme. If it is a default theme, create a copy of it by clicking on the &amp;quot;extend&amp;quot; button.&lt;br /&gt;
* Access the list of themes and click on the &#039;&#039;&#039;theme editor&#039;&#039;&#039; that corresponds to the newly created theme.&lt;br /&gt;
* On the left side, you can visualise the corresponding CSS files of your theme.&lt;br /&gt;
* Click on custom.css and add the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:98--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;.asterisk {display: none;}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:99--&amp;gt;&lt;br /&gt;
* Save the changes. The red asterisk that is usually displayed at the beginning of the question text is hidden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=How to hide the language switcher from the welcome page= &amp;lt;!--T:44--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:45--&amp;gt;&lt;br /&gt;
In the case in which you decide to launch a multilingual survey, you will observe on the welcome page a drop-down menu that permits your respondents to select the language in which they wish to fill in your survey:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:46--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Language switcher show languages.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:47--&amp;gt;&lt;br /&gt;
However, in certain scenarios you wish to hide the respective option. There are many ways in which you can hide the language switcher. The &#039;&#039;&#039;&amp;quot;twig&amp;quot;&#039;&#039;&#039; way is displayed below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:48--&amp;gt;&lt;br /&gt;
Access [[Themes]] from the global [[Administering LimeSurvey|Configuration menu]]:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:49--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Language switcher themes.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:50--&amp;gt;&lt;br /&gt;
Click on the &#039;&#039;&#039;Theme editor&#039;&#039;&#039; button to edit the desired theme:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:51--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Language switcher theme editor.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:52--&amp;gt;&lt;br /&gt;
{{Note|LimeSurvey does not allow you to edit the standard themes! If you wish to add changes to a standard theme, click on the &#039;&#039;&#039;Extend&#039;&#039;&#039; button located on the top bar to create an editable copy of it.}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:53--&amp;gt;&lt;br /&gt;
Look on the left side of the screen for the &#039;&#039;&#039;language_changer.twig&#039;&#039;&#039; file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:54--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Location language switcher twig.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:55--&amp;gt;&lt;br /&gt;
Go to the line that contains the following function: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:56--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
{% if aSurveyInfo.alanguageChanger.show == true %}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:57--&amp;gt;&lt;br /&gt;
Comment it out (replace &amp;quot;%&amp;quot; with &amp;quot;#&amp;quot;) and copy the following line below it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:58--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
{% if false == true %}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:59--&amp;gt;&lt;br /&gt;
The line should look like this in the end:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:60--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Language switcher code replacement.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:61--&amp;gt;&lt;br /&gt;
Click on &amp;quot;Copy to local theme and save changes&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:62--&amp;gt;&lt;br /&gt;
Access your survey and select the edited theme from the [[General settings|general settings]] panel. Now, the welcome page should look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:63--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Hide language switcher twig.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=How to hide the language switcher located on the top menu= &amp;lt;!--T:64--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:65--&amp;gt;&lt;br /&gt;
To hide the top language switcher, we will follow pretty much the same steps as described above. We just have to access the right twig file to edit it. Access again the theme you wish to change, and then go to &#039;&#039;&#039;Screen&#039;&#039;&#039; which is located in the upper-right part of the window and select &#039;&#039;&#039;Navigation&#039;&#039;&#039; from the dropdown list. Now, look on the left side of the window for &amp;quot;language_changer_top_menu.twig&amp;quot;. Click on it and comment out:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:66--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
{% if aSurveyInfo.alanguageChanger.show == true %}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:67--&amp;gt;&lt;br /&gt;
Add the following line below it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:68--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
{% if false == true %}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:69--&amp;gt;&lt;br /&gt;
No language switcher will be displayed on the right side of the screen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:70--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:Language switcher hide top menu.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Image interest points and comments=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A custom question theme for Slider Control Buttons for LimeSurvey 3.x can be found here - https://github.com/tpartner/LimeSurvey-Image-Interest-Points-3.x&lt;br /&gt;
&lt;br /&gt;
=Public registration - mandatory fields= &amp;lt;!--T:102--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:103--&amp;gt;&lt;br /&gt;
If you use a [[Survey participants|survey participants table]] and you also allow [[Participant_settings#Allow_public_registration|public registration]], then users will be prompted by the following message:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:104--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:non-mandatory public reg.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:105--&amp;gt;&lt;br /&gt;
As it can be observed above, only the email field is mandatory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:106--&amp;gt;&lt;br /&gt;
To have all three fields marked as being mandatory, please edit your survey theme accordingly. If you use &#039;&#039;&#039;Fruity&#039;&#039;&#039;, you have to go to the &#039;&#039;&#039;registration&#039;&#039;&#039; screen and see how the participants email field looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:107--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;twig&amp;quot;&amp;gt;&lt;br /&gt;
        {# Participants email #}&lt;br /&gt;
        &amp;lt;div class=&#039;{{ aSurveyInfo.class.registerformcolrowc }} form-group row&#039; {{ aSurveyInfo.attr.registerformcolrowc }}&amp;gt;&lt;br /&gt;
            &amp;lt;label {{ aSurveyInfo.attr.registerformcolrowclabel }} class=&#039;{{ aSurveyInfo.class.registerformcolrowclabel }}  control-label&#039;&amp;gt; {{ gT(&amp;quot;Email address:&amp;quot;) }} {{ include(&#039;./subviews/registration/required.twig&#039;) }}&amp;lt;/label&amp;gt;&lt;br /&gt;
            &amp;lt;div {{ aSurveyInfo.attr.registerformcolrowcdiv }}  &amp;gt;&lt;br /&gt;
                {{ C.Html.textField(&#039;register_email&#039;, aSurveyInfo.sEmail, ({&#039;id&#039; : &#039;register_email&#039;,&#039;class&#039; : &#039;form-control input-sm&#039;,&#039;required&#039; : true})) }}&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:108--&amp;gt;&lt;br /&gt;
After that, make sure to edit the first name and last name fields correspondingly by adding the label: &amp;lt;syntaxhighlight lang=&amp;quot;twig&amp;quot;&amp;gt;{{ include(&#039;./subviews/registration/required.twig&#039;) }}&amp;lt;/syntaxhighlight&amp;gt; and this line which makes the field mandatory to be filled out: &amp;lt;syntaxhighlight lang=&amp;quot;twig&amp;quot;&amp;gt;&#039;required&#039; : true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:109--&amp;gt;&lt;br /&gt;
The edited file should look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:110--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;twig&amp;quot;&amp;gt;&lt;br /&gt;
        {# Participants first name #}&lt;br /&gt;
        &amp;lt;div class=&#039;{{ aSurveyInfo.class.registerformcolrow }} form-group row&#039; {{ aSurveyInfo.attr.registerformcolrow }}&amp;gt;&lt;br /&gt;
            &amp;lt;label for=&#039;register_firstname&#039; class=&#039;{{ aSurveyInfo.class.registerformcolrowlabel }} control-label &#039;&amp;gt;{{ gT(&amp;quot;First name:&amp;quot;) }} {{ include(&#039;./subviews/registration/required.twig&#039;) }}&amp;lt;/label&amp;gt; {# extra label #}&lt;br /&gt;
            &amp;lt;div class=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt;
                {{ C.Html.textField(&#039;register_firstname&#039;, aSurveyInfo.sFirstName, ({&#039;id&#039; : &#039;register_firstname&#039;,&#039;class&#039; : &#039;form-control&#039;, &#039;required&#039; : true})) }} {# mandatory field #}&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;!--T:111--&amp;gt;&lt;br /&gt;
{# Participants last name #}&lt;br /&gt;
        &amp;lt;div class=&#039;{{ aSurveyInfo.class.registerformcolrowb }} form-group row&#039; {{ aSurveyInfo.attr.registerformcolrowb }}&amp;gt;&lt;br /&gt;
            &amp;lt;label {{ aSurveyInfo.attr.registerformcolrowblabel }}  class=&#039;{{ aSurveyInfo.class.registerformcolrowblabel }} control-label &#039;&amp;gt;{{ gT(&amp;quot;Last name:&amp;quot;) }} {{ include(&#039;./subviews/registration/required.twig&#039;) }}&amp;lt;/label&amp;gt; {# extra label #}&lt;br /&gt;
            &amp;lt;div {{ aSurveyInfo.attr.registerformcolrowbdiv }} &amp;gt;&lt;br /&gt;
                {{ C.Html.textField(&#039;register_lastname&#039;, aSurveyInfo.sLastName, ({&#039;id&#039; : &#039;register_lastname&#039;, &#039;class&#039; : &#039;form-control&#039;, &#039;required&#039; : true})) }} {# mandatory field #}&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:112--&amp;gt;&lt;br /&gt;
Now, the public registration page should look like this (all the fields being mandatory):&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:113--&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[File:mandatory public reg.png]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Reverse slider=&lt;br /&gt;
&lt;br /&gt;
{{Box|The reverse slider feature can be used in any (multiple) numerical input question type. For more details, please check the [[Question_type_-_Multiple_numerical_input#Slider|slider wiki subsection]].}}&lt;br /&gt;
&lt;br /&gt;
=Slider with control buttons=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A custom question theme for Slider Control Buttons for LimeSurvey 3.x can be found here - https://github.com/tpartner/LimeSurvey-Slider-Controls-3x&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=QS:Question_theme&amp;diff=162137</id>
		<title>QS:Question theme</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=QS:Question_theme&amp;diff=162137"/>
		<updated>2021-04-19T13:34:17Z</updated>

		<summary type="html">&lt;p&gt;Olle: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;translate&amp;gt;&lt;br /&gt;
&amp;lt;!--T:21--&amp;gt;&lt;br /&gt;
&amp;lt;languages /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;onlyinclude&amp;gt;&lt;br /&gt;
===Question theme (question_theme)=== &amp;lt;!--T:16--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:17--&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Description&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:22--&amp;gt;&lt;br /&gt;
It allows you to use customized themes for the respective question.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:19--&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Available options&#039;&#039;&#039;&lt;br /&gt;
*Your created question themes which are located under the &#039;&#039;&#039;Question themes&#039;&#039;&#039; in the [[Themes]] panel.&lt;br /&gt;
&lt;br /&gt;
See: https://manual.limesurvey.org/Question_themes&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:23--&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;simplebox&amp;quot;&amp;gt;[[File:help.png]] &#039;&#039;&#039;Note:&#039;&#039;&#039; This feature is under development at the moment.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/onlyinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--T:20--&amp;gt;&lt;br /&gt;
[[Category:Question Settings]][[Category:Question Display Options]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/translate&amp;gt;&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=161888</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=161888"/>
		<updated>2021-04-14T11:20:24Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Static analysis */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured?&lt;br /&gt;
&lt;br /&gt;
Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system.&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders&lt;br /&gt;
* Fetchers&lt;br /&gt;
* Data-transfer object&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Injection, dependencies&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be verb + noun, like &amp;quot;createFieldmap&amp;quot; or &amp;quot;quoteText&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Variables or ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== OOP design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Object reification.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
OOP ceremony, like getters and setters?&lt;br /&gt;
&lt;br /&gt;
Useful design patterns:&lt;br /&gt;
&lt;br /&gt;
* Command object pattern&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Database design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== CSS ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== HTML ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=161887</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=161887"/>
		<updated>2021-04-14T10:14:19Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Assertions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured?&lt;br /&gt;
&lt;br /&gt;
Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system.&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders&lt;br /&gt;
* Fetchers&lt;br /&gt;
* Data-transfer object&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Injection, dependencies&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be verb + noun, like &amp;quot;createFieldmap&amp;quot; or &amp;quot;quoteText&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Variables or ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== OOP design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Object reification.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
OOP ceremony, like getters and setters?&lt;br /&gt;
&lt;br /&gt;
Useful design patterns:&lt;br /&gt;
&lt;br /&gt;
* Command object pattern&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
	<entry>
		<id>https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=161799</id>
		<title>Code quality guide</title>
		<link rel="alternate" type="text/html" href="https://www.limesurvey.org/manual/index.php?title=Code_quality_guide&amp;diff=161799"/>
		<updated>2021-04-12T15:34:57Z</updated>

		<summary type="html">&lt;p&gt;Olle: /* Performance */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DRAFT&lt;br /&gt;
&lt;br /&gt;
== Prologue ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Be risk aware&#039;&#039;&#039;. Too good code can be a business risk (slow to write, maybe over-designed). Too sloppy code can also be a business risk (hard to maintain and understand). You have to find a balance that is adapted to the current situation and risk analysis, in which code quality becomes a &#039;&#039;&#039;risk mitigation&#039;&#039;&#039; technique.&lt;br /&gt;
# &#039;&#039;&#039;Be humble&#039;&#039;&#039;. LimeSurvey was made by developers from all over the world, with different age, education and experience. Your code might be read by a completely different team ten years from now, in the same way you are now reading code by developers that no longer work with us, but which work pays our rent.&lt;br /&gt;
# &#039;&#039;&#039;Performance matters sometimes&#039;&#039;&#039;, and shouldn&#039;t be disregarded completely. In particular, database queries using the ORM and ActiveRecord can be problematic. Some surveys have thousands of questions and hundreds of thousands of responses. Fast response time is also important for a fluid user experience.&lt;br /&gt;
# &#039;&#039;&#039;It&#039;s harder to read code than to write code&#039;&#039;&#039;. Don&#039;t choose patterns that are easy or fast to write, but that are easy to read.&lt;br /&gt;
# Make your code &#039;&#039;&#039;communicate intent&#039;&#039;&#039;.&lt;br /&gt;
# It&#039;s easy to forget &#039;&#039;&#039;cross-cutting concerns&#039;&#039;&#039; like translation and security. Keep a mental note.&lt;br /&gt;
# &#039;&#039;&#039;Stress affects code quality&#039;&#039;&#039; and your risk behaviour. If your stress level is too high to write code with proper quality, take a step back and discuss it with your boss, or you&#039;ll push problems to the future.&lt;br /&gt;
&lt;br /&gt;
== Quality ==&lt;br /&gt;
&lt;br /&gt;
The purpose of this guide is to increase the quality of the LimeSurvey code-base. What is quality? Which aspects of quality can be measured?&lt;br /&gt;
&lt;br /&gt;
Which kind of company culture gives rise to appropriate code quality?&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually easier to get along what is &amp;quot;bad&amp;quot; code than what&#039;s &amp;quot;best&amp;quot; code. Blacklist instead of whitelist?&lt;br /&gt;
&lt;br /&gt;
Quality attributes related to code quality:&lt;br /&gt;
&lt;br /&gt;
* Readability&lt;br /&gt;
* Testability&lt;br /&gt;
* Maintainability&lt;br /&gt;
* Performance&lt;br /&gt;
* Security&lt;br /&gt;
* Reliability&lt;br /&gt;
&lt;br /&gt;
Can not increase all at the same time - performance vs readability.&lt;br /&gt;
&lt;br /&gt;
It&#039;s better to avoid emotional language when describing code quality, like &amp;quot;clean&amp;quot; or &amp;quot;dirty&amp;quot;. Be precise.&lt;br /&gt;
&lt;br /&gt;
Idiomatic code is more readable than non-idiomatic code. What&#039;s idiomatic depends on which context or domain you work in. We work in PHP and web application development, and have other idioms than in, say, functional or hardware-close programming.&lt;br /&gt;
&lt;br /&gt;
Some code are &amp;quot;hot spots&amp;quot; - changed often. Those parts should require higher quality than other code.&lt;br /&gt;
&lt;br /&gt;
== Risk ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s no inherit module functionality in PHP, but Yii has a module system.&lt;br /&gt;
&lt;br /&gt;
== Classes ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Different categories of classes:&lt;br /&gt;
&lt;br /&gt;
* Builders&lt;br /&gt;
* Fetchers&lt;br /&gt;
* Data-transfer object&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Injection, dependencies&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
Functions are one of the fundamental building blocks of programming, so it&#039;s important to get it right.&lt;br /&gt;
&lt;br /&gt;
Functions that are part of a class are called &amp;quot;methods&amp;quot;, if you want to be formal.&lt;br /&gt;
&lt;br /&gt;
Different types of functions in PHP:&lt;br /&gt;
&lt;br /&gt;
* Functions (not attached to a class)&lt;br /&gt;
* Methods (attached to class)&lt;br /&gt;
* Anonymous functions (does not capture scope automatically)&lt;br /&gt;
* Short functions (does capture scope automatically)&lt;br /&gt;
&lt;br /&gt;
I&#039;m using &amp;quot;function&amp;quot; as a word for all types of functions in PHP below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Size ===&lt;br /&gt;
&lt;br /&gt;
* A function should be short enough to be completely readable on one screen, approx 60 lines at most&lt;br /&gt;
* A function should not have too many arguments; if you have five or more, consider making it a class instead&lt;br /&gt;
* If a function is growing too big, it might be better to create a class instead or split it into several functions (if both functions have &#039;&#039;&#039;common state&#039;&#039;&#039;, a class might be better, unless you want to pass around &#039;&#039;&#039;explicit state&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Contract ===&lt;br /&gt;
&lt;br /&gt;
* A function has a contract with its surrounding environment:&lt;br /&gt;
** Pre-condition: What needs to be true before the function is executed&lt;br /&gt;
** Post-condition: What will be true after the function has returned&lt;br /&gt;
* The function contract can be checked with assertions (internal invariants) or exceptions (outside world); see below&lt;br /&gt;
* Another good way is to use unit-tests to make sure the function&#039;s contract remains stable through changes&lt;br /&gt;
* The first line in the docblock should be one sentence describing the relation between function arguments and its output&amp;lt;br&amp;gt;Example&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates a random password of length $length (defaults to 12)&lt;br /&gt;
 * @param int $length&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function createPassword($lenght = 12)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Creates an HTML language drop down for survey with id $surveyId, marking $selectedLanguage as selected&lt;br /&gt;
 * @param int $surveyId&lt;br /&gt;
 * @param string $selectedLanguage&lt;br /&gt;
 * @return string &amp;lt;select&amp;gt; HTML&lt;br /&gt;
 */&lt;br /&gt;
function createLanguageDropdown($surveyId, $selectedLanguage)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Push side-effects up in the stack trace ===&lt;br /&gt;
&lt;br /&gt;
This one is a bit abstract.&lt;br /&gt;
&lt;br /&gt;
First, what is a &#039;&#039;&#039;side-effect&#039;&#039;&#039;? In short, it&#039;s anything that happens in a function that &#039;&#039;is not&#039;&#039; described by the &#039;&#039;relation&#039;&#039; between function arguments and output.&lt;br /&gt;
&lt;br /&gt;
Another way to phrase it is, that a side-effect is anything that is not always the same for the given input to a function; a function without any side-effects will always return the same result if it gets the same input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// gT accesses the file system and will return different result depending on what&#039;s in the translation files&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null);&lt;br /&gt;
&lt;br /&gt;
// If $sText and $sEscapeMode is always the same, quoteText() will always return the same string.&lt;br /&gt;
function quoteText($sText, $sEscapeMode = &#039;html&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function with no side-effects is called &#039;&#039;&#039;pure&#039;&#039;&#039;. A function that is guaranteed to always return (no exceptions, no errors, no warnings, no division by zero) is called &#039;&#039;&#039;total&#039;&#039;&#039;. Another name for purity is [https://en.wikipedia.org/wiki/Referential_transparency referential transparency].&lt;br /&gt;
&lt;br /&gt;
It&#039;s a good thing if functions in a code-base &#039;&#039;either&#039;&#039; have side-effects, &#039;&#039;or&#039;&#039; return a result. Not both. This is called [https://en.wikipedia.org/wiki/Command%E2%80%93query_separation command-query separation].&lt;br /&gt;
&lt;br /&gt;
Examples of side-effects:&lt;br /&gt;
&lt;br /&gt;
* Database or file access&lt;br /&gt;
* Echo, or write to stdout or stderr&lt;br /&gt;
* Randomization&lt;br /&gt;
&lt;br /&gt;
Why does this matter? Because side-effects make testing harder! &#039;&#039;&#039;If you have side-effects, you cannot create unit tests&#039;&#039;&#039;, &#039;&#039;unless&#039;&#039; those side-effects can be mocked. If not, you need integrity tests with database setup or more. The more side-effects you have in a function, the more mocking is required to unit test it.&lt;br /&gt;
&lt;br /&gt;
So, one solution to this is to move side-effects higher up in the stack trace, that is, move them to the calling code.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(int $surveyid, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since this function takes $surveyid, we know it will probably query the database. Quering the database is a side-effect, and thus it requires a database scaffold for the test (or mocking, but the database connection is not injected, so there&#039;s no way to do it). One simple solution is to lift the side-effect out and pass in a Survey object instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function getSurveyInfo(Survey $survey, string $languagecode = &#039;&#039;, boolean $force = false): array;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Much better! The side-effect of calling the database now happens &#039;&#039;before&#039;&#039; the function call. Now we can either mock a Survey object, or make an imitating stub for our test.&lt;br /&gt;
&lt;br /&gt;
Reading the function body, there is in fact another database access, but since this is a relation to the Survey model, it could easily be moved out too. We&#039;ve just made a function testable! Hooray!&lt;br /&gt;
&lt;br /&gt;
This principle is rephrased in an architectural settings as [https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md functional core, imperative shell]. This architecture helps you get a good ratio between fast unit tests and slower integrity tests.&lt;br /&gt;
&lt;br /&gt;
=== Function name ===&lt;br /&gt;
&lt;br /&gt;
* The function name should tell you &#039;&#039;&#039;what&#039;&#039;&#039; it does; docs should tell you &#039;&#039;&#039;why&#039;&#039;&#039; (if this information is not obvious). &#039;&#039;&#039;How&#039;&#039;&#039; is described by the code itself (hopefully, but can in some cases be relevant to document).&lt;br /&gt;
* Function name can often be verb + noun, like &amp;quot;createFieldmap&amp;quot; or &amp;quot;quoteText&amp;quot;&lt;br /&gt;
* Prefix with &amp;quot;is&amp;quot; for boolean functions&lt;br /&gt;
&lt;br /&gt;
todo: difference between create, get, fetch, generate, ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* gT() - An exception to descriptive function names; since this is used everywhere, the minimal name is motivated; maybe even name it &amp;quot;t()&amp;quot;?&lt;br /&gt;
* createPassword() - One verb, one noun - clear&lt;br /&gt;
* isCaptchaEnabled() - Using &amp;quot;is&amp;quot; signifies it returns boolean; clear purpose&lt;br /&gt;
* getFullResponseTable() - Verb, adjective, noun - but is &amp;quot;full&amp;quot; needed here? Is there a non-full version of the same function?&lt;br /&gt;
* replaceExpressionCodes() - Pretty good, but it&#039;s obvious that more information is needed in the docblock to describe what &amp;quot;replace&amp;quot; means, and what an &amp;quot;expression code&amp;quot; is&lt;br /&gt;
* fixCKeditorText() - &amp;quot;fix&amp;quot; is a bit vague, but maybe best there is? Pray that docblock contains information!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* languageDropdown() - No verb, doesn&#039;t tell you what it does&lt;br /&gt;
* rmdirr() - Two r&#039;s, looks like a typo, but the last &amp;quot;r&amp;quot; actually means &amp;quot;recursive&amp;quot;!&lt;br /&gt;
* CSVUnquote() - Is CSV really the namespace? Why is the verb the second word?&lt;br /&gt;
* incompleteAnsFilterState() - One verb, but in the middle, or is &amp;quot;filter state&amp;quot; a noun?; why abbreviate &amp;quot;answer&amp;quot; to &amp;quot;ans&amp;quot;?&lt;br /&gt;
* reverseTranslateFieldNames() - Two verbs - did author mean &amp;quot;reverseTranslatedFieldNames&amp;quot;? Or &amp;quot;reverse and translate&amp;quot;? If so, it should be two functions, one for reverse and one for translate&lt;br /&gt;
* checkUploadedFileSizeAndRenderJson() - If you have an &amp;quot;and&amp;quot; in the function name, you should consider splitting it into two functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assertions ===&lt;br /&gt;
&lt;br /&gt;
NB: This is not assertions as used in unit-tests, but assertions as used to check invariants in production code.&lt;br /&gt;
&lt;br /&gt;
Assertions can be used for mental checks. They are only used for internal logic - failed assertions are not supposed to be seen by the end user. For interaction with the outside world, like database or browser, exceptions should be used instead.&lt;br /&gt;
&lt;br /&gt;
Assertions can be disabled in production mode and have then no runtime cost.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Translation helper function&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $sToTranslate&lt;br /&gt;
 * @param string $sEscapeMode Valid values are html (this is the default, js and unescaped)&lt;br /&gt;
 * @param string $sLanguage&lt;br /&gt;
 * @return string&lt;br /&gt;
 */&lt;br /&gt;
function gT($sToTranslate, $sEscapeMode = &#039;html&#039;, $sLanguage = null)&lt;br /&gt;
{&lt;br /&gt;
    assert(is_string($sToTranslate));&lt;br /&gt;
    assert(strlen($sToTranslate) &amp;gt; 0);&lt;br /&gt;
    assert($sEscapeMode === &#039;html&#039; || $sEscapeMode === &#039;js&#039; || $sEscapeMode === &#039;unescaped&#039;);&lt;br /&gt;
    assert(strlen($sLanguage) === 2 || $sLanguage === null);&lt;br /&gt;
    return quoteText(Yii::t(&#039;&#039;, $sToTranslate, array(), null, $sLanguage), $sEscapeMode);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
todo: exception hierarchy&lt;br /&gt;
&lt;br /&gt;
todo: what can be repaired after exception? transaction rollback&lt;br /&gt;
&lt;br /&gt;
* Exceptions are used to check inputs from the outside world (browser, database, file system)&lt;br /&gt;
* Don&#039;t use exceptions for control flow, only when fatal errors happen that make further execution impossible&lt;br /&gt;
* Exceptions should have clear error messages that make them &#039;&#039;actionable&#039;&#039; - there should be a clear &amp;quot;next step&amp;quot; for the user or developer&lt;br /&gt;
* Never, ever, do an empty catch-block&lt;br /&gt;
&lt;br /&gt;
==== Alternatives to exceptions ====&lt;br /&gt;
&lt;br /&gt;
Some people consider exceptions being a hidden form of &amp;quot;goto&amp;quot; which confuses the control flow of the program. They propose different other solutions:&lt;br /&gt;
&lt;br /&gt;
* Return a tuple, like &amp;lt;code&amp;gt;[$result, $errorMessage]&amp;lt;/code&amp;gt; where $errorMessage is null at success&lt;br /&gt;
* Return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; at failure&lt;br /&gt;
* Tagged union result type (not yet possible in latest PHP)&lt;br /&gt;
* Result object&lt;br /&gt;
&lt;br /&gt;
The con being that you have to manually propagate the error, where an exception can happen from anywhere in the stack trace without any other function knowing about it.&lt;br /&gt;
&lt;br /&gt;
=== If-statements ===&lt;br /&gt;
&lt;br /&gt;
If-statements should not be too long or hard to understand. If they are, consider factor out the checks into separate functions.&lt;br /&gt;
&lt;br /&gt;
=== Variables ===&lt;br /&gt;
&lt;br /&gt;
Descriptive naming.&lt;br /&gt;
&lt;br /&gt;
* Variables or ActiveRecord classes can have same name as the class, like &amp;quot;$survey&amp;quot; or &amp;quot;$question&amp;quot;&lt;br /&gt;
* Add -s for plural when it&#039;s an array: &amp;quot;$surveys&amp;quot;, &amp;quot;$questions&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Not so good:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better with model name and -s for plural:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$labelSets = LabelSet::model()-&amp;gt;findAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Looping ===&lt;br /&gt;
&lt;br /&gt;
The bodies of foreach-loops are often a good candidate to factor out into separate functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach ($questions as $question) {&lt;br /&gt;
  processQuestion($question);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Loop indexing should be named i, j, k etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
for ($i = 0; $i &amp;lt; $length; $i++) {&lt;br /&gt;
    for ($j = 0; $j &amp;lt; $otherLength; $j++) {&lt;br /&gt;
        // ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Function composition ===&lt;br /&gt;
&lt;br /&gt;
todo, &amp;quot;doThisAndThat&amp;quot; --&amp;gt; &amp;quot;doThis(doThat())&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
* This function generates an array containing the fieldcode, and matching data in the same order as the activate script&lt;br /&gt;
*&lt;br /&gt;
* @param Survey $survey Survey ActiveRecord model&lt;br /&gt;
* @param string $style &#039;short&#039; (default) or &#039;full&#039; - full creates extra information like default values&lt;br /&gt;
* @param boolean $force_refresh - Forces to really refresh the array, not just take the session copy&lt;br /&gt;
* @param bool|int $questionid Limit to a certain qid only (for question preview) - default is false&lt;br /&gt;
* @param string $sLanguage The language to use&lt;br /&gt;
* @param array $aDuplicateQIDs&lt;br /&gt;
* @return array&lt;br /&gt;
*/&lt;br /&gt;
function createFieldMap($survey, $style = &#039;short&#039;, $force_refresh = false, $questionid = false, $sLanguage = &#039;&#039;, &amp;amp;$aDuplicateQIDs = array())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Good:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Name is verb and noun&lt;br /&gt;
* Each argument is documented (almost)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Returns &amp;quot;array&amp;quot;, which can be anything. A separate class for &amp;quot;Fieldmap&amp;quot; would be better.&lt;br /&gt;
* Not clear if it should be &amp;quot;createFieldMap&amp;quot; or &amp;quot;createFieldmap&amp;quot; - is &amp;quot;fieldmap&amp;quot; one word or two?&lt;br /&gt;
* Very many boolean arguments. Should probably be a class with methods like &amp;lt;code&amp;gt;$fieldmapCreator-&amp;gt;setForceRefresh(true);&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
* Too long.&lt;br /&gt;
* Doesn&#039;t elaborate on what &amp;quot;fieldmap&amp;quot; really is, and does not link to any documentation about it.&lt;br /&gt;
* Access super-global, which makes it hard to test. Should instead return a data-transfer object, like:&amp;lt;br&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$_SESSION[&#039;fieldmap&#039;] = createFieldMap(...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Has no test&lt;br /&gt;
* Mixes Hungarian notation with non-Hungarian notation in the argument list&lt;br /&gt;
&lt;br /&gt;
== Defensive programming and error handling ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fail hard, fail early&#039;&#039;&#039;. The earlier the program crashes when something is wrong, the lower is the probability that you&#039;ll end up with corrupt data in the database that will later require manual labor to correct.&lt;br /&gt;
* Fail with &#039;&#039;&#039;actionable information&#039;&#039;&#039;&lt;br /&gt;
* Check input to functions and throw &amp;lt;code&amp;gt;InvalidArgumentException&amp;lt;/code&amp;gt; if it violates the function&#039;s pre-conditions&lt;br /&gt;
* Use assert for class-internal invariants&lt;br /&gt;
* Use assert to make assumptions explicit for the reader&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
=== PHPDoc ===&lt;br /&gt;
&lt;br /&gt;
* All functions must have a proper docblock&lt;br /&gt;
* The first line of the docblock should describe the relation between &#039;&#039;all&#039;&#039; the input arguments and the output of the function in a single sentence&lt;br /&gt;
* Each parameter must have the correct type&lt;br /&gt;
* Don&#039;t use &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; as type if it&#039;s a list of object - instead, use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; like so: &amp;lt;code&amp;gt;Survey[]&amp;lt;/code&amp;gt; for a list of surveys&lt;br /&gt;
&lt;br /&gt;
=== Technical documentation ===&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
== OOP design ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Object reification.&lt;br /&gt;
&lt;br /&gt;
Hierarchy depth.&lt;br /&gt;
&lt;br /&gt;
OOP ceremony, like getters and setters?&lt;br /&gt;
&lt;br /&gt;
Useful design patterns:&lt;br /&gt;
&lt;br /&gt;
* Command object pattern&lt;br /&gt;
* Wrapper&lt;br /&gt;
&lt;br /&gt;
== The PHP of yesterday ==&lt;br /&gt;
&lt;br /&gt;
Old idioms and habits that should be abandoned.&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t use associative arrays, almost never. Data-transfer objects have multiple benefits: they are clearly structured, can be documented, can enforce invariants. There&#039;s &#039;&#039;no&#039;&#039; performance overhead of objects compared to arrays in modern PHP.&lt;br /&gt;
* Objects are already passed by reference; arrays are value types&lt;br /&gt;
* Use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;array()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hungarian notation was useful before static analysis existed for PHP. These days it&#039;s better to use something that can be checked automatically: docblock @param and inline @var annotations.&lt;br /&gt;
&lt;br /&gt;
== The PHP of tomorrow ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Union tags&lt;br /&gt;
&lt;br /&gt;
Short functions&lt;br /&gt;
&lt;br /&gt;
Match expressions&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
XSS&lt;br /&gt;
&lt;br /&gt;
Database injection&lt;br /&gt;
&lt;br /&gt;
Permissions and roles&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Loading too many rows into PHP models&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
QA&lt;br /&gt;
&lt;br /&gt;
PHPUnit&lt;br /&gt;
&lt;br /&gt;
TDD&lt;br /&gt;
&lt;br /&gt;
Mocks, can&#039;t mock functions, static methods, mocking as a DSL, PHPUnit mocking tools&lt;br /&gt;
&lt;br /&gt;
Pure functions, referential transparency, side-effects&lt;br /&gt;
&lt;br /&gt;
Unit, functional, integration&lt;br /&gt;
&lt;br /&gt;
Don&#039;t test the framework; don&#039;t test PHP&lt;br /&gt;
&lt;br /&gt;
Makes sure the function or class lives up to its specification; make sure it will &#039;&#039;always&#039;&#039; live up to its specification, even in the future, to avoid regressions.&lt;br /&gt;
&lt;br /&gt;
== Static analysis ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;php -l&amp;lt;/code&amp;gt;, CodeSniffer, Mess Detector, Psalm&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
todo&lt;br /&gt;
&lt;br /&gt;
Books, links, videos&lt;/div&gt;</summary>
		<author><name>Olle</name></author>
	</entry>
</feed>