From 8bcdff4a855f621fec11ba255112c1d4ff57046e Mon Sep 17 00:00:00 2001 From: Jens Schuppe Date: Fri, 5 Apr 2024 12:24:11 +0200 Subject: [PATCH] Fix PHPStan issues --- CRM/Twingle/Config.php | 2 +- CRM/Twingle/Form/Profile.php | 238 +++++++++++----------- CRM/Twingle/Form/Settings.php | 24 ++- CRM/Twingle/Page/Configuration.php | 2 +- CRM/Twingle/Page/Profiles.php | 2 +- CRM/Twingle/Profile.php | 101 +++++---- CRM/Twingle/Submission.php | 135 ++++++------ CRM/Twingle/Tools.php | 35 ++-- CRM/Twingle/Upgrader.php | 10 +- Civi/Twingle/Exceptions/BaseException.php | 18 +- api/v3/TwingleDonation/Cancel.php | 18 +- api/v3/TwingleDonation/Endrecurring.php | 22 +- api/v3/TwingleDonation/Submit.php | 138 +++++++------ phpcs.xml.dist | 1 + phpstan.neon.template | 1 + twingle.php | 2 +- 16 files changed, 385 insertions(+), 364 deletions(-) diff --git a/CRM/Twingle/Config.php b/CRM/Twingle/Config.php index d75b7b4..29acf82 100644 --- a/CRM/Twingle/Config.php +++ b/CRM/Twingle/Config.php @@ -27,7 +27,7 @@ class CRM_Twingle_Config { * Get the options for protecting a recurring contribution linked Twingle * against ending or cancellation (because Twingle would keep on collecting them) * - * @return array + * @return array */ public static function getRecurringProtectionOptions() { return [ diff --git a/CRM/Twingle/Form/Profile.php b/CRM/Twingle/Form/Profile.php index efffdce..950aba2 100644 --- a/CRM/Twingle/Form/Profile.php +++ b/CRM/Twingle/Form/Profile.php @@ -16,7 +16,8 @@ declare(strict_types = 1); use CRM_Twingle_ExtensionUtil as E; -use Civi\Twingle\Exceptions\ProfileException as ProfileException; +use Civi\Twingle\Exceptions\ProfileException; +use Civi\Twingle\Exceptions\BaseException; /** * Form controller class @@ -30,7 +31,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * * The profile object the form is acting on. */ - protected $profile; + protected ?CRM_Twingle_Profile $profile = NULL; /** * @var string @@ -40,120 +41,128 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { protected $_op; /** - * @var array + * @var array * * A static cache of retrieved payment instruments found within * self::getPaymentInstruments(). */ - protected static $_paymentInstruments = NULL; + protected static $_paymentInstruments; /** - * @var array + * @var array * * A static cache of retrieved contribution statuses found within * static::getContributionStatusOptions(). */ - protected static $_contributionStatusOptions = NULL; + protected static array $_contributionStatusOptions; /** - * @var array + * @var array * * A static cache of retrieved groups found within static::getGroups(). */ - protected static $_groups = NULL; + protected static array $_groups; /** - * @var array + * @var array * * A static cache of retrieved newsletter groups found within * static::getNewsletterGroups(). */ - protected static $_newsletterGroups = NULL; + protected static array $_newsletterGroups; /** - * @var array + * @var array * * A static cache of retrieved campaigns found within static::getCampaigns(). */ - protected static $_campaigns = NULL; + protected static array $_campaigns; /** - * @var array + * @var array * * A static cache of retrieved financial types found within * static::getFinancialTypes(). */ - protected static $_financialTypes = NULL; + protected static array $_financialTypes; /** - * @var array + * @var array * * A static cache of retrieved genders found within * static::getGenderOptions(). */ - protected static $_genderOptions = NULL; + protected static array $_genderOptions; /** - * @var array + * @var array * * A static cache of retrieved prefixes found within * static::getGenderOptions(). */ - protected static $_prefixOptions = NULL; + protected static array $_prefixOptions; /** - * @var array + * @var array * * A static cache of retrieved location types found within * static::getLocationTypes(). */ - protected static $_locationTypes = NULL; + protected static array $_locationTypes; /** - * @var array + * @var array * * A static cache of retrieved location types found within * static::getXCMProfiles(). */ - protected static $_xcm_profiles = NULL; + protected static array $_xcm_profiles; /** - * @var array + * @var array * * A static cache of retrieved membership types found within * static::getMembershipTypes(). */ - protected static $_membershipTypes = NULL; + protected static array $_membershipTypes; /** - * @var array + * @var array * * A static cache of retrieved CiviSEPA creditors found within * static::getSepaCreditors(). */ - protected static $_sepaCreditors = NULL; + protected static array $_sepaCreditors; + + public function preProcess(): void { + // "Create" is the default operation. + $op = CRM_Utils_Request::retrieve('op', 'String', $this); + $this->_op = is_string($op) ? $op : 'create'; + + // Verify that a profile with the given name exists. + $profile_name = CRM_Utils_Request::retrieve('name', 'String', $this); + if (is_string($profile_name)) { + $this->profile = CRM_Twingle_Profile::getProfile($profile_name); + } + + // Set redirect destination. + $this->controller->_destination = CRM_Utils_System::url( + 'civicrm/admin/settings/twingle/profiles', + 'reset=1' + ); + + parent::preProcess(); + } /** * Builds the form structure. */ - public function buildQuickForm() { - // "Create" is the default operation. - if (!$this->_op = CRM_Utils_Request::retrieve('op', 'String', $this)) { - $this->_op = 'create'; - } - - // Verify that a profile with the given name exists. - $profile_name = CRM_Utils_Request::retrieve('name', 'String', $this); - if (!$this->profile = CRM_Twingle_Profile::getProfile($profile_name)) { - $profile_name = NULL; - } - - // Set redirect destination. - $this->controller->_destination = CRM_Utils_System::url('civicrm/admin/settings/twingle/profiles', 'reset=1'); + public function buildQuickForm(): void { + $profile_name = (isset($this->profile) ? $this->profile->getName() : NULL); switch ($this->_op) { case 'delete': - if ($profile_name) { + if (isset($profile_name)) { CRM_Utils_System::setTitle(E::ts('Delete Twingle API profile %1', [1 => $profile_name])); $this->addButtons([ [ @@ -168,10 +177,13 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { case 'edit': // When editing without a valid profile name, edit the default profile. - if (!$profile_name) { + if (!isset($profile_name)) { $profile_name = 'default'; $this->profile = CRM_Twingle_Profile::getProfile($profile_name); } + if (!isset($this->profile)) { + throw new BaseException(E::ts('Could not retrieve profile with name "%1"', [1 => $profile_name])); + } CRM_Utils_System::setTitle(E::ts('Edit Twingle API profile %1', [1 => $this->profile->getName()])); break; @@ -179,10 +191,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { // Retrieve the source profile name. $profile_name = CRM_Utils_Request::retrieve('source_name', 'String', $this); // When copying without a valid profile name, copy the default profile. - if (!$profile_name) { + if (!is_string($profile_name)) { $profile_name = 'default'; } - $this->profile = clone CRM_Twingle_Profile::getProfile($profile_name); + $originalProfile = CRM_Twingle_Profile::getProfile($profile_name); + if (!isset($originalProfile)) { + throw new BaseException(E::ts('Could not retrieve profile with name "%1"', [1 => $profile_name])); + } + $this->profile = clone $originalProfile; // Propose a new name for this profile. $profile_name = $profile_name . '_copy'; @@ -192,7 +208,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { case 'create': // Load factory default profile values. - $this->profile = CRM_Twingle_Profile::createDefaultProfile($profile_name); + $this->profile = CRM_Twingle_Profile::createDefaultProfile($profile_name ?? 'default'); CRM_Utils_System::setTitle(E::ts('New Twingle API profile')); break; } @@ -354,10 +370,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { // field name 'newsletter_double_opt_in', // field label - E::ts('Use Double-Opt-In for newsletter'), - // is not required - FALSE, - [] + E::ts('Use Double-Opt-In for newsletter') ); $this->add( @@ -461,8 +474,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { $this->add( 'text', 'membership_postprocess_call', - E::ts('API Call for Membership Postprocessing'), - FALSE + E::ts('API Call for Membership Postprocessing') ); $this->addRule( 'membership_postprocess_call', @@ -521,9 +533,8 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { /** * Validates the profile form. * - * @return bool|array - * TRUE when the form was successfully validated, or an array of error - * messages, keyed by form element name. + * @return bool + * TRUE when the form was successfully validated. */ public function validate() { $values = $this->exportValues(); @@ -531,14 +542,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { // Validate new profile names. if ( isset($values['name']) - && ($values['name'] != $this->profile->getName() || $this->_op != 'edit') - && !empty(CRM_Twingle_Profile::getProfile($values['name'])) + && (!isset($this->profile) || $values['name'] != $this->profile->getName() || $this->_op != 'edit') + && NULL !== CRM_Twingle_Profile::getProfile($values['name']) ) { $this->_errors['name'] = E::ts('A profile with this name already exists.'); } // Restrict profile names to alphanumeric characters and the underscore. - if (isset($values['name']) && preg_match('/[^A-Za-z0-9\_]/', $values['name'])) { + if (isset($values['name']) && 1 === preg_match('/[^A-Za-z0-9\_]/', $values['name'])) { $this->_errors['name'] = E::ts('Only alphanumeric characters and the underscore (_) are allowed for profile names.'); } @@ -548,14 +559,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { if (isset($values['custom_field_mapping'])) { $custom_field_mapping = preg_split('/\r\n|\r|\n/', $values['custom_field_mapping'], -1, PREG_SPLIT_NO_EMPTY); if (!is_array($custom_field_mapping)) { - throw new Exception( + throw new BaseException( E::ts('Could not parse custom field mapping.') ); } foreach ($custom_field_mapping as $custom_field_map) { $custom_field_map = explode('=', $custom_field_map); if (count($custom_field_map) !== 2) { - throw new Exception( + throw new BaseException( E::ts('Could not parse custom field mapping.') ); } @@ -568,12 +579,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { 'id' => $custom_field_id, ]); } - catch (CiviCRM_API3_Exception $exception) { - throw new Exception( + catch (CRM_Core_Exception $exception) { + throw new BaseException( E::ts( 'Custom field custom_%1 does not exist.', [1 => $custom_field_id] - ) + ), + NULL, + $exception ); } @@ -592,18 +605,20 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { ], ]); } - catch (CiviCRM_API3_Exception $exception) { - throw new Exception( + catch (CRM_Core_Exception $exception) { + throw new BaseException( E::ts( 'Custom field custom_%1 is not in a CustomGroup that extends one of the supported CiviCRM entities.', [1 => $custom_field['id']] - ) - ); + ), + NULL, + $exception + ); } } } } - catch (Exception $exception) { + catch (BaseException $exception) { $this->_errors['custom_field_mapping'] = $exception->getMessage(); } @@ -612,12 +627,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { /** * Set the default values (i.e. the profile's current data) in the form. + * + * @return array */ public function setDefaultValues() { $defaults = parent::setDefaultValues(); - if (in_array($this->_op, ['create', 'edit', 'copy'])) { - $defaults['name'] = $this->profile->getName(); - $profile_data = $this->profile->getData(); + if (in_array($this->_op, ['create', 'edit', 'copy'], TRUE)) { + $defaults['name'] = isset($this->profile) ? $this->profile->getName() : NULL; + $profile_data = isset($this->profile) ? $this->profile->getData() : []; foreach ($profile_data as $element_name => $value) { $defaults[$element_name] = $value; } @@ -632,11 +649,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { /** * Store the values submitted with the form in the profile. */ - public function postProcess() { + public function postProcess(): void { $values = $this->exportValues(); try { - if (in_array($this->_op, ['create', 'edit', 'copy'])) { - if (empty($values['name'])) { + if (!isset($this->profile)) { + throw new BaseException(E::ts('No profile set.')); + } + if (in_array($this->_op, ['create', 'edit', 'copy'], TRUE)) { + if (!is_string($values['name'])) { $values['name'] = 'default'; } $this->profile->setName($values['name']); @@ -670,9 +690,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves location types present within the system as options for select * form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getLocationTypes() { if (!isset(static::$_locationTypes)) { @@ -682,7 +700,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { 'is_active' => 1, ]); foreach ($query['values'] as $type) { - static::$_locationTypes[$type['id']] = $type['name']; + static::$_locationTypes[(int) $type['id']] = $type['name']; } } return static::$_locationTypes; @@ -691,11 +709,11 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { /** * Retrieves XCM profiles (if supported). 'default' profile is always available * - * @return array + * @return array */ public static function getXCMProfiles() { if (!isset(static::$_xcm_profiles)) { - if (method_exists('CRM_Xcm_Configuration', 'getProfileList')) { + if (class_exists('CRM_Xcm_Configuration')) { static::$_xcm_profiles = [ '' => E::ts('<select profile>'), ]; @@ -712,9 +730,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves financial types present within the system as options for select * form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getFinancialTypes() { if (!isset(static::$_financialTypes)) { @@ -725,7 +741,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { 'return' => 'id,name', ]); foreach ($query['values'] as $type) { - static::$_financialTypes[$type['id']] = $type['name']; + static::$_financialTypes[(int) $type['id']] = $type['name']; } } return static::$_financialTypes; @@ -735,9 +751,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves membership types present within the system as options for select * form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getMembershipTypes() { if (!isset(static::$_membershipTypes)) { @@ -758,9 +772,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves genders present within the system as options for select form * elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getGenderOptions() { if (!isset(static::$_genderOptions)) { @@ -775,7 +787,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { ], ]); foreach ($query['values'] as $gender) { - static::$_genderOptions[$gender['value']] = $gender['label']; + static::$_genderOptions[(int) $gender['value']] = $gender['label']; } } return static::$_genderOptions; @@ -785,9 +797,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves prefixes present within the system as options for select form * elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getPrefixOptions() { if (!isset(static::$_prefixOptions)) { @@ -802,7 +812,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { ], ]); foreach ($query['values'] as $prefix) { - static::$_prefixOptions[$prefix['value']] = $prefix['label']; + static::$_prefixOptions[(int) $prefix['value']] = $prefix['label']; } } return static::$_prefixOptions; @@ -811,9 +821,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { /** * Retrieves CiviSEPA creditors as options for select form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getSepaCreditors() { if (!isset(static::$_sepaCreditors)) { @@ -823,7 +831,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { 'option.limit' => 0, ]); foreach ($result['values'] as $sepa_creditor) { - static::$_sepaCreditors[$sepa_creditor['id']] = $sepa_creditor['name']; + static::$_sepaCreditors[(int) $sepa_creditor['id']] = $sepa_creditor['name']; } } } @@ -834,9 +842,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves payment instruments present within the system as options for * select form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getPaymentInstruments() { if (!isset(self::$_paymentInstruments)) { @@ -869,9 +875,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { /** * Retrieves contribution statuses as options for select form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getContributionStatusOptions() { if (!isset(self::$_contributionStatusOptions)) { @@ -889,7 +893,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { ); foreach ($query['values'] as $contribution_status) { - self::$_contributionStatusOptions[$contribution_status['value']] = $contribution_status['label']; + self::$_contributionStatusOptions[(int) $contribution_status['value']] = $contribution_status['label']; } } @@ -900,9 +904,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves active groups used as mailing lists within the system as options * for select form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array * */ public static function getNewsletterGroups() { @@ -935,9 +937,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { /** * Retrieves active groups as options for select form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getGroups() { if (!isset(static::$_groups)) { @@ -948,7 +948,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { 'return' => 'id,name', ]); foreach ($query['values'] as $group) { - static::$_groups[$group['id']] = $group['name']; + static::$_groups[(int) $group['id']] = $group['name']; } } return static::$_groups; @@ -958,9 +958,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves active groups used as postal mailing lists within the system as * options for select form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getPostinfoGroups() { return static::getGroups(); @@ -970,9 +968,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { * Retrieves active groups used as donation receipt requester lists within the * system as options for select form elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getDonationReceiptGroups() { return static::getGroups(); @@ -981,9 +977,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { /** * Retrieves campaigns as options for select elements. * - * @return array - * - * @throws \CiviCRM_API3_Exception + * @return array */ public static function getCampaigns() { if (!isset(static::$_campaigns)) { @@ -996,7 +990,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { ], ]); foreach ($query['values'] as $campaign) { - static::$_campaigns[$campaign['id']] = $campaign['title']; + static::$_campaigns[(int) $campaign['id']] = $campaign['title']; } } return static::$_campaigns; diff --git a/CRM/Twingle/Form/Settings.php b/CRM/Twingle/Form/Settings.php index 6303dc9..7852311 100644 --- a/CRM/Twingle/Form/Settings.php +++ b/CRM/Twingle/Form/Settings.php @@ -25,7 +25,8 @@ use CRM_Twingle_ExtensionUtil as E; class CRM_Twingle_Form_Settings extends CRM_Core_Form { /** - * @var arraylistofallsettingsoptions + * @var array + * List of all settings options. */ public static $SETTINGS_LIST = [ 'twingle_prefix', @@ -41,7 +42,7 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form { /** * @inheritdoc */ - public function buildQuickForm() { + public function buildQuickForm(): void { // Set redirect destination. $this->controller->_destination = CRM_Utils_System::url('civicrm/admin/settings/twingle', 'reset=1'); @@ -131,15 +132,14 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form { parent::validate(); // if activity creation is active, make sure the fields are set - $protection_mode = CRM_Utils_Array::value('twingle_protect_recurring', $this->_submitValues); + $protection_mode = $this->_submitValues['twingle_protect_recurring'] ?? NULL; if ($protection_mode == CRM_Twingle_Config::RCUR_PROTECTION_ACTIVITY) { foreach (['twingle_protect_recurring_activity_type', 'twingle_protect_recurring_activity_subject', 'twingle_protect_recurring_activity_status', 'twingle_protect_recurring_activity_assignee', ] as $activity_field) { - $current_value = CRM_Utils_Array::value($activity_field, $this->_submitValues); - if (empty($current_value)) { + if (NULL !== ($this->_submitValues[$activity_field] ?? NULL)) { $this->_errors[$activity_field] = E::ts('This is required for activity creation'); } } @@ -151,12 +151,12 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form { /** * @inheritdoc */ - public function postProcess() { + public function postProcess(): void { $values = $this->exportValues(); // store settings foreach (self::$SETTINGS_LIST as $setting) { - Civi::settings()->set($setting, CRM_Utils_Array::value($setting, $values)); + Civi::settings()->set($setting, $values[$setting]); } parent::postProcess(); @@ -164,11 +164,13 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form { /** * Get a list of option group items - * @param $group_id string group ID or name - * @return array list of ID(value) => label - * @throws CiviCRM_API3_Exception + * @param string $group_id + * Group ID or name. + * @param array $reserved + * @return array list of ID(value) => label + * @throws \CRM_Core_Exception */ - protected function getOptionValueList($group_id, $reserved = [0, 1]) { + protected function getOptionValueList(string $group_id, array $reserved = [0, 1]): array { $list = ['' => E::ts('-select-')]; $query = civicrm_api3('OptionValue', 'get', [ 'option_group_id' => $group_id, diff --git a/CRM/Twingle/Page/Configuration.php b/CRM/Twingle/Page/Configuration.php index f30cf67..d0e0e3c 100644 --- a/CRM/Twingle/Page/Configuration.php +++ b/CRM/Twingle/Page/Configuration.php @@ -19,7 +19,7 @@ use CRM_Twingle_ExtensionUtil as E; class CRM_Twingle_Page_Configuration extends CRM_Core_Page { - public function run() { + public function run(): void { parent::run(); } diff --git a/CRM/Twingle/Page/Profiles.php b/CRM/Twingle/Page/Profiles.php index e8f014b..80d7de2 100644 --- a/CRM/Twingle/Page/Profiles.php +++ b/CRM/Twingle/Page/Profiles.php @@ -19,7 +19,7 @@ use CRM_Twingle_ExtensionUtil as E; class CRM_Twingle_Page_Profiles extends CRM_Core_Page { - public function run() { + public function run():void { CRM_Utils_System::setTitle(E::ts('Twingle API Profiles')); $profiles = []; foreach (CRM_Twingle_Profile::getProfiles() as $profile_name => $profile) { diff --git a/CRM/Twingle/Profile.php b/CRM/Twingle/Profile.php index e4494dd..6553505 100644 --- a/CRM/Twingle/Profile.php +++ b/CRM/Twingle/Profile.php @@ -28,20 +28,20 @@ class CRM_Twingle_Profile { * @var string * The name of the profile. */ - protected $name = NULL; + protected $name; /** - * @var array + * @var array * The properties of the profile. */ - protected $data = NULL; + protected $data; /** * CRM_Twingle_Profile constructor. * * @param string $name * The name of the profile. - * @param array $data + * @param array $data * The properties of the profile */ public function __construct($name, $data) { @@ -56,7 +56,7 @@ class CRM_Twingle_Profile { /** * Logs (production) access to this profile */ - public function logAccess() { + public function logAccess(): void { CRM_Core_DAO::executeQuery(' UPDATE civicrm_twingle_profile SET @@ -80,19 +80,29 @@ class CRM_Twingle_Profile { }, explode(',', $selector) ); - return in_array($project_id, $project_ids); + return in_array($project_id, $project_ids, TRUE); } /** - * @return array + * Retrieves the profile's configured custom field mapping. + * + * @return array * The profile's configured custom field mapping */ public function getCustomFieldMapping() { $custom_field_mapping = []; - if (!empty($custom_field_definition = $this->getAttribute('custom_field_mapping'))) { - foreach (preg_split('/\r\n|\r|\n/', $custom_field_definition, -1, PREG_SPLIT_NO_EMPTY) as $custom_field_map) { - [$twingle_field_name, $custom_field_name] = explode('=', $custom_field_map); - $custom_field_mapping[$twingle_field_name] = $custom_field_name; + if (is_string($custom_field_definition = $this->getAttribute('custom_field_mapping'))) { + $custom_field_maps = preg_split( + '/\r\n|\r|\n/', + $custom_field_definition, + -1, + PREG_SPLIT_NO_EMPTY + ); + if (FALSE !== $custom_field_maps) { + foreach ($custom_field_maps as $custom_field_map) { + [$twingle_field_name, $custom_field_name] = explode('=', $custom_field_map); + $custom_field_mapping[$twingle_field_name] = $custom_field_name; + } } } return $custom_field_mapping; @@ -101,7 +111,7 @@ class CRM_Twingle_Profile { /** * Retrieves all data attributes of the profile. * - * @return array + * @return array */ public function getData() { return $this->data; @@ -119,9 +129,9 @@ class CRM_Twingle_Profile { /** * Sets the profile name. * - * @param $name + * @param string $name */ - public function setName($name) { + public function setName(string $name): void { $this->name = $name; } @@ -146,8 +156,8 @@ class CRM_Twingle_Profile { * @throws \Civi\Twingle\Exceptions\ProfileException * When the attribute name is not known. */ - public function setAttribute($attribute_name, $value) { - if (!in_array($attribute_name, self::allowedAttributes())) { + public function setAttribute($attribute_name, $value): void { + if (!in_array($attribute_name, self::allowedAttributes(), TRUE)) { throw new ProfileException( E::ts('Unknown attribute %1.', [1 => $attribute_name]), ProfileException::ERROR_CODE_UNKNOWN_PROFILE_ATTRIBUTE @@ -160,27 +170,19 @@ class CRM_Twingle_Profile { /** * Get the CiviCRM transaction ID (to be used in contributions and recurring contributions) * - * @param $twingle_id string Twingle ID + * @param string $twingle_id Twingle ID * @return string CiviCRM transaction ID */ - public function getTransactionID($twingle_id) { + public function getTransactionID(string $twingle_id) { $prefix = Civi::settings()->get('twingle_prefix'); - if (empty($prefix)) { - return $twingle_id; - } - else { - return $prefix . $twingle_id; - } + return ($prefix ?? '') . $twingle_id; } /** * Verifies whether the profile is valid (i.e. consistent and not colliding * with other profiles). - * - * @throws Exception - * When the profile could not be successfully validated. */ - public function verifyProfile() { + public function verifyProfile(): void { // TODO: check // data of this profile consistent? // conflicts with other profiles? @@ -189,14 +191,14 @@ class CRM_Twingle_Profile { /** * Persists the profile within the CiviCRM settings. */ - public function saveProfile() { + public function saveProfile(): void { // make sure it's valid $this->verifyProfile(); // check if the profile exists $profile_id = CRM_Core_DAO::singleValueQuery( 'SELECT id FROM civicrm_twingle_profile WHERE name = %1', [1 => [$this->name, 'String']]); - if ($profile_id) { + if (isset($profile_id)) { // existing profile -> just update the config CRM_Core_DAO::executeQuery( 'UPDATE civicrm_twingle_profile SET config = %2 WHERE name = %1', @@ -219,7 +221,7 @@ class CRM_Twingle_Profile { /** * Deletes the profile from the database */ - public function deleteProfile() { + public function deleteProfile(): void { CRM_Core_DAO::executeQuery( 'DELETE FROM civicrm_twingle_profile WHERE name = %1', [1 => [$this->name, 'String']] @@ -229,7 +231,7 @@ class CRM_Twingle_Profile { /** * Returns an array of attributes allowed for a profile. * - * @return array + * @return array */ public static function allowedAttributes() { return array_merge( @@ -273,9 +275,9 @@ class CRM_Twingle_Profile { /** * Retrieves a list of supported payment methods. * - * @return array + * @return array */ - public static function paymentInstruments() { + public static function paymentInstruments(): array { return [ 'pi_banktransfer' => E::ts('Bank transfer'), 'pi_debit_manual' => E::ts('Debit manual'), @@ -364,10 +366,9 @@ class CRM_Twingle_Profile { * which is responsible for processing the project's data. * Returns the default profile if no match was found. * - * @param $project_id + * @param string $project_id * * @return CRM_Twingle_Profile - * @throws \Civi\Core\Exception\DBQueryException */ public static function getProfileForProject($project_id) { $profiles = self::getProfiles(); @@ -379,9 +380,8 @@ class CRM_Twingle_Profile { } // If none matches, use the default profile. - $default_profile = $profiles['default']; - if (!empty($default_profile)) { - return $default_profile; + if (isset($profiles['default'])) { + return $profiles['default']; } else { throw new ProfileException( @@ -396,26 +396,21 @@ class CRM_Twingle_Profile { * * @param string $name * - * @return CRM_Twingle_Profile | NULL + * @return CRM_Twingle_Profile|NULL */ public static function getProfile($name) { - if (!empty($name)) { - $profile_data = CRM_Core_DAO::singleValueQuery( - 'SELECT config FROM civicrm_twingle_profile WHERE name = %1', - [1 => [$name, 'String']] - ); - if ($profile_data) { - return new CRM_Twingle_Profile($name, json_decode($profile_data, 1)); - } - } - return NULL; + $profile_data = CRM_Core_DAO::singleValueQuery( + 'SELECT config FROM civicrm_twingle_profile WHERE name = %1', + [1 => [$name, 'String']] + ); + return isset($profile_data) ? new CRM_Twingle_Profile($name, (array) json_decode($profile_data, TRUE)) : NULL; } /** * Retrieves the list of all profiles persisted within the current CiviCRM * settings, including the default profile. * - * @return array + * @return array * profile_name => CRM_Twingle_Profile */ public static function getProfiles() { @@ -439,7 +434,9 @@ class CRM_Twingle_Profile { */ public static function getProfileStats() { $stats = []; - $profile_data = CRM_Core_DAO::executeQuery('SELECT name, last_access, access_counter FROM civicrm_twingle_profile'); + $profile_data = CRM_Core_DAO::executeQuery( + 'SELECT name, last_access, access_counter FROM civicrm_twingle_profile' + ); while ($profile_data->fetch()) { // phpcs:disable Drupal.Arrays.Array.ArrayIndentation $stats[$profile_data->name] = [ diff --git a/CRM/Twingle/Submission.php b/CRM/Twingle/Submission.php index 7c8ed9c..a6ce330 100644 --- a/CRM/Twingle/Submission.php +++ b/CRM/Twingle/Submission.php @@ -16,6 +16,7 @@ declare(strict_types = 1); use CRM_Twingle_ExtensionUtil as E; +use Civi\Twingle\Exceptions\BaseException; class CRM_Twingle_Submission { @@ -40,18 +41,18 @@ class CRM_Twingle_Submission { public const EMPLOYER_RELATIONSHIP_TYPE_ID = 5; /** - * @param array &$params + * @param array &$params * A reference to the parameters array of the submission. * * @param \CRM_Twingle_Profile $profile * The Twingle profile to use for validation, defaults to the default * profile. * - * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception * When invalid parameters have been submitted. */ - public static function validateSubmission(&$params, $profile = NULL) { - if (!$profile) { + public static function validateSubmission(&$params, $profile = NULL): void { + if (!isset($profile)) { $profile = CRM_Twingle_Profile::createDefaultProfile(); } @@ -62,8 +63,8 @@ class CRM_Twingle_Submission { 'quarterly', 'yearly', 'monthly', - ])) { - throw new CiviCRM_API3_Exception( + ], TRUE)) { + throw new CRM_Core_Exception( E::ts('Invalid donation rhythm.'), 'invalid_format' ); @@ -71,8 +72,9 @@ class CRM_Twingle_Submission { // Get the payment instrument defined within the profile, or return an error // if none matches (i.e. an unknown payment method was submitted). - if (!$payment_instrument_id = $profile->getAttribute('pi_' . $params['payment_method'])) { - throw new CiviCRM_API3_Exception( + $payment_instrument_id = $profile->getAttribute('pi_' . $params['payment_method']); + if (!isset($payment_instrument_id)) { + throw new CRM_Core_Exception( E::ts('Payment method could not be matched to existing payment instrument.'), 'invalid_format' ); @@ -80,16 +82,16 @@ class CRM_Twingle_Submission { $params['payment_instrument_id'] = $payment_instrument_id; // Validate date for parameter "confirmed_at". - if (!DateTime::createFromFormat('YmdHis', $params['confirmed_at'])) { - throw new CiviCRM_API3_Exception( + if (FALSE === DateTime::createFromFormat('YmdHis', $params['confirmed_at'])) { + throw new CRM_Core_Exception( E::ts('Invalid date for parameter "confirmed_at".'), 'invalid_format' ); } // Validate date for parameter "user_birthdate". - if (!empty($params['user_birthdate']) && !DateTime::createFromFormat('Ymd', $params['user_birthdate'])) { - throw new CiviCRM_API3_Exception( + if (!empty($params['user_birthdate']) && FALSE === DateTime::createFromFormat('Ymd', $params['user_birthdate'])) { + throw new CRM_Core_Exception( E::ts('Invalid date for parameter "user_birthdate".'), 'invalid_format' ); @@ -97,9 +99,10 @@ class CRM_Twingle_Submission { // Get the gender ID defined within the profile, or return an error if none // matches (i.e. an unknown gender was submitted). - if (!empty($params['user_gender'])) { - if (!$gender_id = $profile->getAttribute('gender_' . $params['user_gender'])) { - throw new CiviCRM_API3_Exception( + if (is_string($params['user_gender'])) { + $gender_id = $profile->getAttribute('gender_' . $params['user_gender']); + if (!isset($gender_id)) { + throw new CRM_Core_Exception( E::ts('Gender could not be matched to existing gender.'), 'invalid_format' ); @@ -108,12 +111,12 @@ class CRM_Twingle_Submission { } // Validate custom fields parameter, if given. - if (!empty($params['custom_fields'])) { + if (isset($params['custom_fields'])) { if (is_string($params['custom_fields'])) { $params['custom_fields'] = json_decode($params['custom_fields'], TRUE); } if (!is_array($params['custom_fields'])) { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('Invalid format for custom fields.'), 'invalid_format' ); @@ -121,13 +124,13 @@ class CRM_Twingle_Submission { } // Validate campaign_id, if given. - if (!empty($params['campaign_id'])) { + if (isset($params['campaign_id'])) { // Check whether campaign_id is a numeric string and cast it to an integer. if (is_numeric($params['campaign_id'])) { $params['campaign_id'] = intval($params['campaign_id']); } else { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('campaign_id must be a numeric string. '), 'invalid_format' ); @@ -140,7 +143,7 @@ class CRM_Twingle_Submission { ['id' => $params['campaign_id']] ); } - catch (CiviCRM_API3_Exception $e) { + catch (CRM_Core_Exception $e) { unset($params['campaign_id']); } } @@ -152,28 +155,33 @@ class CRM_Twingle_Submission { * * @param string $contact_type * The contact type to look for/to create. - * @param array $contact_data + * @param array $contact_data * Data to use for contact lookup/to create a contact with. * @param CRM_Twingle_Profile $profile * Profile used for this process - * @param array $submission + * @param array $submission * Submission data * * @return int|NULL * The ID of the matching/created contact, or NULL if no matching contact * was found and no new contact could be created. - * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception * When invalid data was given. */ - public static function getContact($contact_type, $contact_data, $profile, $submission = []) { + public static function getContact( + string $contact_type, + array $contact_data, + CRM_Twingle_Profile $profile, + array $submission = [] + ) { // If no parameters are given, do nothing. - if (empty($contact_data)) { + if ([] === $contact_data) { return NULL; } // add xcm profile $xcm_profile = $profile->getAttribute('xcm_profile'); - if (!empty($xcm_profile)) { + if (isset($xcm_profile) && '' !== $xcm_profile) { $contact_data['xcm_profile'] = $xcm_profile; } @@ -181,7 +189,7 @@ class CRM_Twingle_Submission { CRM_Twingle_Submission::setCampaign($contact_data, 'contact', $submission, $profile); // Prepare values: country. - if (!empty($contact_data['country'])) { + if (isset($contact_data['country'])) { if (is_numeric($contact_data['country'])) { // If a country ID is given, update the parameters. $contact_data['country_id'] = $contact_data['country']; @@ -190,12 +198,12 @@ class CRM_Twingle_Submission { else { // Look up the country depending on the given ISO code. $country = civicrm_api3('Country', 'get', ['iso_code' => $contact_data['country']]); - if (!empty($country['id'])) { + if (isset($country['id'])) { $contact_data['country_id'] = $country['id']; unset($contact_data['country']); } else { - throw new \CiviCRM_API3_Exception( + throw new \CRM_Core_Exception( E::ts('Unknown country %1.', [1 => $contact_data['country']]), 'invalid_format' ); @@ -204,7 +212,7 @@ class CRM_Twingle_Submission { } // Prepare values: language. - if (!empty($contact_data['preferred_language'])) { + if (is_string($contact_data['preferred_language']) && '' !== $contact_data['preferred_language']) { $mapping = CRM_Core_I18n_PseudoConstant::longForShortMapping(); // Override the default mapping for German. $mapping['de'] = 'de_DE'; @@ -214,39 +222,31 @@ class CRM_Twingle_Submission { // Pass to XCM. $contact_data['contact_type'] = $contact_type; $contact = civicrm_api3('Contact', 'getorcreate', $contact_data); - if (empty($contact['id'])) { - return NULL; - } - return $contact['id']; + return isset($contact['id']) ? (int) $contact['id'] : NULL; } /** * Shares an organisation's work address, unless the contact already has one. * - * @param $contact_id + * @param int $contact_id * The ID of the contact to share the organisation address with. - * @param $organisation_id + * @param int $organisation_id * The ID of the organisation whose address to share with the contact. - * @param $location_type_id + * @param int $location_type_id * The ID of the location type to use for address lookup. * * @return boolean * Whether the organisation address has been shared with the contact. * - * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception * When looking up or creating the shared address failed. */ public static function shareWorkAddress( - $contact_id, - $organisation_id, - $location_type_id = self::LOCATION_TYPE_ID_WORK + int $contact_id, + int $organisation_id, + int $location_type_id = self::LOCATION_TYPE_ID_WORK ) { - if (empty($organisation_id)) { - // Only if organisation exists. - return FALSE; - } - // Check whether organisation has a WORK address. $existing_org_addresses = civicrm_api3('Address', 'get', [ 'contact_id' => $organisation_id, @@ -285,13 +285,9 @@ class CRM_Twingle_Submission { * @param int $organisation_id * The ID of the employer contact. * - * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception */ - public static function updateEmployerRelation($contact_id, $organisation_id) { - if (empty($contact_id) || empty($organisation_id)) { - return; - } - + public static function updateEmployerRelation(int $contact_id, int $organisation_id): void { // see if there is already one $existing_relationship = civicrm_api3('Relationship', 'get', [ 'relationship_type_id' => self::EMPLOYER_RELATIONSHIP_TYPE_ID, @@ -318,15 +314,15 @@ class CRM_Twingle_Submission { * functionality is activated within the Twingle extension settings. * * @return bool - * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception */ public static function civiSepaEnabled() { $sepa_extension = civicrm_api3('Extension', 'get', [ 'full_name' => 'org.project60.sepa', 'is_active' => 1, ]); - return Civi::settings()->get('twingle_use_sepa') - && $sepa_extension['count']; + return (bool) Civi::settings()->get('twingle_use_sepa') + && $sepa_extension['count'] >= 0; } /** @@ -337,7 +333,7 @@ class CRM_Twingle_Submission { * The submitted "donation_rhythm" paramter according to the API action * specification. * - * @return array + * @return array{'frequency_unit'?: string, 'frequency_interval'?: int} * An array with "frequency_unit" and "frequency_interval" keys, to be added * to contribution parameter arrays. */ @@ -378,19 +374,21 @@ class CRM_Twingle_Submission { * @return int * The next possible day of this or the next month to start collecting. */ - public static function getSEPACycleDay($start_date, $creditor_id) { + public static function getSEPACycleDay($start_date, $creditor_id): int { $buffer_days = (int) CRM_Sepa_Logic_Settings::getSetting('pp_buffer_days'); $frst_notice_days = (int) CRM_Sepa_Logic_Settings::getSetting('batching.FRST.notice', $creditor_id); - $earliest_rcur_date = strtotime("$start_date + $frst_notice_days days + $buffer_days days"); + if (FALSE === ($earliest_rcur_date = strtotime("$start_date + $frst_notice_days days + $buffer_days days"))) { + throw new BaseException(E::ts('Could not calculate SEPA cycle day from configuration.')); + } // Find the next cycle day $cycle_days = CRM_Sepa_Logic_Settings::getListSetting('cycledays', range(1, 28), $creditor_id); $earliest_cycle_day = $earliest_rcur_date; - while (!in_array(date('j', $earliest_cycle_day), $cycle_days)) { + while (!in_array(date('j', $earliest_cycle_day), $cycle_days, TRUE)) { $earliest_cycle_day = strtotime('+ 1 day', $earliest_cycle_day); } - return date('j', $earliest_cycle_day); + return (int) date('j', $earliest_cycle_day); } /** @@ -399,16 +397,21 @@ class CRM_Twingle_Submission { * from the submission data. Should that be empty, the profile's default * campaign is used. * - * @param array $entity_data + * @param array $entity_data * the data set where the campaign_id should be set * @param string $context * defines the type of the entity_data: one of 'contribution', 'membership','mandate', 'recurring', 'contact' - * @param array $submission + * @param array $submission * the submitted data * @param CRM_Twingle_Profile $profile * the twingle profile used */ - public static function setCampaign(&$entity_data, $context, $submission, $profile) { + public static function setCampaign( + array &$entity_data, + string $context, + array $submission, + CRM_Twingle_Profile $profile + ): void { // first: make sure it's not set from other workflows unset($entity_data['campaign_id']); @@ -418,13 +421,13 @@ class CRM_Twingle_Submission { // backward compatibility: $enabled_contexts = ['contribution', 'contact']; } - if (in_array($context, $enabled_contexts)) { + if (in_array($context, $enabled_contexts, TRUE)) { // use the submitted campaign if set - if (!empty($submission['campaign_id'])) { + if (is_numeric($submission['campaign_id'])) { $entity_data['campaign_id'] = $submission['campaign_id']; } // otherwise use the profile's - elseif (!empty($campaign = $profile->getAttribute('campaign'))) { + elseif (is_numeric($campaign = $profile->getAttribute('campaign'))) { $entity_data['campaign_id'] = $campaign; } } diff --git a/CRM/Twingle/Tools.php b/CRM/Twingle/Tools.php index 5c4007c..9725a96 100644 --- a/CRM/Twingle/Tools.php +++ b/CRM/Twingle/Tools.php @@ -16,6 +16,7 @@ declare(strict_types = 1); use CRM_Twingle_ExtensionUtil as E; +use Civi\Twingle\Exceptions\BaseException; class CRM_Twingle_Tools { @@ -29,11 +30,11 @@ class CRM_Twingle_Tools { * Check if the attempted modification of the recurring contribution is allowed. * If not, an exception will be raised * - * @param $recurring_contribution_id int - * @param $change array + * @param int $recurring_contribution_id + * @param array $change * @throws Exception if the change is not allowed */ - public static function checkRecurringContributionChange($recurring_contribution_id, $change) { + public static function checkRecurringContributionChange(int $recurring_contribution_id, array $change): void { // check if a change to the status is planned if (empty($change['contribution_status_id'])) { return; @@ -77,14 +78,17 @@ class CRM_Twingle_Tools { } // this _IS_ on of the cases where we should step in: - CRM_Twingle_Tools::processRecurringContributionTermination($recurring_contribution_id, $recurring_contribution); + CRM_Twingle_Tools::processRecurringContributionTermination( + $recurring_contribution_id, + $recurring_contribution + ); } /** * @param $recurring_contribution_id int recurring contribution ID to check * @param $recurring_contribution array recurring contribution data, optional * @return bool|null true, false or null if can't be determined - * @throws CiviCRM_API3_Exception + * @throws \CRM_Core_Exception */ public static function isTwingleRecurringContribution($recurring_contribution_id, $recurring_contribution = NULL) { // this currently only works with prefixes @@ -106,11 +110,13 @@ class CRM_Twingle_Tools { /** * Execute the recurring contribution protection * - * @param $recurring_contribution_id int recurring contribution ID - * @param $recurring_contribution array recurring contribution fields + * @param int $recurring_contribution_id + * Recurring contribution ID. + * @param array $recurring_contribution + * Recurring contribution fields. * @throws Exception could be one of the measures */ - public static function processRecurringContributionTermination($recurring_contribution_id, $recurring_contribution) { + public static function processRecurringContributionTermination(int $recurring_contribution_id, array $recurring_contribution) { // check if we're suspended if (self::$protection_suspended) { return; @@ -124,7 +130,7 @@ class CRM_Twingle_Tools { case CRM_Twingle_Config::RCUR_PROTECTION_EXCEPTION: // phpcs:disable Generic.Files.LineLength.TooLong - throw new Exception(E::ts( + throw new BaseException(E::ts( 'This is a Twingle recurring contribution. It should be terminated through the Twingle interface, otherwise it will still be collected.' )); @@ -183,10 +189,10 @@ class CRM_Twingle_Tools { /** * Check if the given payment instrument is SEPA * - * @param $payment_instrument_id string payment instrument + * @param string $payment_instrument_id * @return boolean */ - public static function isSDD($payment_instrument_id) { + public static function isSDD(string $payment_instrument_id) { static $sepa_payment_instruments = NULL; if ($sepa_payment_instruments === NULL) { // init with instrument names @@ -208,11 +214,10 @@ class CRM_Twingle_Tools { /** * Get a CiviSEPA mandate for the given contribution ID * - * @param $contribution_id integer contribution ID *or* recurring contribution ID - * @return integer mandate ID or null + * @param int $contribution_id contribution ID *or* recurring contribution ID + * @return array|null mandate or null */ - public static function getMandateFor($contribution_id) { - $contribution_id = (int) $contribution_id; + public static function getMandateFor(int $contribution_id): ?array { if ($contribution_id) { try { // try recurring mandate diff --git a/CRM/Twingle/Upgrader.php b/CRM/Twingle/Upgrader.php index 8741dac..2af4667 100644 --- a/CRM/Twingle/Upgrader.php +++ b/CRM/Twingle/Upgrader.php @@ -25,7 +25,7 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base { /** * Installer script */ - public function install() { + public function install(): void { // create a DB table for the twingle profiles $this->executeSqlFile('sql/civicrm_twingle_profile.sql'); @@ -43,7 +43,7 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base { * /** * Copy financial_type_id setting to new setting financial_type_id_recur. */ - public function upgrade_4000() { + public function upgrade_4000(): bool { $this->ctx->log->info('Applying update 4000: Copying Financial type to new setting Financial type (recurring).'); foreach (CRM_Twingle_Profile::getProfiles() as $profile) { $profile->setAttribute('financial_type_id_recur', $profile->getAttribute('financial_type_id')); @@ -57,7 +57,7 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base { * * @link https://civicrm.org/advisory/civi-sa-2019-21-poi-saved-search-and-report-instance-apis */ - public function upgrade_5011() { + public function upgrade_5011(): bool { // Do not use CRM_Core_BAO::getItem() or Civi::settings()->get(). // Extract and unserialize directly from the database. $twingle_profiles_query = CRM_Core_DAO::executeQuery(" @@ -78,14 +78,14 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base { * @return TRUE on success * @throws Exception */ - public function upgrade_5140() { + public function upgrade_5140(): bool { $this->ctx->log->info('Converting twingle profiles.'); // create a DB table for the twingle profiles $this->executeSqlFile('sql/civicrm_twingle_profile.sql'); // migrate the current profiles - if ($profiles_data = Civi::settings()->get('twingle_profiles')) { + if (is_array($profiles_data = Civi::settings()->get('twingle_profiles'))) { foreach ($profiles_data as $profile_name => $profile_data) { $profile = new CRM_Twingle_Profile($profile_name, $profile_data); $data = json_encode($profile->getData()); diff --git a/Civi/Twingle/Exceptions/BaseException.php b/Civi/Twingle/Exceptions/BaseException.php index f1ce9e3..5aa721e 100644 --- a/Civi/Twingle/Exceptions/BaseException.php +++ b/Civi/Twingle/Exceptions/BaseException.php @@ -35,14 +35,16 @@ class BaseException extends \Exception { /** * BaseException Constructor - * @param string $message - * Error message - * @param string $error_code - * A meaningful error code + * @param string|null $message + * Error message + * @param string|null $error_code + * A meaningful error code + * @param \Throwable|null $previous + * A previously thrown exception to include. */ - public function __construct(string $message = '', string $error_code = '') { - parent::__construct($message, 1); - $this->log_message = !empty($message) ? E::LONG_NAME . ': ' . $message : ''; + public function __construct(?string $message = '', ?string $error_code = '', ?\Throwable $previous = NULL) { + parent::__construct($message, 1, $previous); + $this->log_message = '' !== $message ? E::LONG_NAME . ': ' . $message : ''; $this->code = $error_code; } @@ -59,7 +61,7 @@ class BaseException extends \Exception { * @return string */ public function getErrorCode() { - return $this->code; + return (string) $this->code; } } diff --git a/api/v3/TwingleDonation/Cancel.php b/api/v3/TwingleDonation/Cancel.php index 7e1eaa5..9061476 100644 --- a/api/v3/TwingleDonation/Cancel.php +++ b/api/v3/TwingleDonation/Cancel.php @@ -21,7 +21,7 @@ use CRM_Twingle_ExtensionUtil as E; * TwingleDonation.Cancel API specification (optional) * This is used for documentation and validation. * - * @param array $params description of fields supported by this API call + * @param array> $params description of fields supported by this API call * * @return void * @@ -61,8 +61,8 @@ function _civicrm_api3_twingle_donation_Cancel_spec(&$params) { /** * TwingleDonation.Cancel API * - * @param array $params - * @return array API result descriptor + * @param array $params + * @return array API result descriptor * @see civicrm_api3_create_success * @see civicrm_api3_create_error */ @@ -75,7 +75,7 @@ function civicrm_api3_twingle_donation_Cancel($params) { try { // Validate date for parameter "cancelled_at". if (!DateTime::createFromFormat('YmdHis', $params['cancelled_at'])) { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('Invalid date for parameter "cancelled_at".'), 'invalid_format' ); @@ -89,7 +89,7 @@ function civicrm_api3_twingle_donation_Cancel($params) { ]); $contribution_type = 'Contribution'; } - catch (CiviCRM_API3_Exception $exception) { + catch (CRM_Core_Exception $exception) { $contribution = civicrm_api3('ContributionRecur', 'getsingle', [ 'trxn_id' => $default_profile->getTransactionID($params['trx_id']), ]); @@ -101,9 +101,9 @@ function civicrm_api3_twingle_donation_Cancel($params) { && CRM_Twingle_Tools::isSDD($contribution['payment_instrument_id']) ) { // End SEPA mandate if applicable. - $mandate = CRM_Twingle_Tools::getMandateFor($contribution['id']); + $mandate = CRM_Twingle_Tools::getMandateFor((int) $contribution['id']); if (!$mandate) { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('SEPA Mandate for contribution [%1 not found.', [1 => $contribution['id']]), 'api_error' ); @@ -112,7 +112,7 @@ function civicrm_api3_twingle_donation_Cancel($params) { // Mandates can not be terminated in the past. $end_date = date_create_from_format('YmdHis', $params['cancelled_at']); - if ($end_date) { + if (FALSE !== $end_date) { // Mandates can not be terminated in the past: $end_date = date('Ymd', max( time(), @@ -128,7 +128,7 @@ function civicrm_api3_twingle_donation_Cancel($params) { $end_date, $params['cancel_reason'] )) { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('Could not terminate SEPA mandate'), 'api_error' ); diff --git a/api/v3/TwingleDonation/Endrecurring.php b/api/v3/TwingleDonation/Endrecurring.php index 5d7b445..68023f9 100644 --- a/api/v3/TwingleDonation/Endrecurring.php +++ b/api/v3/TwingleDonation/Endrecurring.php @@ -21,7 +21,7 @@ use CRM_Twingle_ExtensionUtil as E; * TwingleDonation.Endrecurring API specification (optional) * This is used for documentation and validation. * - * @param array $params description of fields supported by this API call + * @param array> $params description of fields supported by this API call * * @return void * @@ -54,8 +54,8 @@ function _civicrm_api3_twingle_donation_endrecurring_spec(&$params) { /** * TwingleDonation.Endrecurring API * - * @param array $params - * @return array API result descriptor + * @param array $params + * @return array API result descriptor * @see civicrm_api3_create_success * @see civicrm_api3_create_error */ @@ -67,8 +67,8 @@ function civicrm_api3_twingle_donation_endrecurring($params) { try { // Validate date for parameter "ended_at". - if (!DateTime::createFromFormat('YmdHis', $params['ended_at'])) { - throw new CiviCRM_API3_Exception( + if (FALSE === DateTime::createFromFormat('YmdHis', $params['ended_at'])) { + throw new CRM_Core_Exception( E::ts('Invalid date for parameter "ended_at".'), 'invalid_format' ); @@ -86,9 +86,9 @@ function civicrm_api3_twingle_donation_endrecurring($params) { && CRM_Twingle_Tools::isSDD($contribution['payment_instrument_id']) ) { // END SEPA MANDATE - $mandate = CRM_Twingle_Tools::getMandateFor($contribution['id']); - if (!$mandate) { - throw new CiviCRM_API3_Exception( + $mandate = CRM_Twingle_Tools::getMandateFor((int) $contribution['id']); + if (!isset($mandate)) { + throw new CRM_Core_Exception( E::ts('SEPA Mandate for recurring contribution [%1 not found.', [1 => $contribution['id']]), 'api_error' ); @@ -96,7 +96,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) { $mandate_id = $mandate['id']; $end_date = date_create_from_format('YmdHis', $params['ended_at']); - if ($end_date) { + if (FALSE !== $end_date) { // Mandates can not be terminated in the past: $end_date = date('Ymd', max( time(), @@ -109,7 +109,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) { // verify that the mandate has not been terminated in the past if ($mandate['status'] != 'FRST' && $mandate['status'] != 'RCUR') { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('SEPA Mandate [%1] already terminated.', [1 => $mandate_id]), 'api_error' ); @@ -120,7 +120,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) { $end_date, E::ts('Mandate closed by TwingleDonation.Endrecurring API call') )) { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('Could not terminate SEPA mandate'), 'api_error' ); diff --git a/api/v3/TwingleDonation/Submit.php b/api/v3/TwingleDonation/Submit.php index 8f63aea..ecaeac8 100644 --- a/api/v3/TwingleDonation/Submit.php +++ b/api/v3/TwingleDonation/Submit.php @@ -16,12 +16,13 @@ declare(strict_types = 1); use CRM_Twingle_ExtensionUtil as E; +use Civi\Twingle\Exceptions\BaseException; /** * TwingleDonation.Submit API specification * This is used for documentation and validation. * - * @param array $params + * @param array> $params * Description of fields supported by this API call. * * @return void @@ -259,8 +260,8 @@ function _civicrm_api3_twingle_donation_Submit_spec(&$params) { /** * TwingleDonation.Submit API * - * @param array $params - * @return array API result descriptor + * @param array $params + * @return array API result descriptor * @see civicrm_api3_create_success * @see civicrm_api3_create_error */ @@ -294,7 +295,7 @@ function civicrm_api3_twingle_donation_Submit($params) { 'trxn_id' => $profile->getTransactionID($params['trx_id']), ]); if ($existing_contribution['count'] > 0 || $existing_contribution_recur['count'] > 0) { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('Contribution with the given transaction ID already exists.'), 'api_error' ); @@ -303,7 +304,7 @@ function civicrm_api3_twingle_donation_Submit($params) { // Extract custom field values using the profile's mapping of Twingle fields // to CiviCRM custom fields. $custom_fields = []; - if (!empty($params['custom_fields'])) { + if (is_array($params['custom_fields'])) { $custom_field_mapping = $profile->getCustomFieldMapping(); // Make all params available for custom field mapping @@ -343,11 +344,9 @@ function civicrm_api3_twingle_donation_Submit($params) { 'user_city' => 'city', 'user_country' => 'country', ] as $address_param => $address_component) { - if (!empty($params[$address_param])) { + if (isset($params[$address_param]) && '' !== $params[$address_param]) { $params[$address_component] = $params[$address_param]; - if ($address_param != $address_component) { - unset($params[$address_param]); - } + unset($params[$address_param]); } } @@ -369,13 +368,13 @@ function civicrm_api3_twingle_donation_Submit($params) { } // Prepare parameter mapping for organisation. - if (!empty($params['user_company'])) { + if (is_string($params['user_company']) && '' !== $params['user_company']) { $params['organization_name'] = $params['user_company']; unset($params['user_company']); } // Remove parameter "id". - if (!empty($params['id'])) { + if (isset($params['id'])) { unset($params['id']); } @@ -419,67 +418,67 @@ function civicrm_api3_twingle_donation_Submit($params) { } // Get the prefix ID defined within the profile - if (!empty($params['user_gender'])) { - $prefix_id = (int) $profile->getAttribute('prefix_' . $params['user_gender']); - if ($prefix_id) { - $contact_data['prefix_id'] = $prefix_id; - } + if ( + isset($params['user_gender']) + && NULL !== ($prefix_id = $profile->getAttribute('prefix_' . $params['user_gender'])) + ) { + $contact_data['prefix_id'] = $prefix_id; } // Add custom field values. - if (!empty($custom_fields['Contact'])) { + if (isset($custom_fields['Contact'])) { $contact_data += $custom_fields['Contact']; } - if (!empty($custom_fields['Individual'])) { + if (isset($custom_fields['Individual'])) { $contact_data += $custom_fields['Individual']; } // Organisation lookup. - if (!empty($params['organization_name'])) { + if (is_string($params['organization_name']) && '' !== $params['organization_name']) { $organisation_data = [ 'organization_name' => $params['organization_name'], ]; // Add custom field values. - if (!empty($custom_fields['Organization'])) { + if (isset($custom_fields['Organization'])) { $organisation_data += $custom_fields['Organization']; } - if (!empty($submitted_address)) { + if ([] !== $submitted_address) { $organisation_data += $submitted_address; // Use configured location type for organisation address. $organisation_data['location_type_id'] = (int) $profile->getAttribute('location_type_id_organisation'); } - if (!$organisation_id = CRM_Twingle_Submission::getContact( + if (!is_int($organisation_id = CRM_Twingle_Submission::getContact( 'Organization', $organisation_data, $profile, $params - )) { - throw new CiviCRM_API3_Exception( + ))) { + throw new CRM_Core_Exception( E::ts('Organisation contact could not be found or created.'), 'api_error' ); } } - elseif (!empty($submitted_address)) { + elseif ([] !== $submitted_address) { $contact_data += $submitted_address; } - if (!$contact_id = CRM_Twingle_Submission::getContact( + if (!is_int($contact_id = CRM_Twingle_Submission::getContact( 'Individual', $contact_data, $profile, $params - )) { - throw new CiviCRM_API3_Exception( + ))) { + throw new CRM_Core_Exception( E::ts('Individual contact could not be found or created.'), 'api_error' ); } // Save user_extrafield as contact note. - if (!empty($params['user_extrafield'])) { + if (isset($params['user_extrafield']) && '' != $params['user_extrafield']) { civicrm_api3('Note', 'create', [ 'entity_table' => 'civicrm_contact', 'entity_id' => $contact_id, @@ -512,13 +511,13 @@ function civicrm_api3_twingle_donation_Submit($params) { // If usage of double opt-in is selected, use MailingEventSubscribe.create // to add contact to newsletter groups defined in the profile $result_values['newsletter']['newsletter_double_opt_in'] - = ($profile->getAttribute('newsletter_double_opt_in')) + = (bool) $profile->getAttribute('newsletter_double_opt_in') ? 'true' : 'false'; if ( - $profile->getAttribute('newsletter_double_opt_in') && - !empty($params['newsletter']) && - !empty($groups = $profile->getAttribute('newsletter_groups')) + (bool) $profile->getAttribute('newsletter_double_opt_in') + && isset($params['newsletter']) + && is_array($groups = $profile->getAttribute('newsletter_groups')) ) { $group_memberships = array_column( civicrm_api3( @@ -539,7 +538,7 @@ function civicrm_api3_twingle_donation_Submit($params) { 'id' => (int) $group_id, ] )['visibility'] == 'Public Pages'; - if (!in_array($group_id, $group_memberships) && $is_public_group) { + if (!in_array($group_id, $group_memberships, FALSE) && $is_public_group) { $result_values['newsletter'][][$group_id] = civicrm_api3( 'MailingEventSubscribe', 'create', @@ -557,8 +556,8 @@ function civicrm_api3_twingle_donation_Submit($params) { // If requested, add contact to newsletter groups defined in the profile. } elseif ( - !empty($params['newsletter']) - && !empty($groups = $profile->getAttribute('newsletter_groups')) + isset($params['newsletter']) + && is_array($groups = $profile->getAttribute('newsletter_groups')) ) { foreach ($groups as $group_id) { civicrm_api3( @@ -575,7 +574,10 @@ function civicrm_api3_twingle_donation_Submit($params) { } // If requested, add contact to postinfo groups defined in the profile. - if (!empty($params['postinfo']) && !empty($groups = $profile->getAttribute('postinfo_groups'))) { + if ( + isset($params['postinfo']) + && is_array($groups = $profile->getAttribute('postinfo_groups')) + ) { foreach ($groups as $group_id) { civicrm_api3('GroupContact', 'create', [ 'group_id' => $group_id, @@ -588,7 +590,10 @@ function civicrm_api3_twingle_donation_Submit($params) { // If requested, add contact to donation_receipt groups defined in the // profile. - if (!empty($params['donation_receipt']) && !empty($groups = $profile->getAttribute('donation_receipt_groups'))) { + if ( + isset($params['donation_receipt']) + && is_array($groups = $profile->getAttribute('donation_receipt_groups')) + ) { foreach ($groups as $group_id) { civicrm_api3('GroupContact', 'create', [ 'group_id' => $group_id, @@ -611,18 +616,18 @@ function civicrm_api3_twingle_donation_Submit($params) { ]; // Add custom field values. - if (!empty($custom_fields['Contribution'])) { + if (isset($custom_fields['Contribution'])) { $contribution_data += $custom_fields['Contribution']; } - if (!empty($params['purpose'])) { + if (isset($params['purpose'])) { $contribution_data['note'] = $params['purpose']; } // set campaign, subject to configuration CRM_Twingle_Submission::setCampaign($contribution_data, 'contribution', $params, $profile); - if (!empty($contribution_source = $profile->getAttribute('contribution_source'))) { + if (NULL !== ($contribution_source = $profile->getAttribute('contribution_source'))) { $contribution_data['source'] = $contribution_source; } @@ -637,8 +642,8 @@ function civicrm_api3_twingle_donation_Submit($params) { 'debit_iban', 'debit_bic', ] as $sepa_attribute) { - if (empty($params[$sepa_attribute])) { - throw new CiviCRM_API3_Exception( + if (!isset($params[$sepa_attribute])) { + throw new CRM_Core_Exception( E::ts('Missing attribute %1 for SEPA mandate', [ 1 => $sepa_attribute, ]), @@ -648,6 +653,11 @@ function civicrm_api3_twingle_donation_Submit($params) { } $creditor_id = $profile->getAttribute('sepa_creditor_id'); + if (!is_int($creditor_id)) { + throw new BaseException( + E::ts('SEPA creditor is not configured for profile "%1".', [1 => $profile->getName()]) + ); + } // Compose mandate data from contribution data, ... $mandate_data = @@ -669,10 +679,10 @@ function civicrm_api3_twingle_donation_Submit($params) { // phpcs:ignore Drupal.Formatting.SpaceUnaryOperator.PlusMinus + CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']); // Add custom field values. - if (!empty($custom_fields['ContributionRecur'])) { + if (isset($custom_fields['ContributionRecur'])) { $mandate_data += $custom_fields['ContributionRecur']; } - if (!empty($mandate_source = $profile->getAttribute('contribution_source'))) { + if (NULL !== ($mandate_source = $profile->getAttribute('contribution_source'))) { $mandate_data['source'] = $mandate_source; } @@ -691,7 +701,7 @@ function civicrm_api3_twingle_donation_Submit($params) { // If requested, let CiviSEPA generate the mandate reference $use_own_mandate_reference = Civi::settings()->get('twingle_dont_use_reference'); - if (!empty($use_own_mandate_reference)) { + if ((bool) $use_own_mandate_reference) { unset($mandate_data['reference']); } @@ -730,7 +740,7 @@ function civicrm_api3_twingle_donation_Submit($params) { + CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']); // Add custom field values. - if (!empty($custom_fields['ContributionRecur'])) { + if (isset($custom_fields['ContributionRecur'])) { $contribution_recur_data += $custom_fields['ContributionRecur']; $contribution_data += $custom_fields['ContributionRecur']; } @@ -740,7 +750,7 @@ function civicrm_api3_twingle_donation_Submit($params) { $contribution_recur = civicrm_api3('ContributionRecur', 'create', $contribution_recur_data); if ($contribution_recur['is_error']) { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('Could not create recurring contribution.'), 'api_error' ); @@ -766,7 +776,7 @@ function civicrm_api3_twingle_donation_Submit($params) { ]); $contribution_data['contribution_recur_id'] = $parent_contribution['id']; } - catch (Exception $exception) { + catch (CRM_Core_Exception $exception) { $result_values['parent_contribution'] = E::ts( 'Could not find recurring contribution with given parent transaction ID.' ); @@ -775,7 +785,7 @@ function civicrm_api3_twingle_donation_Submit($params) { $contribution = civicrm_api3('Contribution', 'create', $contribution_data); if ($contribution['is_error']) { - throw new CiviCRM_API3_Exception( + throw new CRM_Core_Exception( E::ts('Could not create contribution'), 'api_error' ); @@ -799,12 +809,12 @@ function civicrm_api3_twingle_donation_Submit($params) { } else { // this is a follow-up recurring payment - $membership_type_id = FALSE; + $membership_type_id = NULL; } } // CREATE the membership if required - if (!empty($membership_type_id)) { + if (isset($membership_type_id)) { $membership_data = [ 'contact_id' => $contact_id, 'membership_type_id' => $membership_type_id, @@ -821,7 +831,7 @@ function civicrm_api3_twingle_donation_Submit($params) { // call the postprocess API $postprocess_call = $profile->getAttribute('membership_postprocess_call'); - if (!empty($postprocess_call)) { + if (is_string($postprocess_call)) { [$pp_entity, $pp_action] = explode('.', $postprocess_call, 2); try { // gather the contribution IDs @@ -829,7 +839,7 @@ function civicrm_api3_twingle_donation_Submit($params) { if (isset($contribution_recur['id'])) { $recurring_contribution_id = $contribution_recur['id']; } - elseif (!empty($result_values['sepa_mandate'])) { + elseif (isset($result_values['sepa_mandate'])) { $mandate = reset($result_values['sepa_mandate']); if ($mandate['entity_table'] == 'civicrm_contribution_recur') { $recurring_contribution_id = (int) $mandate['entity_id']; @@ -851,14 +861,20 @@ function civicrm_api3_twingle_donation_Submit($params) { // refresh membership data $result_values['membership'] = civicrm_api3('Membership', 'getsingle', ['id' => $membership['id']]); } - catch (CiviCRM_API3_Exception $ex) { + catch (CRM_Core_Exception $exception) { // TODO: more error handling? - Civi::log() - ->warning( - "Twingle membership postprocessing call {$pp_entity}.{$pp_action} has failed: " . $ex->getMessage() - ); - throw new Exception( - E::ts('Twingle membership postprocessing call has failed, see log for more information') + Civi::log()->warning( + sprintf( + 'Twingle membership postprocessing call %s.%s has failed: %s', + $pp_entity, + $pp_action, + $exception->getMessage() + ) + ); + throw new BaseException( + E::ts('Twingle membership postprocessing call has failed, see log for more information'), + NULL, + $exception ); } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 07d420f..0e10e72 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -22,6 +22,7 @@ + diff --git a/phpstan.neon.template b/phpstan.neon.template index 9f2f699..a65c395 100644 --- a/phpstan.neon.template +++ b/phpstan.neon.template @@ -9,5 +9,6 @@ parameters: - {VENDOR_DIR}/civicrm/civicrm-core/Civi/ - {VENDOR_DIR}/civicrm/civicrm-core/CRM/ - {VENDOR_DIR}/civicrm/civicrm-core/api/ + - {VENDOR_DIR}/civicrm/civicrm-packages/ bootstrapFiles: - {VENDOR_DIR}/autoload.php diff --git a/twingle.php b/twingle.php index f22eafc..8a838ea 100644 --- a/twingle.php +++ b/twingle.php @@ -8,7 +8,7 @@ use CRM_Twingle_ExtensionUtil as E; */ function twingle_civicrm_pre($op, $objectName, $id, &$params) { if ($objectName == 'ContributionRecur' && $op == 'edit') { - CRM_Twingle_Tools::checkRecurringContributionChange($id, $params); + CRM_Twingle_Tools::checkRecurringContributionChange((int) $id, $params); } }