From 0d375e210700436e24222fefaa1e0b33a98399ec Mon Sep 17 00:00:00 2001
From: "b.endres"
Date: Fri, 5 Sep 2025 15:48:00 +0200
Subject: [PATCH 01/12] added extension template
---
.editorconfig | 240 +++++++++++
.github/workflows/phpcs.yml | 43 ++
.github/workflows/phpstan.yml | 53 +++
.github/workflows/phpunit.yml | 38 ++
.gitignore | 10 +
ComposerHelper.php | 29 ++
ci/composer.json | 15 +
docs/index.md | 1 +
mkdocs.yml | 27 ++
phpcs.xml.dist | 83 ++++
phpstan.ci.neon | 13 +
phpstan.neon.dist | 42 ++
phpstan.neon.template | 11 +
phpstanBootstrap.php | 47 +++
phpunit.xml.dist | 38 ++
tests/docker-compose.yml | 35 ++
tests/docker-phpunit.sh | 29 ++
tests/docker-prepare.sh | 59 +++
tests/ignored-deprecations.json | 1 +
tests/phpunit/bootstrap.php | 122 ++++++
tools/create-release.sh | 399 ++++++++++++++++++
tools/git/hooks-wrapper.sh | 27 ++
.../hooks/pre-commit.d/remember-update-pot.sh | 4 +
.../pre-merge-commit.d/run-pre-commit-hook.sh | 6 +
tools/git/init-hooks.sh | 22 +
tools/phpcs/composer.json | 11 +
tools/phpstan/composer.json | 19 +
tools/phpunit/composer.json | 13 +
tools/update-pot.sh | 35 ++
29 files changed, 1472 insertions(+)
create mode 100644 .editorconfig
create mode 100644 .github/workflows/phpcs.yml
create mode 100644 .github/workflows/phpstan.yml
create mode 100644 .github/workflows/phpunit.yml
create mode 100644 .gitignore
create mode 100644 ComposerHelper.php
create mode 100644 ci/composer.json
create mode 100644 docs/index.md
create mode 100644 mkdocs.yml
create mode 100644 phpcs.xml.dist
create mode 100644 phpstan.ci.neon
create mode 100644 phpstan.neon.dist
create mode 100644 phpstan.neon.template
create mode 100644 phpstanBootstrap.php
create mode 100644 phpunit.xml.dist
create mode 100644 tests/docker-compose.yml
create mode 100755 tests/docker-phpunit.sh
create mode 100755 tests/docker-prepare.sh
create mode 100644 tests/ignored-deprecations.json
create mode 100644 tests/phpunit/bootstrap.php
create mode 100755 tools/create-release.sh
create mode 100755 tools/git/hooks-wrapper.sh
create mode 100755 tools/git/hooks/pre-commit.d/remember-update-pot.sh
create mode 100755 tools/git/hooks/pre-merge-commit.d/run-pre-commit-hook.sh
create mode 100755 tools/git/init-hooks.sh
create mode 100644 tools/phpcs/composer.json
create mode 100644 tools/phpstan/composer.json
create mode 100644 tools/phpunit/composer.json
create mode 100755 tools/update-pot.sh
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..f5f7003
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,240 @@
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+max_line_length = 120
+tab_width = 2
+ij_continuation_indent_size = 2
+ij_visual_guides = 80,120
+
+[{*.php}]
+ij_php_align_assignments = false
+ij_php_align_class_constants = false
+ij_php_align_enum_cases = false
+ij_php_align_group_field_declarations = false
+ij_php_align_inline_comments = false
+ij_php_align_key_value_pairs = false
+ij_php_align_match_arm_bodies = false
+ij_php_align_multiline_array_initializer_expression = false
+ij_php_align_multiline_binary_operation = false
+ij_php_align_multiline_chained_methods = false
+ij_php_align_multiline_extends_list = false
+ij_php_align_multiline_for = true
+ij_php_align_multiline_parameters = false
+ij_php_align_multiline_parameters_in_calls = false
+ij_php_align_multiline_ternary_operation = false
+ij_php_align_named_arguments = false
+ij_php_align_phpdoc_comments = false
+ij_php_align_phpdoc_param_names = false
+ij_php_anonymous_brace_style = end_of_line
+ij_php_api_weight = 28
+ij_php_array_initializer_new_line_after_left_brace = true
+ij_php_array_initializer_right_brace_on_new_line = true
+ij_php_array_initializer_wrap = on_every_item
+ij_php_assignment_wrap = normal
+ij_php_attributes_wrap = normal
+ij_php_author_weight = 28
+ij_php_binary_operation_sign_on_next_line = false
+ij_php_binary_operation_wrap = normal
+ij_php_blank_lines_after_class_header = 1
+ij_php_blank_lines_after_function = 1
+ij_php_blank_lines_after_imports = 1
+ij_php_blank_lines_after_opening_tag = 0
+ij_php_blank_lines_after_package = 1
+ij_php_blank_lines_around_class = 1
+ij_php_blank_lines_around_constants = 1
+ij_php_blank_lines_around_enum_cases = 0
+ij_php_blank_lines_around_field = 1
+ij_php_blank_lines_around_method = 1
+ij_php_blank_lines_before_class_end = 1
+ij_php_blank_lines_before_imports = 1
+ij_php_blank_lines_before_method_body = 0
+ij_php_blank_lines_before_package = 1
+ij_php_blank_lines_before_return_statement = 1
+ij_php_blank_lines_between_imports = 0
+ij_php_block_brace_style = end_of_line
+ij_php_call_parameters_new_line_after_left_paren = true
+ij_php_call_parameters_right_paren_on_new_line = true
+ij_php_call_parameters_wrap = on_every_item
+ij_php_catch_on_new_line = true
+ij_php_category_weight = 28
+ij_php_class_brace_style = end_of_line
+ij_php_comma_after_last_argument = false
+ij_php_comma_after_last_array_element = true
+ij_php_comma_after_last_closure_use_var = false
+ij_php_comma_after_last_match_arm = false
+ij_php_comma_after_last_parameter = false
+ij_php_concat_spaces = true
+ij_php_copyright_weight = 28
+ij_php_deprecated_weight = 4
+ij_php_do_while_brace_force = always
+ij_php_else_if_style = as_is
+ij_php_else_on_new_line = true
+ij_php_example_weight = 28
+ij_php_extends_keyword_wrap = off
+ij_php_extends_list_wrap = off
+ij_php_fields_default_visibility = private
+ij_php_filesource_weight = 28
+ij_php_finally_on_new_line = true
+ij_php_for_brace_force = always
+ij_php_for_statement_new_line_after_left_paren = false
+ij_php_for_statement_right_paren_on_new_line = false
+ij_php_for_statement_wrap = off
+ij_php_force_empty_methods_in_one_line = false
+ij_php_force_short_declaration_array_style = true
+ij_php_getters_setters_naming_style = camel_case
+ij_php_getters_setters_order_style = getters_first
+ij_php_global_weight = 28
+ij_php_group_use_wrap = on_every_item
+ij_php_if_brace_force = always
+ij_php_if_lparen_on_next_line = false
+ij_php_if_rparen_on_next_line = false
+ij_php_ignore_weight = 28
+ij_php_import_sorting = alphabetic
+ij_php_indent_break_from_case = true
+ij_php_indent_case_from_switch = true
+ij_php_indent_code_in_php_tags = false
+ij_php_internal_weight = 28
+ij_php_keep_blank_lines_after_lbrace = 1
+ij_php_keep_blank_lines_before_right_brace = 1
+ij_php_keep_blank_lines_in_code = 1
+ij_php_keep_blank_lines_in_declarations = 1
+ij_php_keep_control_statement_in_one_line = false
+ij_php_keep_first_column_comment = false
+ij_php_keep_indents_on_empty_lines = false
+ij_php_keep_line_breaks = false
+ij_php_keep_rparen_and_lbrace_on_one_line = true
+ij_php_keep_simple_classes_in_one_line = false
+ij_php_keep_simple_methods_in_one_line = false
+ij_php_lambda_brace_style = end_of_line
+ij_php_license_weight = 28
+ij_php_line_comment_add_space = false
+ij_php_line_comment_at_first_column = true
+ij_php_link_weight = 28
+ij_php_lower_case_boolean_const = false
+ij_php_lower_case_keywords = true
+ij_php_lower_case_null_const = false
+ij_php_method_brace_style = end_of_line
+ij_php_method_call_chain_wrap = on_every_item
+ij_php_method_parameters_new_line_after_left_paren = true
+ij_php_method_parameters_right_paren_on_new_line = true
+ij_php_method_parameters_wrap = on_every_item
+ij_php_method_weight = 28
+ij_php_modifier_list_wrap = false
+ij_php_multiline_chained_calls_semicolon_on_new_line = true
+ij_php_namespace_brace_style = 1
+ij_php_new_line_after_php_opening_tag = true
+ij_php_null_type_position = in_the_end
+ij_php_package_weight = 28
+ij_php_param_weight = 1
+ij_php_parameters_attributes_wrap = normal
+ij_php_parentheses_expression_new_line_after_left_paren = false
+ij_php_parentheses_expression_right_paren_on_new_line = false
+ij_php_phpdoc_blank_line_before_tags = true
+ij_php_phpdoc_blank_lines_around_parameters = true
+ij_php_phpdoc_keep_blank_lines = true
+ij_php_phpdoc_param_spaces_between_name_and_description = 1
+ij_php_phpdoc_param_spaces_between_tag_and_type = 1
+ij_php_phpdoc_param_spaces_between_type_and_name = 1
+ij_php_phpdoc_use_fqcn = true
+ij_php_phpdoc_wrap_long_lines = true
+ij_php_place_assignment_sign_on_next_line = false
+ij_php_place_parens_for_constructor = 1
+ij_php_property_read_weight = 28
+ij_php_property_weight = 28
+ij_php_property_write_weight = 28
+ij_php_return_type_on_new_line = false
+ij_php_return_weight = 2
+ij_php_see_weight = 5
+ij_php_since_weight = 28
+ij_php_sort_phpdoc_elements = true
+ij_php_space_after_colon = true
+ij_php_space_after_colon_in_enum_backed_type = true
+ij_php_space_after_colon_in_named_argument = true
+ij_php_space_after_colon_in_return_type = true
+ij_php_space_after_comma = true
+ij_php_space_after_for_semicolon = true
+ij_php_space_after_quest = true
+ij_php_space_after_type_cast = true
+ij_php_space_after_unary_not = false
+ij_php_space_before_array_initializer_left_brace = false
+ij_php_space_before_catch_keyword = true
+ij_php_space_before_catch_left_brace = true
+ij_php_space_before_catch_parentheses = true
+ij_php_space_before_class_left_brace = true
+ij_php_space_before_closure_left_parenthesis = true
+ij_php_space_before_colon = true
+ij_php_space_before_colon_in_enum_backed_type = false
+ij_php_space_before_colon_in_named_argument = false
+ij_php_space_before_colon_in_return_type = false
+ij_php_space_before_comma = false
+ij_php_space_before_do_left_brace = true
+ij_php_space_before_else_keyword = true
+ij_php_space_before_else_left_brace = true
+ij_php_space_before_finally_keyword = true
+ij_php_space_before_finally_left_brace = true
+ij_php_space_before_for_left_brace = true
+ij_php_space_before_for_parentheses = true
+ij_php_space_before_for_semicolon = false
+ij_php_space_before_if_left_brace = true
+ij_php_space_before_if_parentheses = true
+ij_php_space_before_method_call_parentheses = false
+ij_php_space_before_method_left_brace = true
+ij_php_space_before_method_parentheses = false
+ij_php_space_before_quest = true
+ij_php_space_before_short_closure_left_parenthesis = false
+ij_php_space_before_switch_left_brace = true
+ij_php_space_before_switch_parentheses = true
+ij_php_space_before_try_left_brace = true
+ij_php_space_before_unary_not = false
+ij_php_space_before_while_keyword = true
+ij_php_space_before_while_left_brace = true
+ij_php_space_before_while_parentheses = true
+ij_php_space_between_ternary_quest_and_colon = false
+ij_php_spaces_around_additive_operators = true
+ij_php_spaces_around_arrow = false
+ij_php_spaces_around_assignment_in_declare = true
+ij_php_spaces_around_assignment_operators = true
+ij_php_spaces_around_bitwise_operators = true
+ij_php_spaces_around_equality_operators = true
+ij_php_spaces_around_logical_operators = true
+ij_php_spaces_around_multiplicative_operators = true
+ij_php_spaces_around_null_coalesce_operator = true
+ij_php_spaces_around_pipe_in_union_type = false
+ij_php_spaces_around_relational_operators = true
+ij_php_spaces_around_shift_operators = true
+ij_php_spaces_around_unary_operator = false
+ij_php_spaces_around_var_within_brackets = false
+ij_php_spaces_within_array_initializer_braces = false
+ij_php_spaces_within_brackets = false
+ij_php_spaces_within_catch_parentheses = false
+ij_php_spaces_within_for_parentheses = false
+ij_php_spaces_within_if_parentheses = false
+ij_php_spaces_within_method_call_parentheses = false
+ij_php_spaces_within_method_parentheses = false
+ij_php_spaces_within_parentheses = false
+ij_php_spaces_within_short_echo_tags = true
+ij_php_spaces_within_switch_parentheses = false
+ij_php_spaces_within_while_parentheses = false
+ij_php_special_else_if_treatment = false
+ij_php_subpackage_weight = 28
+ij_php_ternary_operation_signs_on_next_line = true
+ij_php_ternary_operation_wrap = on_every_item
+ij_php_throws_weight = 3
+ij_php_todo_weight = 6
+ij_php_treat_multiline_arrays_and_lambdas_multiline = false
+ij_php_unknown_tag_weight = 28
+ij_php_upper_case_boolean_const = true
+ij_php_upper_case_null_const = true
+ij_php_uses_weight = 28
+ij_php_var_weight = 0
+ij_php_variable_naming_style = camel_case
+ij_php_version_weight = 28
+ij_php_while_brace_force = always
+ij_php_while_on_new_line = false
+
+[{*.neon,*.neon.dist,*neon.template}]
+indent_style = tab
+tab_width = 4
diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml
new file mode 100644
index 0000000..a0290ee
--- /dev/null
+++ b/.github/workflows/phpcs.yml
@@ -0,0 +1,43 @@
+name: PHP_CodeSniffer
+
+on:
+ pull_request:
+ paths:
+ - '**.php'
+ - tools/phpcs/composer.json
+ - phpcs.xml.dist
+ - .github/workflows/phpcs.yml
+
+jobs:
+ phpcs:
+ runs-on: ubuntu-latest
+ name: PHP_CodeSniffer
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.3
+ coverage: none
+ tools: cs2pr
+ env:
+ fail-fast: true
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+
+ - name: Cache dependencies
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('tools/phpcs/composer.json') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer composer-phpcs -- update --no-progress --prefer-dist
+
+ - name: Run PHP_CodeSniffer
+ run: composer phpcs -- -q --report=checkstyle | cs2pr
diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml
new file mode 100644
index 0000000..efc0b1d
--- /dev/null
+++ b/.github/workflows/phpstan.yml
@@ -0,0 +1,53 @@
+name: PHPStan
+
+on:
+ pull_request:
+ paths:
+ - '**.php'
+ - composer.json
+ - tools/phpstan/composer.json
+ - ci/composer.json
+ - phpstan.ci.neon
+ - phpstan.neon.dist
+ - .github/workflows/phpstan.yml
+
+jobs:
+ phpstan:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php-versions: ['8.1', '8.4']
+ prefer: ['prefer-stable', 'prefer-lowest']
+ name: PHPStan with PHP ${{ matrix.php-versions }} ${{ matrix.prefer }}
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+ coverage: none
+ env:
+ fail-fast: true
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+
+ - name: Cache dependencies
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ matrix.prefer }}-${{ hashFiles('**/composer.json') }}
+ restore-keys: ${{ runner.os }}-composer-${{ matrix.prefer }}-
+
+ - name: Install dependencies
+ run: |
+ composer update --no-progress --prefer-dist --${{ matrix.prefer }} --optimize-autoloader &&
+ composer composer-phpunit -- update --no-progress --prefer-dist &&
+ composer composer-phpstan -- update --no-progress --prefer-dist --optimize-autoloader &&
+ composer --working-dir=ci update --no-progress --prefer-dist --${{ matrix.prefer }} --ignore-platform-req=ext-gd
+
+ - name: Run PHPStan
+ run: composer phpstan -- analyse -c phpstan.ci.neon
diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
new file mode 100644
index 0000000..46a30af
--- /dev/null
+++ b/.github/workflows/phpunit.yml
@@ -0,0 +1,38 @@
+name: PHPUnit
+
+on:
+ pull_request:
+ paths:
+ - '**.php'
+ - composer.json
+ - tools/phpunit/composer.json
+ - phpunit.xml.dist
+ - tests/docker-prepare.sh
+ - .github/workflows/phpunit.yml
+
+env:
+ # On github CI machine creating the "/vendor" volume fails otherwise with: read-only file system: unknown
+ BIND_VOLUME_PERMISSIONS: rw
+
+jobs:
+ phpunit:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ civicrm-image-tags: [ 'drupal', '5.45-drupal-php8.1' ]
+ name: PHPUnit with Docker image michaelmcandrew/civicrm:${{ matrix.civicrm-image-tags }}
+ env:
+ CIVICRM_IMAGE_TAG: ${{ matrix.civicrm-image-tags }}
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Pull images
+ run: docker compose -f tests/docker-compose.yml pull --quiet
+ - name: Start containers
+ run: docker compose -f tests/docker-compose.yml up -d
+ - name: Prepare environment
+ run: docker compose -f tests/docker-compose.yml exec civicrm sites/default/files/civicrm/ext/org.project60.bic/tests/docker-prepare.sh
+ - name: Run PHPUnit
+ run: docker compose -f tests/docker-compose.yml exec civicrm sites/default/files/civicrm/ext/org.project60.bic/tests/docker-phpunit.sh
+ - name: Remove containers
+ run: docker compose -f tests/docker-compose.yml down -v
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..62dffec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+/.phpcs.cache
+/.phpunit.result.cache
+/.phpstan/
+/ci/composer.lock
+/ci/vendor/
+/composer.lock
+/phpstan.neon
+/tools/*/vendor/
+/tools/*/composer.lock
+/vendor/
diff --git a/ComposerHelper.php b/ComposerHelper.php
new file mode 100644
index 0000000..ac7d95d
--- /dev/null
+++ b/ComposerHelper.php
@@ -0,0 +1,29 @@
+getComposer()->getRepositoryManager();
+ $package = $event->getComposer()->getPackage();
+ $package->setRequires(
+ array_filter(
+ $package->getRequires(),
+ fn (Link $require, string $name) =>
+ 'civicrm/civicrm-core' !== $name &&
+ 'civicrm-ext' !== $repositoryManager->findPackage($name, $require->getConstraint())?->getType(),
+ ARRAY_FILTER_USE_BOTH
+ )
+ );
+ }
+
+}
diff --git a/ci/composer.json b/ci/composer.json
new file mode 100644
index 0000000..21e1c53
--- /dev/null
+++ b/ci/composer.json
@@ -0,0 +1,15 @@
+{
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "allow-plugins": {
+ "civicrm/composer-compile-plugin": false,
+ "civicrm/composer-downloads-plugin": true,
+ "cweagans/composer-patches": true
+ },
+ "sort-packages": true
+ },
+ "require": {
+ "civicrm/civicrm-core": ">=5.45"
+ }
+}
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..e10b99d
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1 @@
+# Introduction
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..70803d3
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,27 @@
+site_name: Little BIC extension
+site_description: Generates and maintains a list of banks
+site_author: B. Endres
+site_url: https://github.com/Project60/org.project60.bic
+
+theme:
+ name: material
+
+nav:
+ - Introduction: index.md
+
+markdown_extensions:
+ - attr_list
+ - admonition
+ - def_list
+ - codehilite
+ - toc:
+ permalink: True
+ - pymdownx.superfences
+ - pymdownx.inlinehilite
+ - pymdownx.tilde
+ - pymdownx.betterem
+ - pymdownx.mark
+
+plugins:
+ - search:
+ lang: en
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 0000000..dc7d9cf
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,83 @@
+
+
+ CiviCRM coding standard with some additional changes
+
+ api
+ Civi
+ CRM
+ tests
+ bic.php
+
+ /CRM/Bic/DAO/.*\.php$
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpstan.ci.neon b/phpstan.ci.neon
new file mode 100644
index 0000000..883179f
--- /dev/null
+++ b/phpstan.ci.neon
@@ -0,0 +1,13 @@
+includes:
+ - phpstan.neon.dist
+
+parameters:
+ scanDirectories:
+ - ci/vendor/civicrm/civicrm-core/CRM/
+ bootstrapFiles:
+ - ci/vendor/autoload.php
+ # Because we test with different versions in CI we have unmatched errors
+ reportUnmatchedIgnoredErrors: false
+ ignoreErrors:
+ # Errors we get when using "prefer-lowest"
+ - '#::getSubscribedEvents\(\) return type has no value type specified in iterable type array.$#'
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 0000000..8d641f6
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,42 @@
+parameters:
+ paths:
+ - api
+ - Civi
+ - CRM
+ - tests
+ - bic.php
+ excludePaths:
+ analyse:
+ - CRM/Bic/DAO/*
+ - tests/phpunit/bootstrap.php
+ scanFiles:
+ - bic.civix.php
+ - tools/phpunit/vendor/bin/.phpunit/phpunit/src/Framework/TestCase.php
+ scanDirectories:
+ - tools/phpunit/vendor/bin/.phpunit/phpunit/src/Framework
+ bootstrapFiles:
+ - tools/phpunit/vendor/bin/.phpunit/phpunit/vendor/autoload.php
+ - phpstanBootstrap.php
+ level: 9
+ universalObjectCratesClasses:
+ - Civi\Core\Event\GenericHookEvent
+ - CRM_Core_Config
+ - CRM_Core_DAO
+ earlyTerminatingMethodCalls:
+ CRM_Queue_Runner:
+ - runAllViaWeb
+ checkTooWideReturnTypesInProtectedAndPublicMethods: true
+ checkUninitializedProperties: true
+ checkMissingCallableSignature: true
+ treatPhpDocTypesAsCertain: false
+ exceptions:
+ check:
+ missingCheckedExceptionInThrows: true
+ tooWideThrowType: true
+ checkedExceptionClasses:
+ - \Webmozart\Assert\InvalidArgumentException
+ implicitThrows: false
+ ignoreErrors:
+ # Note paths are prefixed with "*/" to work with inspections in PHPStorm because of:
+ # https://youtrack.jetbrains.com/issue/WI-63891/PHPStan-ignoreErrors-configuration-isnt-working-with-inspections
+ tmpDir: .phpstan
diff --git a/phpstan.neon.template b/phpstan.neon.template
new file mode 100644
index 0000000..34f6dfd
--- /dev/null
+++ b/phpstan.neon.template
@@ -0,0 +1,11 @@
+# Copy this file to phpstan.neon and replace {VENDOR_DIR} with the appropriate
+# path.
+
+includes:
+ - phpstan.neon.dist
+
+parameters:
+ scanDirectories:
+ - {VENDOR_DIR}/civicrm/civicrm-core/CRM/
+ bootstrapFiles:
+ - {VENDOR_DIR}/autoload.php
diff --git a/phpstanBootstrap.php b/phpstanBootstrap.php
new file mode 100644
index 0000000..67ad97d
--- /dev/null
+++ b/phpstanBootstrap.php
@@ -0,0 +1,47 @@
+.
+ */
+
+declare(strict_types = 1);
+
+// phpcs:disable Drupal.Commenting.DocComment.ContentAfterOpen
+/** @var \PHPStan\DependencyInjection\Container $container */
+/** @phpstan-var array $bootstrapFiles */
+$bootstrapFiles = $container->getParameter('bootstrapFiles');
+foreach ($bootstrapFiles as $bootstrapFile) {
+ if (str_ends_with($bootstrapFile, 'vendor/autoload.php')) {
+ $vendorDir = dirname($bootstrapFile);
+ $civiCrmVendorDir = $vendorDir . '/civicrm';
+ $civiCrmCoreDir = $civiCrmVendorDir . '/civicrm-core';
+ if (file_exists($civiCrmCoreDir)) {
+ set_include_path(get_include_path()
+ . PATH_SEPARATOR . $civiCrmCoreDir
+ . PATH_SEPARATOR . $civiCrmVendorDir . '/civicrm-packages'
+ );
+ // $bootstrapFile might not be included, yet. It is required for the
+ // following require_once, though.
+ require_once $bootstrapFile;
+ // Prevent error "Class 'CRM_Core_Exception' not found in file".
+ require_once $civiCrmCoreDir . '/CRM/Core/Exception.php';
+
+ break;
+ }
+ }
+}
+
+if (file_exists(__DIR__ . '/vendor/autoload.php')) {
+ require_once __DIR__ . '/vendor/autoload.php';
+}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..4877d3a
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+ ./tests/phpunit
+
+
+
+
+
+ api
+ CRM
+ Civi
+
+
+ CRM/Bic/DAO
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml
new file mode 100644
index 0000000..30bc030
--- /dev/null
+++ b/tests/docker-compose.yml
@@ -0,0 +1,35 @@
+services:
+ civicrm:
+ image: michaelmcandrew/civicrm:${CIVICRM_IMAGE_TAG:-drupal}
+ environment:
+ - PROJECT_NAME=test
+ - BASE_URL=http://localhost
+ - CIVICRM_DB_NAME=test
+ - CIVICRM_DB_USER=root
+ - CIVICRM_DB_PASS=secret
+ - CIVICRM_DB_HOST=mysql
+ - CIVICRM_DB_PORT=3306
+ - CIVICRM_CRED_KEYS=aes-cbc::test
+ - CIVICRM_SIGN_KEYS=jwt-hs256::test
+ - CIVICRM_SITE_KEY=TEST_KEY
+ - DRUPAL_DB_NAME=test
+ - DRUPAL_DB_USER=root
+ - DRUPAL_DB_PASS=secret
+ - DRUPAL_DB_HOST=mysql
+ - DRUPAL_DB_PORT=3306
+ - PHP_DATE_TIMEZONE=UTC
+ - DEBUG=ON
+ - SMTP_HOST=localhost
+ - SMTP_MAILDOMAIN=example.org
+ volumes:
+ - ../:/var/www/html/sites/default/files/civicrm/ext/org.project60.bic:${BIND_VOLUME_PERMISSIONS:-ro}
+ - /var/www/html/sites/default/files/civicrm/ext/org.project60.bic/vendor
+ - /var/www/html/sites/default/files/civicrm/ext/org.project60.bic/tools/phpunit/vendor
+ # Don't start Apache HTTP Server, but keep container running
+ command: ["tail", "-f", "/dev/null"]
+ stop_signal: SIGKILL
+ mysql:
+ image: mariadb
+ environment:
+ MARIADB_ROOT_PASSWORD: secret
+ MARIADB_DATABASE: test
diff --git a/tests/docker-phpunit.sh b/tests/docker-phpunit.sh
new file mode 100755
index 0000000..9271976
--- /dev/null
+++ b/tests/docker-phpunit.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+set -eu -o pipefail
+
+SCRIPT_DIR=$(realpath "$(dirname "$0")")
+EXT_DIR=$(dirname "$SCRIPT_DIR")
+
+cd "$EXT_DIR"
+if [ ! -e tools/phpunit/vendor/bin ]; then
+ "$SCRIPT_DIR/docker-prepare.sh"
+fi
+
+# CIVICRM_SMARTY_AUTOLOAD_PATH is not set in the container's civicrm.settings.php so we have to do it here.
+# Otherwise this results in this error:
+# Fatal error: Cannot declare class Smarty, because the name is already in use in /var/www/html/sites/all/modules/civicrm/packages/smarty5/Smarty.php on line 4
+smarty=$(printf '%s\n' /var/www/html/sites/all/modules/civicrm/packages/smarty* | sort -r | head -n1)
+if [ -e "$smarty/Smarty.php" ]; then
+ export CIVICRM_SMARTY_AUTOLOAD_PATH="$smarty/Smarty.php"
+elif [ -e "$smarty/vendor/autoload.php" ]; then
+ export CIVICRM_SMARTY_AUTOLOAD_PATH="$smarty/vendor/autoload.php"
+fi
+
+export XDEBUG_MODE=coverage
+# TODO: Remove when not needed, anymore.
+# In Docker container with CiviCRM 5.5? all deprecations are reported as direct
+# deprecations so "disabling" check of deprecation count is necessary for the
+# tests to pass (if baselineFile does not contain all deprecations).
+export SYMFONY_DEPRECATIONS_HELPER="max[total]=99999&baselineFile=./tests/ignored-deprecations.json"
+
+composer phpunit -- --cache-result-file=/tmp/.phpunit.result.cache "$@"
diff --git a/tests/docker-prepare.sh b/tests/docker-prepare.sh
new file mode 100755
index 0000000..e0a5119
--- /dev/null
+++ b/tests/docker-prepare.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+set -eu -o pipefail
+
+EXT_DIR=$(dirname "$(dirname "$(realpath "$0")")")
+EXT_NAME=$(basename "$EXT_DIR")
+
+if ! type git >/dev/null 2>&1; then
+ apt -y update
+ apt -y install git
+fi
+
+# Prevent this git error: The repository does not have the correct ownership and git refuses to use it
+git config --global --add safe.directory "/var/www/html/sites/default/files/civicrm/ext/$EXT_NAME"
+
+i=0
+while ! mysql -h "$CIVICRM_DB_HOST" -P "$CIVICRM_DB_PORT" -u "$CIVICRM_DB_USER" --password="$CIVICRM_DB_PASS" -e 'SELECT 1;' >/dev/null 2>&1; do
+ i=$((i+1))
+ if [ $i -gt 10 ]; then
+ echo "Failed to connect to database" >&2
+ exit 1
+ fi
+
+ echo -n .
+ sleep 1
+done
+
+echo
+
+export XDEBUG_MODE=off
+if mysql -h "$CIVICRM_DB_HOST" -P "$CIVICRM_DB_PORT" -u "$CIVICRM_DB_USER" --password="$CIVICRM_DB_PASS" "$CIVICRM_DB_NAME" -e 'SELECT 1 FROM civicrm_setting LIMIT 1;' >/dev/null 2>&1; then
+ cv flush
+else
+ # For headless tests it is required that CIVICRM_UF is defined using the corresponding env variable.
+ sed -E "s/define\('CIVICRM_UF', '([^']+)'\);/define('CIVICRM_UF', getenv('CIVICRM_UF') ?: '\1');/g" \
+ -i /var/www/html/sites/default/civicrm.settings.php
+ civicrm-docker-install
+
+ # Avoid this error:
+ # The autoloader expected class "Civi\ActionSchedule\Mapping" to be defined in
+ # file "[...]/Civi/ActionSchedule/Mapping.php". The file was found but the
+ # class was not in it, the class name or namespace probably has a typo.
+ #
+ # Necessary for CiviCRM 5.66.0 - 5.74.x.
+ # https://github.com/civicrm/civicrm-core/blob/5.66.0/Civi/ActionSchedule/Mapping.php
+ if [ -e /var/www/html/sites/all/modules/civicrm/Civi/ActionSchedule/Mapping.php ] \
+ && grep -q '// Empty file' /var/www/html/sites/all/modules/civicrm/Civi/ActionSchedule/Mapping.php; then
+ rm /var/www/html/sites/all/modules/civicrm/Civi/ActionSchedule/Mapping.php
+ fi
+
+ # For headless tests these files need to exist.
+ touch /var/www/html/sites/all/modules/civicrm/sql/test_data.mysql
+ touch /var/www/html/sites/all/modules/civicrm/sql/test_data_second_domain.mysql
+
+ cv ext:enable "$EXT_NAME"
+fi
+
+cd "$EXT_DIR"
+composer update --no-progress --prefer-dist --optimize-autoloader
+composer composer-phpunit -- update --no-progress --prefer-dist
diff --git a/tests/ignored-deprecations.json b/tests/ignored-deprecations.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/ignored-deprecations.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php
new file mode 100644
index 0000000..210c17a
--- /dev/null
+++ b/tests/phpunit/bootstrap.php
@@ -0,0 +1,122 @@
+add('CRM_', [$extensionDir]);
+ $loader->addPsr4('Civi\\', [$extensionDir . '/Civi']);
+ $loader->add('api_', [$extensionDir]);
+ $loader->addPsr4('api\\', [$extensionDir . '/api']);
+ $loader->register();
+
+ if (file_exists($extensionDir . '/autoload.php')) {
+ require_once $extensionDir . '/autoload.php';
+ }
+}
+
+/**
+ * Call the "cv" command.
+ *
+ * @param string $cmd
+ * The rest of the command to send.
+ * @param string $decode
+ * Ex: 'json' or 'phpcode'.
+ * @return mixed
+ * Response output (if the command executed normally).
+ * For 'raw' or 'phpcode', this will be a string. For 'json', it could be any JSON value.
+ * @throws \RuntimeException
+ * If the command terminates abnormally.
+ */
+function cv(string $cmd, string $decode = 'json') {
+ $cmd = 'cv ' . $cmd;
+ $descriptorSpec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => STDERR];
+ $oldOutput = getenv('CV_OUTPUT');
+ putenv('CV_OUTPUT=json');
+
+ // Execute `cv` in the original folder. This is a work-around for
+ // phpunit/codeception, which seem to manipulate PWD.
+ $cmd = sprintf('cd %s; %s', escapeshellarg(getenv('PWD')), $cmd);
+
+ $process = proc_open($cmd, $descriptorSpec, $pipes, __DIR__);
+ putenv("CV_OUTPUT=$oldOutput");
+ fclose($pipes[0]);
+ $result = stream_get_contents($pipes[1]);
+ fclose($pipes[1]);
+ if (proc_close($process) !== 0) {
+ throw new \RuntimeException("Command failed ($cmd):\n$result");
+ }
+ switch ($decode) {
+ case 'raw':
+ return $result;
+
+ case 'phpcode':
+ // If the last output is /*PHPCODE*/, then we managed to complete execution.
+ if (substr(trim($result), 0, 12) !== '/*BEGINPHP*/' || substr(trim($result), -10) !== '/*ENDPHP*/') {
+ throw new \RuntimeException("Command failed ($cmd):\n$result");
+ }
+ return $result;
+
+ case 'json':
+ return json_decode($result, TRUE);
+
+ default:
+ throw new \RuntimeException("Bad decoder format ($decode)");
+ }
+}
diff --git a/tools/create-release.sh b/tools/create-release.sh
new file mode 100755
index 0000000..29cbc35
--- /dev/null
+++ b/tools/create-release.sh
@@ -0,0 +1,399 @@
+#!/bin/bash
+set -euo pipefail
+
+# shellcheck disable=SC2155
+readonly PHP=$(which "${PHP:-php}")
+# shellcheck disable=SC2155
+readonly COMPOSER=$(which "${COMPOSER:-composer}")
+# shellcheck disable=SC2155
+readonly JQ=$(which "${JQ:-jq}")
+
+if [ -z "$PHP" ]; then
+ echo "php not found" >&2
+ exit 1
+fi
+
+if [ -z "$COMPOSER" ]; then
+ echo "composer not found" >&2
+ exit 1
+fi
+
+if [ -z "$JQ" ]; then
+ echo "jq not found" >&2
+ exit 1
+fi
+
+# shellcheck disable=SC2155
+readonly SCRIPT_NAME=$(basename "$0")
+
+usage() {
+ cat <.*' info.xml | sed 's#[[:space:]]*\(.*\)[[:space:]]*#\1#')
+ if [ -z "$version" ]; then
+ echo "Version not found in info.xml" >&2
+ exit 1
+ fi
+
+ if ! [[ "$version" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-dev$ ]]; then
+ echo "The version number $version doesn't match the form a.b.c-dev" >&2
+ exit 1
+ fi
+
+ echo "${BASH_REMATCH[1]}"
+}
+
+detectDevelStage() {
+ local -r version=$1
+ if [[ "$version" = *alpha* ]]; then
+ echo alpha
+ elif [[ "$version" = *beta* ]]; then
+ echo beta
+ elif [[ "$version" = 0.* ]]; then
+ echo dev
+ else
+ echo stable
+ fi
+}
+
+detectNextVersion() {
+ local -r version=$1
+ [[ "$version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]]
+
+ local -r major=${BASH_REMATCH[1]}
+ local minor=${BASH_REMATCH[2]}
+ local patch=${BASH_REMATCH[3]}
+
+ if [[ "$version" = *-* ]]; then
+ # $version has pre-release
+ true
+ elif [ "$major" = 0 ]; then
+ patch=$((patch+1))
+ elif [ "$patch" = 0 ]; then
+ minor=$((minor+1))
+ else
+ patch=$((patch+1))
+ fi
+
+ echo "$major.$minor.$patch-dev"
+}
+
+validateVersion() {
+ local -r version=$1
+ if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta)[1-9][0-9]*)?$ ]]; then
+ echo "The version number $version is not a valid release version" >&2
+ exit 1
+ fi
+
+ if [ -n "$(git tag -l "$version")" ]; then
+ echo "git tag $version already exists" >&2
+ exit 1
+ fi
+}
+
+validateDevelopmentStage() {
+ local -r develStage=$1
+ if ! [[ "$develStage" =~ ^(stable|alpha|beta|dev)$ ]]; then
+ echo "$develStage is not a valid development stage" >&2
+ exit 1
+ fi
+}
+
+validateNextVersion() {
+ local -r nextVersion=$1
+ if ! [[ "$nextVersion" =~ ^[0-9]+\.[0-9]+\.[0-9]+-dev$ ]]; then
+ echo "The version number $nextVersion is not a valid next version" >&2
+ exit 1
+ fi
+}
+
+validateInfoXml() {
+ if [ ! -f info.xml ]; then
+ echo "info.xml not found in working directory" >&2
+ exit 1
+ fi
+
+ # Ensure info.xml contains elements so that they can be replaced in updateInfoXml.
+ if ! grep -q ".*" info.xml; then
+ echo "version element not found in info.xml" >&2
+ exit 1
+ fi
+
+ if ! grep -q ".*" info.xml; then
+ echo "develStage element not found in info.xml" >&2
+ exit 1
+ fi
+
+ if ! grep -q -e "" -e ".*" info.xml; then
+ echo "releaseDate element not found in info.xml" >&2
+ exit 1
+ fi
+}
+
+updateInfoXml() {
+ local -r version=$1
+ local -r develStage=$2
+ local -r releaseDate=${3:-}
+
+ if [ -z "$releaseDate" ]; then
+ local -r releaseDateXml=""
+ else
+ local -r releaseDateXml="$releaseDate"
+ fi
+
+ sed -i -e "s#.*#$version#g" \
+ -e "s#.*#$develStage#g" \
+ -e "s#.*#$releaseDateXml#g" \
+ -e "s##$releaseDateXml#g" \
+ info.xml
+}
+
+hasComposerRequires() {
+ if [ ! -f composer.json ]; then
+ return 1
+ fi
+
+ # All requires that are not "php" or "ext-*".
+ requires=$("$JQ" -r '.require|keys|.[]' composer.json 2>/dev/null | sed -e '/^php$/d' -e '/ext-.*/d')
+ [ "$requires" != "" ]
+}
+
+isVersionLesser() {
+ "$PHP" -r "if (version_compare('$1', '$2', '>=')) exit(1);"
+}
+
+getMinPhpVersion() {
+ local phpVersion=""
+ local -r phpConstraint=$("$JQ" --raw-output --monochrome-output .require.php composer.json)
+ if [ "$phpConstraint" = "null" ]; then
+ echo "PHP version constraint not found in composer.json. Please consider adding it" >&2
+ echo -n "Minimal supported PHP version: " >&2
+ read -r phpVersion
+ else
+ local -r oldIfs=$IFS
+ IFS=' |'
+ local constraint
+ for constraint in $phpConstraint; do
+ if [[ "$constraint" =~ ^(\^|~|>=)([0-9]+(\.[0-9]+(\.[0-9]+)?)?)$ ]]; then
+ if [ -z "$phpVersion" ] || isVersionLesser "${BASH_REMATCH[2]}" "$phpVersion"; then
+ phpVersion=${BASH_REMATCH[2]}
+ fi
+ fi
+ done
+ IFS=$oldIfs
+
+ if [ -n "$phpVersion" ]; then
+ echo -n "Minimal supported PHP version [$phpVersion]: " >&2
+ read -r input
+ if [ "$input" != "" ]; then
+ phpVersion=$input
+ fi
+ else
+ echo "Minimal supported PHP version could not be detected from composer version constraint. (Supported operators: ^, ~, >=, |)" >&2
+ echo -n "Minimal supported PHP version: " >&2
+ read -r phpVersion
+ fi
+ fi
+
+ echo "$phpVersion"
+}
+
+validateMinPhpVersion() {
+ if ! [[ "$1" =~ ^[0-9]+(\.[0-9]+(\.[0-9]+)?)?$ ]]; then
+ echo "$1 is not a supported minimal PHP version" >&2
+ exit 1
+ fi
+}
+
+updatePot() {
+ local -r potFiles=(l10n/*.pot)
+ if [ ${#potFiles[@]} -ge 1 ] && [ -e "${potFiles[0]}" ] && [ -x tools/update-pot.sh ]; then
+ echo "Update .pot file"
+ tools/update-pot.sh
+ if ! git diff --no-patch --exit-code "${potFiles[*]}"; then
+ echo ".pot file has changed. Please update the translation and push changes to the repository." >&2
+ exit 1
+ fi
+ fi
+}
+
+main() {
+ DRY_RUN=0
+ local noComposer=0
+ local noPotUpdate=0
+
+ while [ $# -gt 0 ]; do
+ case $1 in
+ -h|--help)
+ usage
+ exit 0
+ ;;
+
+ --dry-run)
+ DRY_RUN=1
+ shift
+ ;;
+
+ --no-composer)
+ noComposer=1
+ shift
+ ;;
+
+ --no-pot-update)
+ noPotUpdate=1
+ shift
+ ;;
+
+ *)
+ break
+ ;;
+ esac
+ done
+
+ if [ $# -gt 3 ]; then
+ usage >&2
+ exit 1
+ fi
+
+ validateInfoXml
+
+ local version
+ local nextVersion
+ local develStage
+
+ if [ $# -ge 1 ]; then
+ version=$1
+ else
+ version=$(detectVersion)
+ echo -n "Version [$version]: "
+ read -r input
+ if [ -n "$input" ]; then
+ version=$input
+ fi
+ fi
+ validateVersion "$version"
+
+ if [ $# -ge 2 ]; then
+ develStage=$2
+ else
+ develStage=$(detectDevelStage "$version")
+ echo -n "Development stage [$develStage]: "
+ read -r input
+ if [ -n "$input" ]; then
+ develStage=$input
+ fi
+ fi
+ validateDevelopmentStage "$develStage"
+
+ if [ $# -ge 3 ]; then
+ nextVersion=$3
+ else
+ nextVersion=$(detectNextVersion "$version")
+ echo -n "Next version [$nextVersion]: "
+ read -r input
+ if [ -n "$input" ]; then
+ nextVersion=$input
+ fi
+ fi
+ validateNextVersion "$nextVersion"
+
+ if [ $noComposer -eq 0 ] && ! hasComposerRequires; then
+ noComposer=1
+ fi
+
+ if [ $noComposer -eq 0 ]; then
+ local -r minPhpVersion=$(getMinPhpVersion)
+ validateMinPhpVersion "$minPhpVersion"
+ fi
+
+ if [ $noPotUpdate -eq 0 ]; then
+ updatePot
+ fi
+
+ local -r releaseDate=$(date +%Y-%m-%d)
+ run updateInfoXml "$version" "$develStage" "$releaseDate"
+ run git add info.xml
+
+ if [ $noComposer -eq 0 ]; then
+ local -r previousPlatformPhp=$(composer config platform.php 2>/dev/null ||:)
+ run composer config platform.php "$minPhpVersion"
+ run composer update --no-dev --optimize-autoloader
+ if [ -n "$previousPlatformPhp" ]; then
+ run composer config platform.php "$previousPlatformPhp"
+ else
+ run composer config --unset platform.php
+ fi
+ run git add -f composer.lock vendor
+ fi
+
+ run git commit -m "Set version to $version"
+ run git tag "$version"
+
+ run updateInfoXml "$nextVersion" "dev"
+ run git add info.xml
+
+ if [ $noComposer -eq 0 ]; then
+ run git rm -r composer.lock vendor
+ fi
+
+ if [ -f composer.json ]; then
+ local -r branch=$(git branch --show-current)
+ if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then
+ [[ "$nextVersion" =~ ^([0-9]+\.[0-9]+)\.[0-9]+ ]]
+ local -r alias=${BASH_REMATCH[1]}.x-dev
+ run composer config "extra.branch-alias.dev-$branch" "$alias"
+ run git add composer.json
+ fi
+ fi
+
+ run git commit -m "Set version to $nextVersion"
+
+ echo ""
+ echo "Push changes with: git push && git push --tags"
+}
+
+main "$@"
diff --git a/tools/git/hooks-wrapper.sh b/tools/git/hooks-wrapper.sh
new file mode 100755
index 0000000..f32978c
--- /dev/null
+++ b/tools/git/hooks-wrapper.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+# Inspired by https://github.com/sjungwirth/githooks/blob/949d55e84e92dfd84e9a73a8b7624bb2e4dbc872/bin/git/hooks-wrapper
+
+# This script needs to be symlinked to .git/hooks/.
+
+set -e
+
+HOOKNAME=$(basename "$0")
+NATIVE_HOOKS_DIR=$(dirname "$0")
+CUSTOM_HOOKS_DIR=$(dirname "$(realpath "$0")")/hooks
+
+# Runs all executables in $CUSTOM_HOOKS_DIR/hooks/$HOOKNAME.d and
+# $NATIVE_HOOKS_DIR/$HOOKNAME.local if existent.
+
+exitcode=
+for hook in "$CUSTOM_HOOKS_DIR/$HOOKNAME.d/"*; do
+ if [ -x "$hook" ]; then
+ "$hook" "$@" || exitcode=${exitcode:-$?}
+ fi
+done
+
+if [ -x "$NATIVE_HOOKS_DIR/$HOOKNAME.local" ]; then
+ "$NATIVE_HOOKS_DIR/$HOOKNAME.local" "$@" || exitcode=${exitcode:-$?}
+fi
+
+# shellcheck disable=SC2086
+exit $exitcode
diff --git a/tools/git/hooks/pre-commit.d/remember-update-pot.sh b/tools/git/hooks/pre-commit.d/remember-update-pot.sh
new file mode 100755
index 0000000..827242c
--- /dev/null
+++ b/tools/git/hooks/pre-commit.d/remember-update-pot.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo "Please remember to update the .pot file (tools/update-pot.sh) and the translation."
+
diff --git a/tools/git/hooks/pre-merge-commit.d/run-pre-commit-hook.sh b/tools/git/hooks/pre-merge-commit.d/run-pre-commit-hook.sh
new file mode 100755
index 0000000..b3c4912
--- /dev/null
+++ b/tools/git/hooks/pre-merge-commit.d/run-pre-commit-hook.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+. git-sh-setup
+if [ -x "$GIT_DIR/hooks/pre-commit" ]; then
+ exec "$GIT_DIR/hooks/pre-commit"
+fi
diff --git a/tools/git/init-hooks.sh b/tools/git/init-hooks.sh
new file mode 100755
index 0000000..b635da0
--- /dev/null
+++ b/tools/git/init-hooks.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# Inspired by https://github.com/sjungwirth/githooks/blob/949d55e84e92dfd84e9a73a8b7624bb2e4dbc872/bin/git/init-hooks
+
+set -e
+
+SCRIPT_DIR=$(dirname "$0")
+NATIVE_HOOKS_DIR=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)/.git/hooks
+CUSTOM_HOOKS_DIR=$SCRIPT_DIR/hooks
+HOOKS_WRAPPER=$(realpath -s --relative-to="$NATIVE_HOOKS_DIR" "$SCRIPT_DIR")/hooks-wrapper.sh
+
+cd "$CUSTOM_HOOKS_DIR"
+HOOK_DIRS=(*.d)
+
+for hook_dir in "${HOOK_DIRS[@]}"; do
+ hookname=${hook_dir:0:-2}
+ if [ ! -L "$NATIVE_HOOKS_DIR/$hookname" ]; then
+ if [ -f "$NATIVE_HOOKS_DIR/$hookname" ]; then
+ mv "$NATIVE_HOOKS_DIR/$hookname" "$NATIVE_HOOKS_DIR/$hookname.local"
+ fi
+ ln -s "$HOOKS_WRAPPER" "$NATIVE_HOOKS_DIR/$hookname"
+ fi
+done
diff --git a/tools/phpcs/composer.json b/tools/phpcs/composer.json
new file mode 100644
index 0000000..980e4b9
--- /dev/null
+++ b/tools/phpcs/composer.json
@@ -0,0 +1,11 @@
+{
+ "repositories": [
+ {
+ "type": "git",
+ "url": "https://github.com/civicrm/coder.git"
+ }
+ ],
+ "require": {
+ "drupal/coder": "dev-8.x-2.x-civi"
+ }
+}
diff --git a/tools/phpstan/composer.json b/tools/phpstan/composer.json
new file mode 100644
index 0000000..ce2fee2
--- /dev/null
+++ b/tools/phpstan/composer.json
@@ -0,0 +1,19 @@
+{
+ "require": {
+ "kcs/phpstan-strict-rules": "^2",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2",
+ "phpstan/phpstan-beberlei-assert": "^2",
+ "phpstan/phpstan-deprecation-rules": "^2",
+ "phpstan/phpstan-phpunit": "^2",
+ "phpstan/phpstan-strict-rules": "^2",
+ "phpstan/phpstan-webmozart-assert": "^2",
+ "voku/phpstan-rules": "^3.6"
+ },
+ "config": {
+ "allow-plugins": {
+ "phpstan/extension-installer": true
+ },
+ "sort-packages": true
+ }
+}
diff --git a/tools/phpunit/composer.json b/tools/phpunit/composer.json
new file mode 100644
index 0000000..ab64ce0
--- /dev/null
+++ b/tools/phpunit/composer.json
@@ -0,0 +1,13 @@
+{
+ "require": {
+ "symfony/phpunit-bridge": "^7"
+ },
+ "scripts": {
+ "post-install-cmd": [
+ "@php vendor/bin/simple-phpunit install --configuration ../../phpunit.xml.dist"
+ ],
+ "post-update-cmd": [
+ "@php vendor/bin/simple-phpunit install --configuration ../../phpunit.xml.dist"
+ ]
+ }
+}
diff --git a/tools/update-pot.sh b/tools/update-pot.sh
new file mode 100755
index 0000000..485335f
--- /dev/null
+++ b/tools/update-pot.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -euo pipefail
+
+readonly SCRIPT_PATH="$0"
+SCRIPT_NAME=$(basename "$SCRIPT_PATH")
+readonly SCRIPT_NAME
+SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
+readonly SCRIPT_DIR
+
+usage() {
+ cat <
Date: Mon, 8 Sep 2025 12:21:11 +0200
Subject: [PATCH 02/12] Apply changes to `composer.json` from
civicrm-extension-template
---
composer.json | 76 +++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 62 insertions(+), 14 deletions(-)
diff --git a/composer.json b/composer.json
index d92b94b..f6a7513 100644
--- a/composer.json
+++ b/composer.json
@@ -1,17 +1,65 @@
{
- "name": "project60/org.project60.bic",
- "description": "Imports and maintains a list of banks",
- "version": "1.8.0-alpha1",
- "type": "civicrm-module",
- "authors": [
- {
- "name": "B. Endres",
- "email": "endres@systopia.de"
+ "name": "project60/org.project60.bic",
+ "description": "Generates and maintains a list of banks",
+ "type": "civicrm-ext",
+ "license": "AGPL-3.0-or-later",
+ "authors": [
+ {
+ "name": "SYSTOPIA GmbH",
+ "email": "info@systopia.de",
+ "homepage": "https://www.systopia.de"
+ }
+ ],
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "prepend-autoloader": false,
+ "sort-packages": true,
+ "platform": {
+ }
+ },
+ "require": {
+ "php": "^8.1",
+ "civicrm/civicrm-core": ">=5.45",
+ "phpoffice/phpspreadsheet": "^1.30"
+ },
+ "autoload": {
+ "classmap": ["ComposerHelper.php"]
+ },
+ "scripts": {
+ "pre-update-cmd": [
+ "Bic\\ComposerHelper::preUpdate"
+ ],
+ "composer-phpcs": [
+ "@composer --working-dir=tools/phpcs"
+ ],
+ "composer-phpstan": [
+ "@composer --working-dir=tools/phpstan"
+ ],
+ "composer-phpunit": [
+ "@composer --working-dir=tools/phpunit"
+ ],
+ "composer-tools": [
+ "@composer-phpcs",
+ "@composer-phpstan",
+ "@composer-phpunit"
+ ],
+ "phpcs": [
+ "@php tools/phpcs/vendor/bin/phpcs"
+ ],
+ "phpcbf": [
+ "@php tools/phpcs/vendor/bin/phpcbf"
+ ],
+ "phpstan": [
+ "@php tools/phpstan/vendor/bin/phpstan -v"
+ ],
+ "phpunit": [
+ "@php tools/phpunit/vendor/bin/simple-phpunit --coverage-text"
+ ],
+ "test": [
+ "@phpcs",
+ "@phpstan",
+ "@phpunit"
+ ]
}
- ],
- "license": "AGPL-3.0",
- "require": {
- "php": "^8.0",
- "phpoffice/phpspreadsheet": "^1.30"
- }
}
From 76fec01f0b83ae8c1bda1ed00f6428dd6ebe4c9a Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 13:29:20 +0200
Subject: [PATCH 03/12] Increase core version requirement to lowest available
docker image for unit tests (5.54)
---
.github/workflows/phpunit.yml | 2 +-
ci/composer.json | 2 +-
composer.json | 2 +-
info.xml | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
index 46a30af..c356341 100644
--- a/.github/workflows/phpunit.yml
+++ b/.github/workflows/phpunit.yml
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- civicrm-image-tags: [ 'drupal', '5.45-drupal-php8.1' ]
+ civicrm-image-tags: [ 'drupal', '5.54-drupal-php8.1' ]
name: PHPUnit with Docker image michaelmcandrew/civicrm:${{ matrix.civicrm-image-tags }}
env:
CIVICRM_IMAGE_TAG: ${{ matrix.civicrm-image-tags }}
diff --git a/ci/composer.json b/ci/composer.json
index 21e1c53..0a404d9 100644
--- a/ci/composer.json
+++ b/ci/composer.json
@@ -10,6 +10,6 @@
"sort-packages": true
},
"require": {
- "civicrm/civicrm-core": ">=5.45"
+ "civicrm/civicrm-core": ">=5.54"
}
}
diff --git a/composer.json b/composer.json
index f6a7513..ee75e7d 100644
--- a/composer.json
+++ b/composer.json
@@ -20,7 +20,7 @@
},
"require": {
"php": "^8.1",
- "civicrm/civicrm-core": ">=5.45",
+ "civicrm/civicrm-core": ">=5.54",
"phpoffice/phpspreadsheet": "^1.30"
},
"autoload": {
diff --git a/info.xml b/info.xml
index 6af214e..6634025 100644
--- a/info.xml
+++ b/info.xml
@@ -17,7 +17,7 @@
https://github.com/Project60/org.project60.bic/issues
- 5.45
+ 5.54
8.0
From 9a0e8974a2252022fc9f3730d4bb2089708805d5 Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 13:31:14 +0200
Subject: [PATCH 04/12] Fix phpcs errors by running phpcbf
---
CRM/Bic/Page/BicList.php | 23 +--
CRM/Bic/Page/Config.php | 9 +-
CRM/Bic/Parser/AT.php | 44 +++--
CRM/Bic/Parser/BE.php | 32 ++--
CRM/Bic/Parser/CH.php | 25 ++-
CRM/Bic/Parser/DE.php | 118 ++++++------
CRM/Bic/Parser/ES.php | 14 +-
CRM/Bic/Parser/LU.php | 28 +--
CRM/Bic/Parser/NL.php | 26 +--
CRM/Bic/Parser/PL.php | 23 ++-
CRM/Bic/Parser/Parser.php | 94 +++++-----
CRM/Bic/Upgrader.php | 49 ++---
Civi/Bic/ActionProvider/Action/LookupBic.php | 164 ++++++++--------
Civi/Bic/ActionProvider/Action/VerifyBic.php | 187 +++++++++----------
Civi/Bic/ContainerSpecs.php | 37 ++--
api/v3/Bic.php | 89 +++++----
bic.php | 20 +-
17 files changed, 512 insertions(+), 470 deletions(-)
diff --git a/CRM/Bic/Page/BicList.php b/CRM/Bic/Page/BicList.php
index 2c7d63a..f933ca1 100644
--- a/CRM/Bic/Page/BicList.php
+++ b/CRM/Bic/Page/BicList.php
@@ -4,30 +4,30 @@
use CRM_Bic_ExtensionUtil as E;
class CRM_Bic_Page_BicList extends CRM_Core_Page {
- function run() {
+
+ public function run() {
// Prepares variables for being sent to Smarty
CRM_Utils_System::setTitle(E::ts('Find Banks'));
-
//Only show countries with attached information
- $countries = null;
+ $countries = NULL;
$stats = civicrm_api3('Bic', 'stats');
- foreach($stats['values'] as $country => $count) {
+ foreach ($stats['values'] as $country => $count) {
$countries[] = $country;
}
-
+
// Get country names
- $country_names = null;
- if($countries) {
+ $country_names = NULL;
+ if ($countries) {
$config = CRM_Core_Config::singleton();
- $country_names = array();
+ $country_names = [];
$id2code = CRM_Core_PseudoConstant::countryIsoCode();
$default_country = $id2code[$config->defaultContactCountry] ?? '';
$code2id = array_flip($id2code);
- $id2country = CRM_Core_PseudoConstant::country(false, false);
+ $id2country = CRM_Core_PseudoConstant::country(FALSE, FALSE);
foreach ($countries as $code) {
$country_id = $code2id[$code];
$country_name = $id2country[$country_id];
@@ -39,8 +39,9 @@ function run() {
$this->assign('countries', $countries);
$this->assign('country_names', $country_names);
$this->assign('default_country', $default_country);
- $this->assign('show_message', true);
-
+ $this->assign('show_message', TRUE);
+
parent::run();
}
+
}
diff --git a/CRM/Bic/Page/Config.php b/CRM/Bic/Page/Config.php
index 1796ef5..4c1d139 100644
--- a/CRM/Bic/Page/Config.php
+++ b/CRM/Bic/Page/Config.php
@@ -18,7 +18,8 @@
use CRM_Bic_ExtensionUtil as E;
class CRM_Bic_Page_Config extends CRM_Core_Page {
- function run() {
+
+ public function run() {
CRM_Utils_System::setTitle(E::ts('Available Banks'));
@@ -28,13 +29,14 @@ function run() {
foreach ($countries as $country) {
if (isset($stats['values'][$country])) {
$total_count += $stats['values'][$country];
- } else {
+ }
+ else {
$stats['values'][$country] = 0;
}
}
// gather the names
- $country_names = array();
+ $country_names = [];
$config = CRM_Core_Config::singleton();
@@ -56,4 +58,5 @@ function run() {
parent::run();
}
+
}
diff --git a/CRM/Bic/Parser/AT.php b/CRM/Bic/Parser/AT.php
index 71e63e7..13baad2 100644
--- a/CRM/Bic/Parser/AT.php
+++ b/CRM/Bic/Parser/AT.php
@@ -34,7 +34,7 @@ public function update() {
}
$banks = [];
- $headers = null;
+ $headers = NULL;
// iterate CSV records
foreach ($data as $line) {
@@ -43,13 +43,15 @@ public function update() {
$fields = str_getcsv($line, ';');
// skip some stuff: preamble
- if (count($fields) < 20) continue;
+ if (count($fields) < 20) {
+ continue;
+ }
if ($headers === NULL) {
// first 'real' line should be header
$headers = $fields;
- if ( !in_array('Identnummer', $headers)
+ if (!in_array('Identnummer', $headers)
|| !in_array('Bankleitzahl', $headers)
|| !in_array('SWIFT-Code', $headers)) {
return $this->createParserOutdatedError(E::ts("Source file doesn't contain Identnummer/Bankleitzahl/SWIFT-Code"));
@@ -60,18 +62,20 @@ public function update() {
$data_set = array_combine($headers, $fields);
// we will only process banks with a BIC/SWIFT Code
- if ( empty($data_set['Identnummer'])
+ if (empty($data_set['Identnummer'])
|| empty($data_set['Bankleitzahl'])
|| empty($data_set['SWIFT-Code'])
- ) continue;
+ ) {
+ continue;
+ }
// compile data set
- $banks[$data_set['Bankleitzahl']] = array(
- 'value' => $data_set['Bankleitzahl'],
- 'name' => $data_set['SWIFT-Code'],
- 'label' => $data_set['Bankenname'] ?? 'Unknown',
- 'description' => $this->getDescription($data_set)
- );
+ $banks[$data_set['Bankleitzahl']] = [
+ 'value' => $data_set['Bankleitzahl'],
+ 'name' => $data_set['SWIFT-Code'],
+ 'label' => $data_set['Bankenname'] ?? 'Unknown',
+ 'description' => $this->getDescription($data_set),
+ ];
}
}
@@ -88,8 +92,7 @@ public function update() {
* @return string
* the description
*/
- protected function getDescription($data_set)
- {
+ protected function getDescription($data_set) {
$description = '';
if (!empty($data_set['Straße'])) {
$description .= $data_set['Straße'] . ',';
@@ -103,12 +106,15 @@ protected function getDescription($data_set)
return $description;
}
- /*
- * Extracts the National Bank Identifier from an Austrian IBAN.
- */
+ /**
+ *
+ * Extracts the National Bank Identifier from an Austrian IBAN.
+ *
+ */
public function extractNBIDfromIBAN($iban) {
- return array(
- substr($iban, 4, 5),
- );
+ return [
+ substr($iban, 4, 5),
+ ];
}
+
}
diff --git a/CRM/Bic/Parser/BE.php b/CRM/Bic/Parser/BE.php
index a54be91..f04f37e 100644
--- a/CRM/Bic/Parser/BE.php
+++ b/CRM/Bic/Parser/BE.php
@@ -43,8 +43,8 @@ public function update() {
$excel_reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile($file_name);
// Set reader options
- $excel_reader->setReadDataOnly(true);
- $excel_reader->setLoadSheetsOnly(["Q_FULL_LIST_XLS_REPORT"]);
+ $excel_reader->setReadDataOnly(TRUE);
+ $excel_reader->setLoadSheetsOnly(['Q_FULL_LIST_XLS_REPORT']);
// Read Excel file
$excel_object = $excel_reader->load($file_name);
@@ -52,33 +52,37 @@ public function update() {
// Process Excel data
$skip_lines = 2;
- $banks[] = array();
- foreach($excel_rows as $excel_row) {
+ $banks[] = [];
+ foreach ($excel_rows as $excel_row) {
$skip_lines -= 1;
- if ($skip_lines >= 0) continue;
+ if ($skip_lines >= 0) {
+ continue;
+ }
// Process row
$bic = str_replace(' ', '', $excel_row[1]);
- if (in_array(strtolower($bic), array('nav', 'nap', '-')) || substr($bic, 0, 4)=='VRIJ') {
+ if (in_array(strtolower($bic), ['nav', 'nap', '-']) || substr($bic, 0, 4) == 'VRIJ') {
// these are actually dummy entries
continue;
}
// compile bank name
$bank_name = '';
- for ($i=2; $i<5; $i++) {
+ for ($i = 2; $i < 5; $i++) {
$localized_name = trim($excel_row[$i]);
if (!empty($localized_name)) {
- if (!empty($bank_name)) $bank_name .= ' / ';
+ if (!empty($bank_name)) {
+ $bank_name .= ' / ';
+ }
$bank_name .= $localized_name;
}
}
- $bank = array(
+ $bank = [
'value' => $excel_row[0],
'name' => $bic,
'label' => $bank_name,
'description' => '',
- );
+ ];
$banks[] = $bank;
}
@@ -92,13 +96,15 @@ public function update() {
return $this->updateEntries(CRM_Bic_Parser_BE::$country_code, $banks);
}
- /*
+ /**
+ *
* Extracts the National Bank Identifier from an Belgium IBAN.
+ *
*/
public function extractNBIDfromIBAN($iban) {
- return array(
+ return [
substr($iban, 4, 3),
- );
+ ];
}
}
diff --git a/CRM/Bic/Parser/CH.php b/CRM/Bic/Parser/CH.php
index 7bca142..61553b7 100644
--- a/CRM/Bic/Parser/CH.php
+++ b/CRM/Bic/Parser/CH.php
@@ -25,7 +25,7 @@ class CRM_Bic_Parser_CH extends CRM_Bic_Parser_Parser {
public function update() {
// first, download the page
- $banks = array();
+ $banks = [];
$data = $this->downloadFile(CRM_Bic_Parser_CH::$page_url);
if (empty($data)) {
return $this->createParserOutdatedError(ts("Couldn't download data set"));
@@ -40,30 +40,35 @@ public function update() {
$address = substr($line, 184, 4) . ' ' . trim(substr($line, 194, 35));
// we only want branches with BICs
- if (empty($bic)) continue;
+ if (empty($bic)) {
+ continue;
+ }
// encode names
- $name = mb_convert_encoding($name, 'UTF-8', 'ISO-8859-1');
+ $name = mb_convert_encoding($name, 'UTF-8', 'ISO-8859-1');
$address = mb_convert_encoding($address, 'UTF-8', 'ISO-8859-1');
-
- $banks[$bc_code] = array(
+
+ $banks[$bc_code] = [
'value' => $bc_code,
'name' => $bic,
'label' => $name,
'description' => $address,
- );
+ ];
}
// // finally, update DB
return $this->updateEntries('CH', $banks);
}
- /*
+ /**
+ *
* Extracts the National Bank Identifier from an Austrian IBAN.
+ *
*/
public function extractNBIDfromIBAN($iban) {
- return array(
+ return [
substr($iban, 4, 5),
- );
- }
+ ];
+ }
+
}
diff --git a/CRM/Bic/Parser/DE.php b/CRM/Bic/Parser/DE.php
index ea0f6c1..52d6876 100644
--- a/CRM/Bic/Parser/DE.php
+++ b/CRM/Bic/Parser/DE.php
@@ -25,65 +25,69 @@
*/
class CRM_Bic_Parser_DE extends CRM_Bic_Parser_Parser {
- static $page_url = 'https://www.bundesbank.de/resource/blob/926192/07561adcd024c5752c46afa317d554af/mL/blz-aktuell-csv-data.csv';
-
- static $country_code = 'DE';
-
- public function update()
- {
- // First, download the file
- $file_name = sys_get_temp_dir() . '/DE-banks.csv';
- $downloaded_file = $this->downloadFile(CRM_Bic_Parser_DE::$page_url);
-
- if (empty($downloaded_file)) {
- return $this->createParserOutdatedError(ts("Couldn't download data file"));
- }
-
- // store file
- file_put_contents($file_name, $downloaded_file);
- unset($downloaded_file);
-
- // Open and read CSV file
- if (($handle = fopen($file_name, "r")) === false) {
- return $this->createParserOutdatedError(ts("Couldn't open data file"));
- }
-
- // Skip header row
- fgetcsv($handle, 1000, ';');
-
- $banks = [];
- while (($data = fgetcsv($handle, 1000, ';')) !== false) {
- // skip entries with no bic
- if (empty($data[7])) continue;
- if ($data[1] != 1) continue;
-
- // Process row
- $bank = array(
- 'value' => $data[0],
- 'name' => mb_convert_encoding($data[7], 'UTF-8', 'ISO-8859-1'),
- 'label' => mb_convert_encoding($data[5], 'UTF-8', 'ISO-8859-1'),
- 'description' => mb_convert_encoding($data[3], 'UTF-8', 'ISO-8859-1') . ' '
- . mb_convert_encoding($data[4], 'UTF-8', 'ISO-8859-1'),
- );
-
- $banks[] = $bank;
- }
-
- fclose($handle);
- unlink($file_name);
-
- // Finally, update DB
- return $this->updateEntries(CRM_Bic_Parser_DE::$country_code, $banks);
+ static $page_url = 'https://www.bundesbank.de/resource/blob/926192/07561adcd024c5752c46afa317d554af/mL/blz-aktuell-csv-data.csv';
+
+ static $country_code = 'DE';
+
+ public function update() {
+ // First, download the file
+ $file_name = sys_get_temp_dir() . '/DE-banks.csv';
+ $downloaded_file = $this->downloadFile(CRM_Bic_Parser_DE::$page_url);
+
+ if (empty($downloaded_file)) {
+ return $this->createParserOutdatedError(ts("Couldn't download data file"));
}
- /*
- * Extracts the National Bank Identifier from an IBAN.
- */
- public function extractNBIDfromIBAN($iban)
- {
- return array(
- substr($iban, 4, 8),
- );
+ // store file
+ file_put_contents($file_name, $downloaded_file);
+ unset($downloaded_file);
+
+ // Open and read CSV file
+ if (($handle = fopen($file_name, 'r')) === FALSE) {
+ return $this->createParserOutdatedError(ts("Couldn't open data file"));
}
+ // Skip header row
+ fgetcsv($handle, 1000, ';');
+
+ $banks = [];
+ while (($data = fgetcsv($handle, 1000, ';')) !== FALSE) {
+ // skip entries with no bic
+ if (empty($data[7])) {
+ continue;
+ }
+ if ($data[1] != 1) {
+ continue;
+ }
+
+ // Process row
+ $bank = [
+ 'value' => $data[0],
+ 'name' => mb_convert_encoding($data[7], 'UTF-8', 'ISO-8859-1'),
+ 'label' => mb_convert_encoding($data[5], 'UTF-8', 'ISO-8859-1'),
+ 'description' => mb_convert_encoding($data[3], 'UTF-8', 'ISO-8859-1') . ' '
+ . mb_convert_encoding($data[4], 'UTF-8', 'ISO-8859-1'),
+ ];
+
+ $banks[] = $bank;
+ }
+
+ fclose($handle);
+ unlink($file_name);
+
+ // Finally, update DB
+ return $this->updateEntries(CRM_Bic_Parser_DE::$country_code, $banks);
+ }
+
+ /**
+ *
+ * Extracts the National Bank Identifier from an IBAN.
+ *
+ */
+ public function extractNBIDfromIBAN($iban) {
+ return [
+ substr($iban, 4, 8),
+ ];
+ }
+
}
diff --git a/CRM/Bic/Parser/ES.php b/CRM/Bic/Parser/ES.php
index 6cf58c4..a720a28 100644
--- a/CRM/Bic/Parser/ES.php
+++ b/CRM/Bic/Parser/ES.php
@@ -29,7 +29,7 @@ public function update() {
return $this->createParserOutdatedError(ts("Couldn't download basic page"));
}
$header = NULL;
- $banks = array();
+ $banks = [];
// iterate CSV records
foreach ($data as $line) {
@@ -49,12 +49,12 @@ public function update() {
continue;
}
- $banks[$code] = array(
+ $banks[$code] = [
'value' => $code,
'name' => $bic,
'label' => $name,
'description' => $name,
- );
+ ];
}
}
@@ -66,19 +66,19 @@ public function update() {
* Extracts the National Bank Identifier from an Spanish IBAN.
*/
public function extractNBIDfromIBAN($iban) {
- return array(
+ return [
substr($iban, 4, 4),
substr($iban, 4, 8),
- );
+ ];
}
/**
* Returns csv line as array
*/
private function csvLineToArray($str) {
- $expr = "/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/";
+ $expr = '/,(?=(?:[^"]*"[^"]*")*(?![^"]*"))/';
$results = preg_split($expr, trim($str));
- return preg_replace("/^\"(.*)\"$/", "$1", $results);
+ return preg_replace('/^"(.*)"$/', '$1', $results);
}
}
diff --git a/CRM/Bic/Parser/LU.php b/CRM/Bic/Parser/LU.php
index 2a86107..976fb62 100644
--- a/CRM/Bic/Parser/LU.php
+++ b/CRM/Bic/Parser/LU.php
@@ -43,8 +43,8 @@ public function update() {
$excel_reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile($file_name);
// Set reader options
- $excel_reader->setReadDataOnly(true);
- $excel_reader->setLoadSheetsOnly(["Organizations"]);
+ $excel_reader->setReadDataOnly(TRUE);
+ $excel_reader->setLoadSheetsOnly(['Organizations']);
// Read Excel file
$excel_object = $excel_reader->load($file_name);
@@ -52,18 +52,20 @@ public function update() {
// Process Excel data
$skip_lines = 2;
- $banks[] = array();
- foreach($excel_rows as $excel_row) {
+ $banks[] = [];
+ foreach ($excel_rows as $excel_row) {
$skip_lines -= 1;
- if ($skip_lines >= 0) continue;
+ if ($skip_lines >= 0) {
+ continue;
+ }
// Process row
- $bank = array(
+ $bank = [
'value' => $excel_row[1],
'name' => str_replace(' ', '', $excel_row[2]),
'label' => $excel_row[0],
- 'description' => ''
- );
+ 'description' => '',
+ ];
$banks[] = $bank;
}
@@ -77,13 +79,15 @@ public function update() {
return $this->updateEntries(CRM_Bic_Parser_LU::$country_code, $banks);
}
- /*
+ /**
+ *
* Extracts the National Bank Identifier from an IBAN.
+ *
*/
public function extractNBIDfromIBAN($iban) {
- return array(
- substr($iban, 4, 3)
- );
+ return [
+ substr($iban, 4, 3),
+ ];
}
}
diff --git a/CRM/Bic/Parser/NL.php b/CRM/Bic/Parser/NL.php
index c45289c..d53a707 100644
--- a/CRM/Bic/Parser/NL.php
+++ b/CRM/Bic/Parser/NL.php
@@ -44,8 +44,8 @@ public function update() {
$excel_reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile($file_name);
// Set reader options
- $excel_reader->setReadDataOnly(true);
- $excel_reader->setLoadSheetsOnly(["BIC-lijst"]);
+ $excel_reader->setReadDataOnly(TRUE);
+ $excel_reader->setLoadSheetsOnly(['BIC-lijst']);
// Read Excel file
$excel_object = $excel_reader->load($file_name);
@@ -53,18 +53,20 @@ public function update() {
// Process Excel data
$skip_lines = 2;
- $banks[] = array();
- foreach($excel_rows as $excel_row) {
+ $banks[] = [];
+ foreach ($excel_rows as $excel_row) {
$skip_lines -= 1;
- if ($skip_lines >= 0) continue;
+ if ($skip_lines >= 0) {
+ continue;
+ }
// Process row
- $bank = array(
+ $bank = [
'value' => $excel_row[1],
'name' => $excel_row[0],
'label' => $excel_row[2],
- 'description' => ''
- );
+ 'description' => '',
+ ];
$banks[] = $bank;
}
@@ -78,13 +80,15 @@ public function update() {
return $this->updateEntries(CRM_Bic_Parser_NL::$country_code, $banks);
}
- /*
+ /**
+ *
* Extracts the National Bank Identifier from an IBAN.
+ *
*/
public function extractNBIDfromIBAN($iban) {
- return array(
+ return [
substr($iban, 4, 4),
- );
+ ];
}
}
diff --git a/CRM/Bic/Parser/PL.php b/CRM/Bic/Parser/PL.php
index 2d43a6f..e4fd188 100644
--- a/CRM/Bic/Parser/PL.php
+++ b/CRM/Bic/Parser/PL.php
@@ -32,12 +32,12 @@ public function update() {
return $this->createParserOutdatedError(ts("Couldn't download CSV file"));
}
- $data = array();
+ $data = [];
$count = 0;
- foreach( $lines as $line ) {
+ foreach ($lines as $line) {
// convert to UTF8 - original encoding is IBM Latin 2 (CP852)
- $line = iconv('CP852', 'UTF-8', $line );
+ $line = iconv('CP852', 'UTF-8', $line);
// it's tab delimited file with a lot of extra whitespace - splitting, trimming
$data[] = array_map('trim', str_getcsv($line, "\t"));
$count++;
@@ -50,14 +50,14 @@ public function update() {
}
// process lines
- for ($i=0; $i<$count; $i++) {
+ for ($i = 0; $i < $count; $i++) {
$key = $data[$i][4];
- $banks[$key] = array(
+ $banks[$key] = [
'value' => $key,
'name' => $data[$i][20],
'label' => $data[$i][1] . ', ' . $data[$i][5],
'description' => '',
- );
+ ];
}
unset($data);
@@ -66,13 +66,16 @@ public function update() {
return $this->updateEntries('PL', $banks);
}
- /*
+ /**
+ *
* Extracts the National Bank Identifier from an IBAN.
* In Poland it's 8 digits after checksum
+ *
*/
public function extractNBIDfromIBAN($iban) {
- return array(
- substr($iban, 4, 8)
- );
+ return [
+ substr($iban, 4, 8),
+ ];
}
+
}
diff --git a/CRM/Bic/Parser/Parser.php b/CRM/Bic/Parser/Parser.php
index b577d47..7075eb3 100644
--- a/CRM/Bic/Parser/Parser.php
+++ b/CRM/Bic/Parser/Parser.php
@@ -23,7 +23,7 @@ abstract class CRM_Bic_Parser_Parser {
/**
* static function to instatiate the country parser
- *
+ *
* @return a parser object
*/
public static function getParser($country_code) {
@@ -32,7 +32,8 @@ public static function getParser($country_code) {
if (file_exists($parser_implementation)) {
$parser_class = 'CRM_Bic_Parser_' . $country_code;
return new $parser_class();
- } else {
+ }
+ else {
return NULL;
}
}
@@ -49,11 +50,11 @@ public static function getParserList() {
// Iterates through the CRM/Bic/Parser folder looking for country files
foreach ($iterator as $fileinfo) {
$file_name = $fileinfo->getFilename();
- $file_name_parts = explode(".", $file_name);
+ $file_name_parts = explode('.', $file_name);
- if ((end($file_name_parts) == "php") &&
+ if ((end($file_name_parts) == 'php') &&
(strlen(reset($file_name_parts)) == 2) &&
- (reset($file_name_parts) != "Parser")) {
+ (reset($file_name_parts) != 'Parser')) {
$countries[] = reset($file_name_parts);
}
}
@@ -61,16 +62,14 @@ public static function getParserList() {
return $countries;
}
-
/**
* Triggers the parser instance to prepare a full update
*
* @return array( 'count' => nr of banks found
- * 'error' => in case of an error
+ * 'error' => in case of an error
*
*/
- public abstract function update();
-
+ abstract public function update();
/**
* Extracts the national bank ID from a given IBAN
@@ -87,42 +86,43 @@ public function extractNBIDfromIBAN($iban) {
*
* @link http://en.wikipedia.org/wiki/International_Bank_Account_Number
*/
-
+
return FALSE;
}
-
/**
* Will update all entries for a given country
- *
+ *
* @param string $country
* ISO country code
- * @param array $entries
+ * @param array $entries
* a set of ['value'=>national_code, 'label'=>bank_name, 'name'=>BIC, 'description'=>optional data];
*/
protected function updateEntries($country, $entries) {
// if there are no entries given, something probably went wrong.
// As a result, all existing entries would be deleted. To prevent this, we'll throw an exception
if (empty($entries)) {
- $error_message = E::ts("No bank entries could be extracted from the source for country %1. It is possible that the source is outdated. If you can rule out network issues, please report this here: https://github.com/Project60/org.project60.bic/issues.", [1 => $country]);
- CRM_Core_Session::setStatus($error_message, E::ts("Problem with %1 Data Source", [1 => $country]), 'error');
+ $error_message = E::ts('No bank entries could be extracted from the source for country %1. It is possible that the source is outdated. If you can rule out network issues, please report this here: https://github.com/Project60/org.project60.bic/issues.', [1 => $country]);
+ CRM_Core_Session::setStatus($error_message, E::ts('Problem with %1 Data Source', [1 => $country]), 'error');
throw new CRM_Core_Exception($error_message);
}
try {
- $option_group = civicrm_api3('OptionGroup', 'getsingle', array('name' => 'bank_list'));
+ $option_group = civicrm_api3('OptionGroup', 'getsingle', ['name' => 'bank_list']);
$option_group_id = (int) $option_group['id'];
- } catch (Exception $e) {
- return $this->createError("OptionGroup not found. Reinstall extension!");
+ }
+ catch (Exception $e) {
+ return $this->createError('OptionGroup not found. Reinstall extension!');
}
// init stats
- $stats = array(
+ $stats = [
'count_processed' => 0,
'count_added' => 0,
'count_deleted' => 0,
'count_ignored' => 0,
- 'count_updated' => 0);
+ 'count_updated' => 0,
+ ];
// get all data sets
$current_data_query = "
@@ -134,16 +134,16 @@ protected function updateEntries($country, $entries) {
value LIKE '$country%'
AND
option_group_id = $option_group_id;";
- $current_data = array();
+ $current_data = [];
$query = CRM_Core_DAO::executeQuery($current_data_query);
while ($query->fetch()) {
- $current_data[$query->value] = array(
+ $current_data[$query->value] = [
'id' => $query->id,
'value' => $query->value,
'name' => $query->name,
'label' => $query->label,
- 'description' => $query->description
- );
+ 'description' => $query->description,
+ ];
}
unset($query);
@@ -154,7 +154,8 @@ protected function updateEntries($country, $entries) {
if (empty($trimmed_value) || empty($trimmed_name)) {
$stats['count_ignored'] += 1;
continue;
- } else {
+ }
+ else {
$stats['count_processed'] += 1;
}
@@ -165,19 +166,20 @@ protected function updateEntries($country, $entries) {
if (isset($current_data[$bank['value']])) {
$oldbank = $current_data[$bank['value']];
// it already exists -> update?
- if ( $bank['value'] != $oldbank['value']
- || $bank['name'] != $oldbank['name']
- || $bank['label'] != $oldbank['label']
+ if ($bank['value'] != $oldbank['value']
+ || $bank['name'] != $oldbank['name']
+ || $bank['label'] != $oldbank['label']
|| $bank['description'] != $oldbank['description']) {
-
+
// this has changed... UPDATE
$bank['id'] = $oldbank['id'];
$bank['option_group_id'] = $option_group_id;
civicrm_api3('OptionValue', 'create', $bank);
$stats['count_updated'] += 1;
- }
+ }
unset($current_data[$bank['value']]);
- } else {
+ }
+ else {
// this is new: add new option value
$bank['option_group_id'] = $option_group_id;
civicrm_api3('OptionValue', 'create', $bank);
@@ -196,11 +198,11 @@ protected function updateEntries($country, $entries) {
/**
* Download a file and return as a string
- *
+ *
* @return file content or NULL on error
*/
protected function downloadFile($url) {
- if (substr($url, 0, 1)=='/') {
+ if (substr($url, 0, 1) == '/') {
// local files we just read
return file_get_contents($url);
}
@@ -208,20 +210,21 @@ protected function downloadFile($url) {
// on other sources we use CURL
$ch = curl_init();
$timeout = 10;
- curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36");
+ curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36');
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
$data = curl_exec($ch);
$curl_errno = curl_errno($ch);
curl_close($ch);
-
+
if (!$curl_errno) {
- return $data;
- } else {
+ return $data;
+ }
+ else {
return NULL;
}
}
@@ -230,22 +233,23 @@ protected function downloadFile($url) {
* generate a compliant error reply for the updateEntries method
*/
protected function createError($message) {
- return array(
+ return [
'count' => 0,
- 'error' => $message
- );
+ 'error' => $message,
+ ];
}
/**
* generate a compliant error reply for the updateEntries method
*/
protected function createParserOutdatedError($error_message) {
- $message = ts("An error occurred while updating the bank information:
%1
", array(1=>$error_message));
- $message .= ts("Please make sure your server is connected to the internet and try again.
");
- $message .= "
";
+ $message = ts('An error occurred while updating the bank information:
%1
', [1 => $error_message]);
+ $message .= ts('Please make sure your server is connected to the internet and try again.
');
+ $message .= '
';
$message .= ts("If the problem persists, the source file (provided by the bank) might have been changed. In this case the 'Little BIC Extension' needs to be updated. Please check here for a newer release.
");
- $message .= "
";
+ $message .= '
';
$message .= ts("If you are already running the newest version, feel free to contact us via email or on GitHub. The sooner we know about this problem, the sooner it'll be fixed.
");
return $this->createError($message);
}
+
}
diff --git a/CRM/Bic/Upgrader.php b/CRM/Bic/Upgrader.php
index 2d3d0e2..2f5beca 100644
--- a/CRM/Bic/Upgrader.php
+++ b/CRM/Bic/Upgrader.php
@@ -19,30 +19,31 @@
/**
* Collection of upgrade steps.
*/
-class CRM_Bic_Upgrader extends CRM_Extension_Upgrader_Base
-{
+class CRM_Bic_Upgrader extends CRM_Extension_Upgrader_Base {
- /**
- * Make sure the option group is there
- */
- public function enable()
- {
- // check if option group is there, and create if it isn't
- try {
- $option_group = civicrm_api3('OptionGroup', 'getsingle', array('name' => 'bank_list'));
- } catch (Exception $e) {
- // group's not there yet, create:
- try {
- $option_group = civicrm_api3('OptionGroup', 'create', array(
- 'name' => 'bank_list',
- 'title' => ts('List of banks'),
- 'is_reserved' => 0,
- 'is_active' => 1,
- ));
- } catch (Exception $create_ex) {
- // TODO: more info?
- Civi::log()->warning("Couldn't create 'bank_list' OptionGroup.");
- }
- }
+ /**
+ * Make sure the option group is there
+ */
+ public function enable() {
+ // check if option group is there, and create if it isn't
+ try {
+ $option_group = civicrm_api3('OptionGroup', 'getsingle', ['name' => 'bank_list']);
}
+ catch (Exception $e) {
+ // group's not there yet, create:
+ try {
+ $option_group = civicrm_api3('OptionGroup', 'create', [
+ 'name' => 'bank_list',
+ 'title' => ts('List of banks'),
+ 'is_reserved' => 0,
+ 'is_active' => 1,
+ ]);
+ }
+ catch (Exception $create_ex) {
+ // TODO: more info?
+ Civi::log()->warning("Couldn't create 'bank_list' OptionGroup.");
+ }
+ }
+ }
+
}
diff --git a/Civi/Bic/ActionProvider/Action/LookupBic.php b/Civi/Bic/ActionProvider/Action/LookupBic.php
index 488327b..a068265 100644
--- a/Civi/Bic/ActionProvider/Action/LookupBic.php
+++ b/Civi/Bic/ActionProvider/Action/LookupBic.php
@@ -17,102 +17,98 @@
namespace Civi\Bic\ActionProvider\Action;
use Civi\ActionProvider\Action\AbstractAction;
-use \Civi\ActionProvider\Parameter\ParameterBagInterface;
-use \Civi\ActionProvider\Parameter\Specification;
-use \Civi\ActionProvider\Parameter\SpecificationBag;
+use Civi\ActionProvider\Parameter\ParameterBagInterface;
+use Civi\ActionProvider\Parameter\Specification;
+use Civi\ActionProvider\Parameter\SpecificationBag;
use CRM_Sepa_ExtensionUtil as E;
-class LookupBic extends AbstractAction
-{
+class LookupBic extends AbstractAction {
- /**
- * Returns the specification of the configuration options for the actual action.
- *
- * @return SpecificationBag specs
- */
- public function getConfigurationSpecification()
- {
- $normalise_options = [
- '0' => E::ts("No"),
- '1' => E::ts("Yes"),
- ];
+ /**
+ * Returns the specification of the configuration options for the actual action.
+ *
+ * @return \Civi\ActionProvider\Parameter\SpecificationBag specs
+ */
+ public function getConfigurationSpecification() {
+ $normalise_options = [
+ '0' => E::ts('No'),
+ '1' => E::ts('Yes'),
+ ];
- return new SpecificationBag([
- new Specification('normalise', 'Integer', E::ts('Normalise IBAN'), false, null, null, $normalise_options, false),
- ]);
- }
+ return new SpecificationBag([
+ new Specification('normalise', 'Integer', E::ts('Normalise IBAN'), FALSE, NULL, NULL, $normalise_options, FALSE),
+ ]);
+ }
- /**
- * Returns the specification of the parameters of the actual action.
- *
- * @return SpecificationBag specs
- */
- public function getParameterSpecification()
- {
- return new SpecificationBag([
- // required fields
- new Specification('iban', 'String', E::ts('IBAN'), true),
- new Specification('bic', 'String', E::ts('BIC (pass-trough if not found)'), false),
- ]);
- }
+ /**
+ * Returns the specification of the parameters of the actual action.
+ *
+ * @return \Civi\ActionProvider\Parameter\SpecificationBag specs
+ */
+ public function getParameterSpecification() {
+ return new SpecificationBag([
+ // required fields
+ new Specification('iban', 'String', E::ts('IBAN'), TRUE),
+ new Specification('bic', 'String', E::ts('BIC (pass-trough if not found)'), FALSE),
+ ]);
+ }
- /**
- * Returns the specification of the output parameters of this action.
- *
- * @return SpecificationBag specs
- */
- public function getOutputSpecification()
- {
- return new SpecificationBag([
- new Specification('iban', 'String', E::ts('IBAN'), true, null, null, null, false),
- new Specification('bic', 'String', E::ts('BIC'), false, null, null, null, false),
- new Specification('title', 'String', E::ts('Title'), false, null, null, null, false),
- new Specification('error', 'String', E::ts('Error'), false, null, null, null, false),
- ]);
- }
+ /**
+ * Returns the specification of the output parameters of this action.
+ *
+ * @return \Civi\ActionProvider\Parameter\SpecificationBag specs
+ */
+ public function getOutputSpecification() {
+ return new SpecificationBag([
+ new Specification('iban', 'String', E::ts('IBAN'), TRUE, NULL, NULL, NULL, FALSE),
+ new Specification('bic', 'String', E::ts('BIC'), FALSE, NULL, NULL, NULL, FALSE),
+ new Specification('title', 'String', E::ts('Title'), FALSE, NULL, NULL, NULL, FALSE),
+ new Specification('error', 'String', E::ts('Error'), FALSE, NULL, NULL, NULL, FALSE),
+ ]);
+ }
- /**
- * Run the action
- *
- * @param ParameterBagInterface $parameters
- * The parameters to this action.
- * @param ParameterBagInterface $output
- * The parameters this action can send back
- *
- * @return void
- */
- protected function doAction(ParameterBagInterface $parameters, ParameterBagInterface $output)
- {
- // get BIC
- $iban = $parameters->getParameter('iban');
+ /**
+ * Run the action
+ *
+ * @param \Civi\ActionProvider\Parameter\ParameterBagInterface $parameters
+ * The parameters to this action.
+ * @param \Civi\ActionProvider\Parameter\ParameterBagInterface $output
+ * The parameters this action can send back
+ *
+ * @return void
+ */
+ protected function doAction(ParameterBagInterface $parameters, ParameterBagInterface $output) {
+ // get BIC
+ $iban = $parameters->getParameter('iban');
- // normalise
- $normalise = $this->configuration->getParameter('normalise');
- if ($normalise) {
- $iban = strtoupper($iban);
- $iban = preg_replace('/\W/', '', $iban);
- }
- $output->setParameter('iban', $iban);
+ // normalise
+ $normalise = $this->configuration->getParameter('normalise');
+ if ($normalise) {
+ $iban = strtoupper($iban);
+ $iban = preg_replace('/\W/', '', $iban);
+ }
+ $output->setParameter('iban', $iban);
- // verify
- $valid = (bool) preg_match("/^[A-Z0-9]+$/", $iban);
- // FIXME: additional validation?
+ // verify
+ $valid = (bool) preg_match('/^[A-Z0-9]+$/', $iban);
+ // FIXME: additional validation?
- // look up BIC
- $lookup = \civicrm_api3('Bic', 'findbyiban', ['iban' => $iban]);
+ // look up BIC
+ $lookup = \civicrm_api3('Bic', 'findbyiban', ['iban' => $iban]);
- // act
- if (empty($lookup['bic'])) {
- // not found: pass through input BIC
- $output->setParameter('bic', $parameters->getParameter('bic'));
- $output->setParameter('error', E::ts("BIC not found/verified"));
- } else {
- // BIC found
- $output->setParameter('bic', $lookup['bic']);
- $output->setParameter('title', $lookup['title']);
- $output->setParameter('error', '');
- }
+ // act
+ if (empty($lookup['bic'])) {
+ // not found: pass through input BIC
+ $output->setParameter('bic', $parameters->getParameter('bic'));
+ $output->setParameter('error', E::ts('BIC not found/verified'));
+ }
+ else {
+ // BIC found
+ $output->setParameter('bic', $lookup['bic']);
+ $output->setParameter('title', $lookup['title']);
+ $output->setParameter('error', '');
}
+ }
}
diff --git a/Civi/Bic/ActionProvider/Action/VerifyBic.php b/Civi/Bic/ActionProvider/Action/VerifyBic.php
index 0cf3a61..3eb4f05 100644
--- a/Civi/Bic/ActionProvider/Action/VerifyBic.php
+++ b/Civi/Bic/ActionProvider/Action/VerifyBic.php
@@ -17,107 +17,104 @@
namespace Civi\Bic\ActionProvider\Action;
use Civi\ActionProvider\Action\AbstractAction;
-use \Civi\ActionProvider\Parameter\ParameterBagInterface;
-use \Civi\ActionProvider\Parameter\Specification;
-use \Civi\ActionProvider\Parameter\SpecificationBag;
+use Civi\ActionProvider\Parameter\ParameterBagInterface;
+use Civi\ActionProvider\Parameter\Specification;
+use Civi\ActionProvider\Parameter\SpecificationBag;
use CRM_Sepa_ExtensionUtil as E;
-class VerifyBIC extends AbstractAction
-{
-
- /**
- * Returns the specification of the configuration options for the actual action.
- *
- * @return SpecificationBag specs
- */
- public function getConfigurationSpecification()
- {
- $normalise_options = [
- '0' => E::ts("leave as is"),
- '1' => E::ts("normalise to upper case, no spaces"),
- ];
-
- $invalid_bic_options = [
- 'pass_trough' => E::ts("pass through as is"),
- 'erase' => E::ts("set to empty"),
- ];
-
- return new SpecificationBag([
- new Specification('normalise', 'Integer', E::ts('Normalisation'), false, null, null, $normalise_options, false),
- new Specification('invalid', 'String', E::ts('If BIC invalid'), false, null, null, $invalid_bic_options, false),
- ]);
- }
+class VerifyBIC extends AbstractAction {
+
+ /**
+ * Returns the specification of the configuration options for the actual action.
+ *
+ * @return \Civi\ActionProvider\Parameter\SpecificationBag specs
+ */
+ public function getConfigurationSpecification() {
+ $normalise_options = [
+ '0' => E::ts('leave as is'),
+ '1' => E::ts('normalise to upper case, no spaces'),
+ ];
+
+ $invalid_bic_options = [
+ 'pass_trough' => E::ts('pass through as is'),
+ 'erase' => E::ts('set to empty'),
+ ];
+
+ return new SpecificationBag([
+ new Specification('normalise', 'Integer', E::ts('Normalisation'), FALSE, NULL, NULL, $normalise_options, FALSE),
+ new Specification('invalid', 'String', E::ts('If BIC invalid'), FALSE, NULL, NULL, $invalid_bic_options, FALSE),
+ ]);
+ }
+
+ /**
+ * Returns the specification of the parameters of the actual action.
+ *
+ * @return \Civi\ActionProvider\Parameter\SpecificationBag specs
+ */
+ public function getParameterSpecification() {
+ return new SpecificationBag([
+ // required fields
+ new Specification('bic', 'String', E::ts('BIC (to check)'), TRUE),
+ ]);
+ }
- /**
- * Returns the specification of the parameters of the actual action.
- *
- * @return SpecificationBag specs
- */
- public function getParameterSpecification()
- {
- return new SpecificationBag([
- // required fields
- new Specification('bic', 'String', E::ts('BIC (to check)'), true),
- ]);
+ /**
+ * Returns the specification of the output parameters of this action.
+ *
+ * @return \Civi\ActionProvider\Parameter\SpecificationBag specs
+ */
+ public function getOutputSpecification() {
+ return new SpecificationBag([
+ new Specification('bic', 'String', E::ts('BIC'), FALSE, NULL, NULL, NULL, FALSE),
+ new Specification('error', 'String', E::ts('Error'), FALSE, NULL, NULL, NULL, FALSE),
+ ]);
+ }
+
+ /**
+ * Run the action
+ *
+ * @param \Civi\ActionProvider\Parameter\ParameterBagInterface $parameters
+ * The parameters to this action.
+ * @param \Civi\ActionProvider\Parameter\ParameterBagInterface $output
+ * The parameters this action can send back
+ *
+ * @return void
+ */
+ protected function doAction(ParameterBagInterface $parameters, ParameterBagInterface $output) {
+ // get BIC
+ $bic = $parameters->getParameter('bic');
+
+ // normalise
+ $normalise = $this->configuration->getParameter('normalise');
+ if ($normalise) {
+ $bic = strtoupper($bic);
+ $bic = preg_replace('/\s/', '', $bic);
}
- /**
- * Returns the specification of the output parameters of this action.
- *
- * @return SpecificationBag specs
- */
- public function getOutputSpecification()
- {
- return new SpecificationBag([
- new Specification('bic', 'String', E::ts('BIC'), false, null, null, null, false),
- new Specification('error', 'String', E::ts('Error'), false, null, null, null, false),
- ]);
+ // verify
+ $valid = (bool) preg_match('/^[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$/', $bic);
+
+ // apply
+ if ($valid) {
+ $output->setParameter('bic', $bic);
+ $output->setParameter('error', '');
+
}
+ else {
+ $invalid = $this->configuration->getParameter('invalid');
+ switch ($invalid) {
+ case 'pass_trough':
+ $output->setParameter('bic', $bic);
+ break;
- /**
- * Run the action
- *
- * @param ParameterBagInterface $parameters
- * The parameters to this action.
- * @param ParameterBagInterface $output
- * The parameters this action can send back
- *
- * @return void
- */
- protected function doAction(ParameterBagInterface $parameters, ParameterBagInterface $output)
- {
- // get BIC
- $bic = $parameters->getParameter('bic');
-
- // normalise
- $normalise = $this->configuration->getParameter('normalise');
- if ($normalise) {
- $bic = strtoupper($bic);
- $bic = preg_replace('/\s/', '', $bic);
- }
-
- // verify
- $valid = (bool) preg_match("/^[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$/", $bic);
-
- // apply
- if ($valid) {
- $output->setParameter('bic', $bic);
- $output->setParameter('error', '');
-
- } else {
- $invalid = $this->configuration->getParameter('invalid');
- switch ($invalid) {
- case 'pass_trough':
- $output->setParameter('bic', $bic);
- break;
-
- default:
- case 'erase':
- $output->setParameter('bic', '');
- break;
- }
- $output->setParameter('error', E::ts("BIC '%1' invalid", [1 => $bic]));
- }
+ default:
+ case 'erase':
+ $output->setParameter('bic', '');
+ break;
+ }
+ $output->setParameter('error', E::ts("BIC '%1' invalid", [1 => $bic]));
}
-}
\ No newline at end of file
+ }
+
+}
diff --git a/Civi/Bic/ContainerSpecs.php b/Civi/Bic/ContainerSpecs.php
index 268118d..91c1c23 100644
--- a/Civi/Bic/ContainerSpecs.php
+++ b/Civi/Bic/ContainerSpecs.php
@@ -22,23 +22,24 @@
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-class ContainerSpecs implements CompilerPassInterface
-{
+class ContainerSpecs implements CompilerPassInterface {
- /**
- * Register BIC Actions
- */
- public function process(ContainerBuilder $container)
- {
- if (!$container->hasDefinition('action_provider')) {
- return;
- }
- $typeFactoryDefinition = $container->getDefinition('action_provider');
- $typeFactoryDefinition->addMethodCall('addAction', ['LookupBIC', 'Civi\Bic\ActionProvider\Action\LookupBic', E::ts('Look up BIC for IBAN'), [
- \Civi\ActionProvider\Action\AbstractAction::DATA_RETRIEVAL_TAG,
- ]]);
- $typeFactoryDefinition->addMethodCall('addAction', ['VerifyBIC', 'Civi\Bic\ActionProvider\Action\VerifyBic', E::ts('Verify BIC'), [
- \Civi\ActionProvider\Action\AbstractAction::DATA_RETRIEVAL_TAG,
- ]]);
+ /**
+ * Register BIC Actions
+ */
+ public function process(ContainerBuilder $container) {
+ if (!$container->hasDefinition('action_provider')) {
+ return;
}
-}
\ No newline at end of file
+ $typeFactoryDefinition = $container->getDefinition('action_provider');
+ $typeFactoryDefinition->addMethodCall('addAction', ['LookupBIC', 'Civi\Bic\ActionProvider\Action\LookupBic', E::ts('Look up BIC for IBAN'), [
+ \Civi\ActionProvider\Action\AbstractAction::DATA_RETRIEVAL_TAG,
+ ],
+ ]);
+ $typeFactoryDefinition->addMethodCall('addAction', ['VerifyBIC', 'Civi\Bic\ActionProvider\Action\VerifyBic', E::ts('Verify BIC'), [
+ \Civi\ActionProvider\Action\AbstractAction::DATA_RETRIEVAL_TAG,
+ ],
+ ]);
+ }
+
+}
diff --git a/api/v3/Bic.php b/api/v3/Bic.php
index 3a5bbd0..e321a8d 100644
--- a/api/v3/Bic.php
+++ b/api/v3/Bic.php
@@ -16,7 +16,7 @@
/**
* API call to look up BIC codes for a given IBAN
- *
+ *
* @param 'iban' an IBAN number
*/
function civicrm_api3_bic_getfromiban($params) {
@@ -31,7 +31,7 @@ function civicrm_api3_bic_getfromiban($params) {
}
$nbids = $parser->extractNBIDfromIBAN($params['iban']);
- if ($nbids==FALSE) {
+ if ($nbids == FALSE) {
return civicrm_api3_create_error("IBAN parsing not supported for country '$country'!");
}
@@ -43,13 +43,15 @@ function civicrm_api3_bic_getfromiban($params) {
foreach ($search['values'] as $entity) {
if ($candidate == NULL) {
$candidate = $entity;
- } else {
+ }
+ else {
if ($candidate != $entity) {
// two different matches found!
Civi::log()->debug("LittleBIC: contradicting bank records detected: {$country}{$nbid}");
return civicrm_api3_create_error("Contradicting bank records detected: {$country}{$nbid}");
- } else {
+ }
+ else {
// identical records!
Civi::log()->debug("LittleBIC: duplicate bank records detected: {$country}{$nbid}");
}
@@ -60,80 +62,84 @@ function civicrm_api3_bic_getfromiban($params) {
return $candidate;
}
- } catch (Exception $e) {
+ }
+ catch (Exception $e) {
// not found? no problem, just keep looking
}
}
- return civicrm_api3_create_error("BIC for the given IBAN not found.");
+ return civicrm_api3_create_error('BIC for the given IBAN not found.');
}
/**
* API call to search BIC codes for a given IBAN
*
* other than getfromiban, this method won't return errors, if nothing was found
- *
+ *
* @param 'iban' an IBAN number
*/
function civicrm_api3_bic_findbyiban($params) {
if (empty($params['iban']) || strlen($params['iban']) < 7) {
- return civicrm_api3_create_success(array(), $params);
+ return civicrm_api3_create_success([], $params);
}
$result = civicrm_api3_bic_getfromiban($params);
if (empty($result['is_error'])) {
return $result;
- } else {
- return civicrm_api3_create_success(array(), $params);
+ }
+ else {
+ return civicrm_api3_create_success([], $params);
}
}
-
/**
* API call to look up BIC codes or national IDs
- *
+ *
* You can either provide the BIC in the 'bic' parameter,
* or you would have to give the ISO country code in 'country'
* along with the national bank ID in 'nbid'
*
*/
function civicrm_api3_bic_get($params) {
- $query = array();
+ $query = [];
if (!empty($params['bic'])) {
$query['name'] = $params['bic'];
- } elseif (!empty($params['country']) && !empty($params['nbid'])) {
+ }
+ elseif (!empty($params['country']) && !empty($params['nbid'])) {
$query['value'] = $params['country'] . $params['nbid'];
- } else {
+ }
+ else {
return civicrm_api3_create_error("You have to either provide 'bic' or 'country'+'nbid' parameters.");
}
try {
- $option_group = civicrm_api3('OptionGroup', 'getsingle', array('name' => 'bank_list'));
+ $option_group = civicrm_api3('OptionGroup', 'getsingle', ['name' => 'bank_list']);
$query['option_group_id'] = $option_group['id'];
- } catch (Exception $e) {
- return civicrm_api3_create_error("OptionGroup not found. Reinstall extension!");
+ }
+ catch (Exception $e) {
+ return civicrm_api3_create_error('OptionGroup not found. Reinstall extension!');
}
try {
- $data = array();
+ $data = [];
$option_values = civicrm_api3('OptionValue', 'get', $query);
foreach ($option_values['values'] as $key => $value) {
- $data[$key] = array(
+ $data[$key] = [
'bic' => $value['name'],
'country' => substr($value['value'], 0, 2),
'nbid' => substr($value['value'], 2),
'description' => CRM_Utils_Array::value('description', $value),
- 'title' => CRM_Utils_Array::value('label', $value)
- );
+ 'title' => CRM_Utils_Array::value('label', $value),
+ ];
}
- } catch (Exception $e) {
- return civicrm_api3_create_error("Entity does not exist.");
}
-
+ catch (Exception $e) {
+ return civicrm_api3_create_error('Entity does not exist.');
+ }
+
return civicrm_api3_create_success($data, $params);
}
-
/**
* API call to update the stored bank data
*
@@ -141,25 +147,26 @@ function civicrm_api3_bic_get($params) {
*/
function civicrm_api3_bic_update($params) {
if (empty($params['country'])) {
- return civicrm_api3_create_error("No country given");
+ return civicrm_api3_create_error('No country given');
}
- $countries = array();
- if ($params['country']=='all') {
+ $countries = [];
+ if ($params['country'] == 'all') {
$countries = CRM_Bic_Parser_Parser::getParserList();
- } else {
+ }
+ else {
$countries[] = $params['country'];
}
// now, loop through the given countries
- $result = array();
- $total_count =0;
+ $result = [];
+ $total_count = 0;
foreach ($countries as $country) {
$parser = CRM_Bic_Parser_Parser::getParser($country);
if (empty($parser)) {
return civicrm_api3_create_error("Parser for '$country' not found!");
}
-
+
// and execute update for each
// TODO: process errors
$result[$country] = $parser->update();
@@ -167,10 +174,9 @@ function civicrm_api3_bic_update($params) {
}
$null = NULL;
- return civicrm_api3_create_success($result, $params, $null, $null, $null, array('total_count' => $total_count));
+ return civicrm_api3_create_success($result, $params, $null, $null, $null, ['total_count' => $total_count]);
}
-
/**
* API call get stats about the stored banks
*
@@ -178,14 +184,15 @@ function civicrm_api3_bic_update($params) {
*/
function civicrm_api3_bic_stats($params) {
try {
- $option_group = civicrm_api3('OptionGroup', 'getsingle', array('name' => 'bank_list'));
- } catch (Exception $e) {
- return civicrm_api3_create_error("OptionGroup not found. Reinstall extension!");
+ $option_group = civicrm_api3('OptionGroup', 'getsingle', ['name' => 'bank_list']);
+ }
+ catch (Exception $e) {
+ return civicrm_api3_create_error('OptionGroup not found. Reinstall extension!');
}
$option_group_id = (int) $option_group['id'];
if (empty($option_group_id)) {
- return civicrm_api3_create_error("OptionGroup not found. Reinstall extension!");
+ return civicrm_api3_create_error('OptionGroup not found. Reinstall extension!');
}
$query = "
@@ -198,11 +205,11 @@ function civicrm_api3_bic_stats($params) {
option_group_id = $option_group_id
GROUP BY country_code;
";
- $result = array();
+ $result = [];
$query_result = CRM_Core_DAO::executeQuery($query);
while ($query_result->fetch()) {
$result[$query_result->country_code] = (int) $query_result->count;
}
-
+
return civicrm_api3_create_success($result, $params);
}
diff --git a/bic.php b/bic.php
index 97b0a6e..c3b5c36 100644
--- a/bic.php
+++ b/bic.php
@@ -2,7 +2,7 @@
require_once 'bic.civix.php';
-use \Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Implements hook_civicrm_container()
@@ -10,9 +10,9 @@
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_container/
*/
function bic_civicrm_container(ContainerBuilder $container) {
- if (class_exists('\Civi\Bic\ContainerSpecs')) {
- $container->addCompilerPass(new \Civi\Bic\ContainerSpecs());
- }
+ if (class_exists('\Civi\Bic\ContainerSpecs')) {
+ $container->addCompilerPass(new \Civi\Bic\ContainerSpecs());
+ }
}
/**
@@ -41,9 +41,9 @@ function bic_civicrm_enable() {
*/
function bic_civicrm_alterAPIPermissions($entity, $action, &$params, &$permissions) {
// TODO: adjust to correct permission
- $permissions['bic']['getfromiban'] = array('access CiviCRM');
- $permissions['bic']['findbyiban'] = array('access AJAX API');
- $permissions['bic']['get'] = array('access CiviCRM');
+ $permissions['bic']['getfromiban'] = ['access CiviCRM'];
+ $permissions['bic']['findbyiban'] = ['access AJAX API'];
+ $permissions['bic']['get'] = ['access CiviCRM'];
}
/**
@@ -53,15 +53,15 @@ function bic_civicrm_alterAPIPermissions($entity, $action, &$params, &$permissio
*
*/
function bic_civicrm_navigationMenu(&$menu) {
- _bic_civix_insert_navigation_menu($menu, 'Search', array(
- 'label' => ts('Find Banks', array('domain' => 'org.project60.bic')),
+ _bic_civix_insert_navigation_menu($menu, 'Search', [
+ 'label' => ts('Find Banks', ['domain' => 'org.project60.bic']),
'name' => 'BankLists',
'url' => 'civicrm/bicList',
'permission' => 'access CiviContribute',
'operator' => NULL,
'separator' => 2,
'active' => 1,
- ));
+ ]);
_bic_civix_navigationMenu($menu);
}
From 49f40de1d449e1341047cbc53d1e8764a105bcda Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 13:45:19 +0200
Subject: [PATCH 05/12] Manually fix remaining phpcs errors
---
CRM/Bic/Page/BicList.php | 3 +-
CRM/Bic/Page/Config.php | 3 +-
CRM/Bic/Parser/AT.php | 10 ++++---
CRM/Bic/Parser/BE.php | 9 ++----
CRM/Bic/Parser/CH.php | 4 +--
CRM/Bic/Parser/DE.php | 10 +++----
CRM/Bic/Parser/ES.php | 4 ++-
CRM/Bic/Parser/LU.php | 12 ++++----
CRM/Bic/Parser/NL.php | 9 ++----
CRM/Bic/Parser/PL.php | 4 +--
CRM/Bic/Parser/Parser.php | 29 +++++++++++++++++---
CRM/Bic/Upgrader.php | 2 ++
Civi/Bic/ActionProvider/Action/LookupBic.php | 2 ++
Civi/Bic/ActionProvider/Action/VerifyBic.php | 2 ++
Civi/Bic/ContainerSpecs.php | 27 ++++++++++++------
api/v3/Bic.php | 12 +++++---
bic.php | 24 +++++++++++++---
17 files changed, 111 insertions(+), 55 deletions(-)
diff --git a/CRM/Bic/Page/BicList.php b/CRM/Bic/Page/BicList.php
index f933ca1..cb9effa 100644
--- a/CRM/Bic/Page/BicList.php
+++ b/CRM/Bic/Page/BicList.php
@@ -1,6 +1,7 @@
createParserOutdatedError(E::ts("Source file doesn't contain Identnummer/Bankleitzahl/SWIFT-Code"));
+ return $this->createParserOutdatedError(
+ E::ts("Source file doesn't contain Identnummer/Bankleitzahl/SWIFT-Code")
+ );
}
}
else {
diff --git a/CRM/Bic/Parser/BE.php b/CRM/Bic/Parser/BE.php
index f04f37e..e874e96 100644
--- a/CRM/Bic/Parser/BE.php
+++ b/CRM/Bic/Parser/BE.php
@@ -14,18 +14,15 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
-// Include Composer's autoloader file.
-require_once __DIR__ . '/../../../vendor/autoload.php';
-
-require_once 'CRM/Bic/Parser/Parser.php';
+declare(strict_types = 1);
/**
* Abstract class defining the basis for national bank info parsers
*/
class CRM_Bic_Parser_BE extends CRM_Bic_Parser_Parser {
- static $page_url = 'https://www.nbb.be/doc/be/be/protocol/r_fulllist_of_codes_current.xls';
- static $country_code = 'BE';
+ public static $page_url = 'https://www.nbb.be/doc/be/be/protocol/r_fulllist_of_codes_current.xls';
+ public static $country_code = 'BE';
public function update() {
// First, download the file
diff --git a/CRM/Bic/Parser/CH.php b/CRM/Bic/Parser/CH.php
index 61553b7..99182be 100644
--- a/CRM/Bic/Parser/CH.php
+++ b/CRM/Bic/Parser/CH.php
@@ -14,14 +14,14 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
-require_once 'CRM/Bic/Parser/Parser.php';
+declare(strict_types = 1);
/**
* Abstract class defining the basis for national bank info parsers
*/
class CRM_Bic_Parser_CH extends CRM_Bic_Parser_Parser {
- static $page_url = 'https://api.six-group.com/api/epcd/bankmaster/v2/public/downloads/bcbankenstamm';
+ public static $page_url = 'https://api.six-group.com/api/epcd/bankmaster/v2/public/downloads/bcbankenstamm';
public function update() {
// first, download the page
diff --git a/CRM/Bic/Parser/DE.php b/CRM/Bic/Parser/DE.php
index 52d6876..04b89b9 100644
--- a/CRM/Bic/Parser/DE.php
+++ b/CRM/Bic/Parser/DE.php
@@ -15,19 +15,17 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
-// Include Composer's autoloader file.
-require_once __DIR__ . '/../../../vendor/autoload.php';
-
-require_once 'CRM/Bic/Parser/Parser.php';
+declare(strict_types = 1);
/**
* Abstract class defining the basis for national bank info parsers
*/
class CRM_Bic_Parser_DE extends CRM_Bic_Parser_Parser {
- static $page_url = 'https://www.bundesbank.de/resource/blob/926192/07561adcd024c5752c46afa317d554af/mL/blz-aktuell-csv-data.csv';
+ public static $page_url =
+ 'https://www.bundesbank.de/resource/blob/926192/07561adcd024c5752c46afa317d554af/mL/blz-aktuell-csv-data.csv';
- static $country_code = 'DE';
+ public static $country_code = 'DE';
public function update() {
// First, download the file
diff --git a/CRM/Bic/Parser/ES.php b/CRM/Bic/Parser/ES.php
index a720a28..b4a305d 100644
--- a/CRM/Bic/Parser/ES.php
+++ b/CRM/Bic/Parser/ES.php
@@ -14,12 +14,14 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
+declare(strict_types = 1);
+
/**
* Implements CRM_Bic_Parser_Parser for the Spanish case.
*/
class CRM_Bic_Parser_ES extends CRM_Bic_Parser_Parser {
- static $page_url = 'https://raw.githubusercontent.com/ixiam/sepa_bic/main/ES/bic.csv';
+ public static $page_url = 'https://raw.githubusercontent.com/ixiam/sepa_bic/main/ES/bic.csv';
public function update() {
// first, download the page
diff --git a/CRM/Bic/Parser/LU.php b/CRM/Bic/Parser/LU.php
index 976fb62..13c68bd 100644
--- a/CRM/Bic/Parser/LU.php
+++ b/CRM/Bic/Parser/LU.php
@@ -14,18 +14,18 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
-// Include Composer's autoloader file.
-require_once __DIR__ . '/../../../vendor/autoload.php';
-
-require_once 'CRM/Bic/Parser/Parser.php';
+declare(strict_types = 1);
/**
* Abstract class defining the basis for national bank info parsers
*/
class CRM_Bic_Parser_LU extends CRM_Bic_Parser_Parser {
- static $page_url = 'https://www.abbl.lu/media/file/global/dynamic/c0f7906fec3b33783c9f73c1f6109afdbbc9a66c/Luxembourg%20Register%20of%20IBAN-BIC%20Codes-01.12.2022.xlsx';
- static $country_code = 'LU';
+ // phpcs:disable Generic.Files.LineLength.TooLong
+ public static $page_url =
+ 'https://www.abbl.lu/media/file/global/dynamic/c0f7906fec3b33783c9f73c1f6109afdbbc9a66c/Luxembourg%20Register%20of%20IBAN-BIC%20Codes-01.12.2022.xlsx';
+ // phpcs:enable
+ public static $country_code = 'LU';
public function update() {
// First, download the file
diff --git a/CRM/Bic/Parser/NL.php b/CRM/Bic/Parser/NL.php
index d53a707..84b3c80 100644
--- a/CRM/Bic/Parser/NL.php
+++ b/CRM/Bic/Parser/NL.php
@@ -14,19 +14,16 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
-// Include Composer's autoloader file.
-require_once __DIR__ . '/../../../vendor/autoload.php';
-
-require_once 'CRM/Bic/Parser/Parser.php';
+declare(strict_types = 1);
/**
* Abstract class defining the basis for national bank info parsers
*/
class CRM_Bic_Parser_NL extends CRM_Bic_Parser_Parser {
- static $page_url = 'https://www.betaalvereniging.nl/wp-content/uploads/BIC-lijst-NL.xlsx';
+ public static $page_url = 'https://www.betaalvereniging.nl/wp-content/uploads/BIC-lijst-NL.xlsx';
- static $country_code = 'NL';
+ public static $country_code = 'NL';
public function update() {
// First, download the file
diff --git a/CRM/Bic/Parser/PL.php b/CRM/Bic/Parser/PL.php
index e4fd188..5089286 100644
--- a/CRM/Bic/Parser/PL.php
+++ b/CRM/Bic/Parser/PL.php
@@ -15,7 +15,7 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
-require_once 'CRM/Bic/Parser/Parser.php';
+declare(strict_types = 1);
/**
* Implementation of abstract class defining the basis for national bank info parsers, Polish banks
@@ -23,7 +23,7 @@
class CRM_Bic_Parser_PL extends CRM_Bic_Parser_Parser {
// official source of Polish National Bank
- static $page_url = 'https://ewib.nbp.pl/plewibnra?dokNazwa=plewibnra.txt';
+ public static $page_url = 'https://ewib.nbp.pl/plewibnra?dokNazwa=plewibnra.txt';
public function update() {
// first, download the page, it's a CSV file, so more convenient not to use built in method
diff --git a/CRM/Bic/Parser/Parser.php b/CRM/Bic/Parser/Parser.php
index 7075eb3..7b86547 100644
--- a/CRM/Bic/Parser/Parser.php
+++ b/CRM/Bic/Parser/Parser.php
@@ -14,12 +14,16 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
+declare(strict_types = 1);
+
use CRM_Bic_ExtensionUtil as E;
/**
* Abstract class defining the basis for national bank info parsers
*/
+// phpcs:disable Generic.NamingConventions.AbstractClassNamePrefix.Missing
abstract class CRM_Bic_Parser_Parser {
+// phpcs:enable
/**
* static function to instatiate the country parser
@@ -102,7 +106,12 @@ protected function updateEntries($country, $entries) {
// if there are no entries given, something probably went wrong.
// As a result, all existing entries would be deleted. To prevent this, we'll throw an exception
if (empty($entries)) {
- $error_message = E::ts('No bank entries could be extracted from the source for country %1. It is possible that the source is outdated. If you can rule out network issues, please report this here: https://github.com/Project60/org.project60.bic/issues.', [1 => $country]);
+ // phpcs:disable Generic.Files.LineLength.TooLong
+ $error_message = E::ts(
+ 'No bank entries could be extracted from the source for country %1. It is possible that the source is outdated. If you can rule out network issues, please report this here: https://github.com/Project60/org.project60.bic/issues.',
+ [1 => $country]
+ );
+ // phpcs:enable
CRM_Core_Session::setStatus($error_message, E::ts('Problem with %1 Data Source', [1 => $country]), 'error');
throw new CRM_Core_Exception($error_message);
}
@@ -210,7 +219,11 @@ protected function downloadFile($url) {
// on other sources we use CURL
$ch = curl_init();
$timeout = 10;
- curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36');
+ curl_setopt(
+ $ch,
+ CURLOPT_USERAGENT,
+ 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36'
+ );
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
@@ -246,9 +259,17 @@ protected function createParserOutdatedError($error_message) {
$message = ts('An error occurred while updating the bank information:
%1
', [1 => $error_message]);
$message .= ts('Please make sure your server is connected to the internet and try again.
');
$message .= '
';
- $message .= ts("If the problem persists, the source file (provided by the bank) might have been changed. In this case the 'Little BIC Extension' needs to be updated. Please check here for a newer release.
");
+ // phpcs:disable Generic.Files.LineLength.TooLong
+ $message .= ts(
+ "If the problem persists, the source file (provided by the bank) might have been changed. In this case the 'Little BIC Extension' needs to be updated. Please check here for a newer release.
"
+ );
+ // phpcs:enable
$message .= '
';
- $message .= ts("If you are already running the newest version, feel free to contact us via email or on GitHub. The sooner we know about this problem, the sooner it'll be fixed.
");
+ // phpcs:disable Generic.Files.LineLength.TooLong
+ $message .= ts(
+ "If you are already running the newest version, feel free to contact us via email or on GitHub. The sooner we know about this problem, the sooner it'll be fixed.
"
+ );
+ // phpcs:enable
return $this->createError($message);
}
diff --git a/CRM/Bic/Upgrader.php b/CRM/Bic/Upgrader.php
index 2f5beca..6645d9f 100644
--- a/CRM/Bic/Upgrader.php
+++ b/CRM/Bic/Upgrader.php
@@ -14,6 +14,8 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
+declare(strict_types = 1);
+
use CRM_Bic_ExtensionUtil as E;
/**
diff --git a/Civi/Bic/ActionProvider/Action/LookupBic.php b/Civi/Bic/ActionProvider/Action/LookupBic.php
index a068265..e415d86 100644
--- a/Civi/Bic/ActionProvider/Action/LookupBic.php
+++ b/Civi/Bic/ActionProvider/Action/LookupBic.php
@@ -14,6 +14,8 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
+declare(strict_types = 1);
+
namespace Civi\Bic\ActionProvider\Action;
use Civi\ActionProvider\Action\AbstractAction;
diff --git a/Civi/Bic/ActionProvider/Action/VerifyBic.php b/Civi/Bic/ActionProvider/Action/VerifyBic.php
index 3eb4f05..68dd866 100644
--- a/Civi/Bic/ActionProvider/Action/VerifyBic.php
+++ b/Civi/Bic/ActionProvider/Action/VerifyBic.php
@@ -14,6 +14,8 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
+declare(strict_types = 1);
+
namespace Civi\Bic\ActionProvider\Action;
use Civi\ActionProvider\Action\AbstractAction;
diff --git a/Civi/Bic/ContainerSpecs.php b/Civi/Bic/ContainerSpecs.php
index 91c1c23..5f6d0bb 100644
--- a/Civi/Bic/ContainerSpecs.php
+++ b/Civi/Bic/ContainerSpecs.php
@@ -14,6 +14,7 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
+declare(strict_types = 1);
namespace Civi\Bic;
@@ -32,14 +33,24 @@ public function process(ContainerBuilder $container) {
return;
}
$typeFactoryDefinition = $container->getDefinition('action_provider');
- $typeFactoryDefinition->addMethodCall('addAction', ['LookupBIC', 'Civi\Bic\ActionProvider\Action\LookupBic', E::ts('Look up BIC for IBAN'), [
- \Civi\ActionProvider\Action\AbstractAction::DATA_RETRIEVAL_TAG,
- ],
- ]);
- $typeFactoryDefinition->addMethodCall('addAction', ['VerifyBIC', 'Civi\Bic\ActionProvider\Action\VerifyBic', E::ts('Verify BIC'), [
- \Civi\ActionProvider\Action\AbstractAction::DATA_RETRIEVAL_TAG,
- ],
- ]);
+ $typeFactoryDefinition->addMethodCall(
+ 'addAction',
+ [
+ 'LookupBIC',
+ 'Civi\Bic\ActionProvider\Action\LookupBic',
+ E::ts('Look up BIC for IBAN'),
+ [\Civi\ActionProvider\Action\AbstractAction::DATA_RETRIEVAL_TAG],
+ ]
+ );
+ $typeFactoryDefinition->addMethodCall(
+ 'addAction',
+ [
+ 'VerifyBIC',
+ 'Civi\Bic\ActionProvider\Action\VerifyBic',
+ E::ts('Verify BIC'),
+ [\Civi\ActionProvider\Action\AbstractAction::DATA_RETRIEVAL_TAG],
+ ]
+ );
}
}
diff --git a/api/v3/Bic.php b/api/v3/Bic.php
index e321a8d..66033e6 100644
--- a/api/v3/Bic.php
+++ b/api/v3/Bic.php
@@ -14,10 +14,12 @@
| written permission from the original author(s). |
+--------------------------------------------------------*/
+declare(strict_types = 1);
+
/**
* API call to look up BIC codes for a given IBAN
*
- * @param 'iban' an IBAN number
+ * @phpstan-param array{iban: string} $params
*/
function civicrm_api3_bic_getfromiban($params) {
if (empty($params['iban'])) {
@@ -76,7 +78,7 @@ function civicrm_api3_bic_getfromiban($params) {
*
* other than getfromiban, this method won't return errors, if nothing was found
*
- * @param 'iban' an IBAN number
+ * @phpstan-param array{iban: string} $params
*/
function civicrm_api3_bic_findbyiban($params) {
if (empty($params['iban']) || strlen($params['iban']) < 7) {
@@ -143,7 +145,9 @@ function civicrm_api3_bic_get($params) {
/**
* API call to update the stored bank data
*
- * @param 'country' country code to update or 'all'
+ * @phpstan-param array{
+ * country: string, # country code to update or 'all'
+ * } $params
*/
function civicrm_api3_bic_update($params) {
if (empty($params['country'])) {
@@ -201,7 +205,7 @@ function civicrm_api3_bic_stats($params) {
COUNT(value) AS count
FROM
civicrm_option_value
- WHERE
+ WHERE
option_group_id = $option_group_id
GROUP BY country_code;
";
diff --git a/bic.php b/bic.php
index c3b5c36..424d4d0 100644
--- a/bic.php
+++ b/bic.php
@@ -1,42 +1,58 @@
addCompilerPass(new \Civi\Bic\ContainerSpecs());
}
}
/**
- * Implementation of hook_civicrm_config
+ * Implements hook_civicrm_config().
*/
function bic_civicrm_config(&$config) {
+ _bic_composer_autoload();
+
_bic_civix_civicrm_config($config);
}
/**
- * Implementation of hook_civicrm_install
+ * Implements hook_civicrm_install().
*/
function bic_civicrm_install() {
return _bic_civix_civicrm_install();
}
/**
- * Implementation of hook_civicrm_enable
+ * Implements hook_civicrm_enable().
*/
function bic_civicrm_enable() {
return _bic_civix_civicrm_enable();
}
/**
+ * Implements hook_civicrm_alterAPIPersmissions().
+ *
* Set permissions for runner/engine API call
*/
function bic_civicrm_alterAPIPermissions($entity, $action, &$params, &$permissions) {
From 0c740a7f2e5877072effe76696b31205a5df221d Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 13:55:36 +0200
Subject: [PATCH 06/12] Fix PHPStan level 0 errors, mostly related to unknown
classes
---
Civi/Bic/ActionProvider/Action/LookupBic.php | 3 +--
Civi/Bic/ActionProvider/Action/VerifyBic.php | 3 +--
bic.php | 8 ++++----
phpstan.ci.neon | 1 +
phpstan.neon.dist | 2 ++
phpstan.neon.template | 1 +
6 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/Civi/Bic/ActionProvider/Action/LookupBic.php b/Civi/Bic/ActionProvider/Action/LookupBic.php
index e415d86..68d84a6 100644
--- a/Civi/Bic/ActionProvider/Action/LookupBic.php
+++ b/Civi/Bic/ActionProvider/Action/LookupBic.php
@@ -18,13 +18,12 @@
namespace Civi\Bic\ActionProvider\Action;
+use CRM_Bic_ExtensionUtil as E;
use Civi\ActionProvider\Action\AbstractAction;
use Civi\ActionProvider\Parameter\ParameterBagInterface;
use Civi\ActionProvider\Parameter\Specification;
use Civi\ActionProvider\Parameter\SpecificationBag;
-use CRM_Sepa_ExtensionUtil as E;
-
class LookupBic extends AbstractAction {
/**
diff --git a/Civi/Bic/ActionProvider/Action/VerifyBic.php b/Civi/Bic/ActionProvider/Action/VerifyBic.php
index 68dd866..5ebab4a 100644
--- a/Civi/Bic/ActionProvider/Action/VerifyBic.php
+++ b/Civi/Bic/ActionProvider/Action/VerifyBic.php
@@ -18,13 +18,12 @@
namespace Civi\Bic\ActionProvider\Action;
+use CRM_Bic_ExtensionUtil as E;
use Civi\ActionProvider\Action\AbstractAction;
use Civi\ActionProvider\Parameter\ParameterBagInterface;
use Civi\ActionProvider\Parameter\Specification;
use Civi\ActionProvider\Parameter\SpecificationBag;
-use CRM_Sepa_ExtensionUtil as E;
-
class VerifyBIC extends AbstractAction {
/**
diff --git a/bic.php b/bic.php
index 424d4d0..cc5cf30 100644
--- a/bic.php
+++ b/bic.php
@@ -6,6 +6,7 @@
require_once 'bic.civix.php';
// phpcs:enable
+use CRM_Bic_ExtensionUtil as E;
use Symfony\Component\DependencyInjection\ContainerBuilder;
function _bic_composer_autoload(): void {
@@ -32,7 +33,6 @@ function bic_civicrm_container(ContainerBuilder $container) {
*/
function bic_civicrm_config(&$config) {
_bic_composer_autoload();
-
_bic_civix_civicrm_config($config);
}
@@ -40,14 +40,14 @@ function bic_civicrm_config(&$config) {
* Implements hook_civicrm_install().
*/
function bic_civicrm_install() {
- return _bic_civix_civicrm_install();
+ _bic_civix_civicrm_install();
}
/**
* Implements hook_civicrm_enable().
*/
function bic_civicrm_enable() {
- return _bic_civix_civicrm_enable();
+ _bic_civix_civicrm_enable();
}
/**
@@ -70,7 +70,7 @@ function bic_civicrm_alterAPIPermissions($entity, $action, &$params, &$permissio
*/
function bic_civicrm_navigationMenu(&$menu) {
_bic_civix_insert_navigation_menu($menu, 'Search', [
- 'label' => ts('Find Banks', ['domain' => 'org.project60.bic']),
+ 'label' => E::ts('Find Banks', ['domain' => 'org.project60.bic']),
'name' => 'BankLists',
'url' => 'civicrm/bicList',
'permission' => 'access CiviContribute',
diff --git a/phpstan.ci.neon b/phpstan.ci.neon
index 883179f..bd8e612 100644
--- a/phpstan.ci.neon
+++ b/phpstan.ci.neon
@@ -3,6 +3,7 @@ includes:
parameters:
scanDirectories:
+ - ci/vendor/civicrm/civicrm-core/api/
- ci/vendor/civicrm/civicrm-core/CRM/
bootstrapFiles:
- ci/vendor/autoload.php
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 8d641f6..7c5a0b0 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -14,6 +14,8 @@ parameters:
- tools/phpunit/vendor/bin/.phpunit/phpunit/src/Framework/TestCase.php
scanDirectories:
- tools/phpunit/vendor/bin/.phpunit/phpunit/src/Framework
+ - ../action-provider/Civi/
+ - ../org.project60.sepa/CRM/
bootstrapFiles:
- tools/phpunit/vendor/bin/.phpunit/phpunit/vendor/autoload.php
- phpstanBootstrap.php
diff --git a/phpstan.neon.template b/phpstan.neon.template
index 34f6dfd..ccdc8d4 100644
--- a/phpstan.neon.template
+++ b/phpstan.neon.template
@@ -6,6 +6,7 @@ includes:
parameters:
scanDirectories:
+ - {VENDOR_DIR}/civicrm/civicrm-core/api/
- {VENDOR_DIR}/civicrm/civicrm-core/CRM/
bootstrapFiles:
- {VENDOR_DIR}/autoload.php
From 361731f4dda21c357880e71ea72f89333f0b50b1 Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 13:57:24 +0200
Subject: [PATCH 07/12] Add PHPStan baseline for > level 0 errors
---
phpstan-baseline.neon | 938 ++++++++++++++++++++++++++++++++++++++++++
phpstan.neon.dist | 3 +
2 files changed, 941 insertions(+)
create mode 100644 phpstan-baseline.neon
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
new file mode 100644
index 0000000..3c5193c
--- /dev/null
+++ b/phpstan-baseline.neon
@@ -0,0 +1,938 @@
+parameters:
+ ignoreErrors:
+ -
+ message: '#^Cannot access offset ''values'' on array\|int\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: CRM/Bic/Page/BicList.php
+
+ -
+ message: '#^Method CRM_Bic_Page_BicList\:\:run\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Page/BicList.php
+
+ -
+ message: '#^Offset \(int\|string\) might not exist on array\|null\.$#'
+ identifier: offsetAccess.notFound
+ count: 1
+ path: CRM/Bic/Page/BicList.php
+
+ -
+ message: '#^Only booleans are allowed in an if condition, list\|null given\.$#'
+ identifier: if.condNotBoolean
+ count: 1
+ path: CRM/Bic/Page/BicList.php
+
+ -
+ message: '#^Variable \$default_country might not be defined\.$#'
+ identifier: variable.undefined
+ count: 1
+ path: CRM/Bic/Page/BicList.php
+
+ -
+ message: '#^Cannot access offset ''values'' on array\|int\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: CRM/Bic/Page/Config.php
+
+ -
+ message: '#^Iterating over an object of an unknown class an\.$#'
+ identifier: class.notFound
+ count: 2
+ path: CRM/Bic/Page/Config.php
+
+ -
+ message: '#^Method CRM_Bic_Page_Config\:\:run\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Page/Config.php
+
+ -
+ message: '#^Offset \(int\|string\) might not exist on array\|null\.$#'
+ identifier: offsetAccess.notFound
+ count: 1
+ path: CRM/Bic/Page/Config.php
+
+ -
+ message: '#^Call to function in_array\(\) requires parameter \#3 to be set\.$#'
+ identifier: function.strict
+ count: 3
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 7
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_AT\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_AT\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_AT\:\:getDescription\(\) has parameter \$data_set with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_AT\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Parameter \#1 \$keys of function array_combine expects array\, list\ given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Parameter \#1 \$string of function str_getcsv expects string, string\|false given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Parameter \#2 \$subject of function preg_split expects string, file given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_AT\:\:\$page_url has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/AT.php
+
+ -
+ message: '#^Call to function in_array\(\) requires parameter \#3 to be set\.$#'
+ identifier: function.strict
+ count: 1
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 3
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Implicit array creation is not allowed \- variable \$banks does not exist\.$#'
+ identifier: variable.implicitArray
+ count: 1
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Loose comparison via "\=\=" is not allowed\.$#'
+ identifier: equal.notAllowed
+ count: 1
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_BE\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_BE\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_BE\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_BE\:\:\$country_code has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_BE\:\:\$page_url has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/BE.php
+
+ -
+ message: '#^Argument of an invalid type list\\|false supplied for foreach, only iterables are supported\.$#'
+ identifier: foreach.nonIterable
+ count: 1
+ path: CRM/Bic/Parser/CH.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 2
+ path: CRM/Bic/Parser/CH.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_CH\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/CH.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_CH\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/CH.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_CH\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/CH.php
+
+ -
+ message: '#^Parameter \#2 \$subject of function preg_split expects string, file given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/CH.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_CH\:\:\$page_url has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/CH.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 2
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^Loose comparison via "\!\=" is not allowed\.$#'
+ identifier: notEqual.notAllowed
+ count: 1
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_DE\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_DE\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_DE\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^NotEqual\: Condition between 1 and string\|null are falsy, please do not mix types\.$#'
+ identifier: voku.NotEqual
+ count: 1
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^Parameter \#1 \$string of function mb_convert_encoding expects array\|string, string\|null given\.$#'
+ identifier: argument.type
+ count: 3
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_DE\:\:\$country_code has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_DE\:\:\$page_url has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/DE.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 3
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_ES\:\:csvLineToArray\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_ES\:\:csvLineToArray\(\) has parameter \$str with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_ES\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_ES\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_ES\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Parameter \#2 \$subject of function preg_split expects string, file given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Parameter \#3 \$subject of function preg_replace expects array\\|string, list\\|false given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_ES\:\:\$page_url has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/ES.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 1
+ path: CRM/Bic/Parser/LU.php
+
+ -
+ message: '#^Implicit array creation is not allowed \- variable \$banks does not exist\.$#'
+ identifier: variable.implicitArray
+ count: 1
+ path: CRM/Bic/Parser/LU.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_LU\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/LU.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_LU\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/LU.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_LU\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/LU.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_LU\:\:\$country_code has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/LU.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_LU\:\:\$page_url has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/LU.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 1
+ path: CRM/Bic/Parser/NL.php
+
+ -
+ message: '#^Implicit array creation is not allowed \- variable \$banks does not exist\.$#'
+ identifier: variable.implicitArray
+ count: 1
+ path: CRM/Bic/Parser/NL.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_NL\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/NL.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_NL\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/NL.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_NL\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/NL.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_NL\:\:\$country_code has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/NL.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_NL\:\:\$page_url has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/NL.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 2
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Implicit array creation is not allowed \- variable \$banks might not exist\.$#'
+ identifier: variable.implicitArray
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_PL\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_PL\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_PL\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(string\|null\)\: mixed\)\|null, ''trim'' given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Parameter \#1 \$string of function str_getcsv expects string, string\|false given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Property CRM_Bic_Parser_PL\:\:\$page_url has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Variable \$banks might not be defined\.$#'
+ identifier: variable.undefined
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Variable \$count in empty\(\) always exists and is not falsy\.$#'
+ identifier: empty.variable
+ count: 1
+ path: CRM/Bic/Parser/PL.php
+
+ -
+ message: '#^Access to an undefined property object\:\:\$description\.$#'
+ identifier: property.notFound
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Access to an undefined property object\:\:\$id\.$#'
+ identifier: property.notFound
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Access to an undefined property object\:\:\$label\.$#'
+ identifier: property.notFound
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Access to an undefined property object\:\:\$name\.$#'
+ identifier: property.notFound
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Access to an undefined property object\:\:\$value\.$#'
+ identifier: property.notFound
+ count: 2
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Call to an undefined method object\:\:fetch\(\)\.$#'
+ identifier: method.notFound
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Cannot access offset ''id'' on array\|int\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 3
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Implicit array creation is not allowed \- variable \$countries might not exist\.$#'
+ identifier: variable.implicitArray
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^In method "CRM_Bic_Parser_Parser\:\:updateEntries", caught "Exception" must be rethrown\. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception\. More info\: http\://bit\.ly/failloud$#'
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Loose comparison via "\!\=" is not allowed\.$#'
+ identifier: notEqual.notAllowed
+ count: 5
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Loose comparison via "\=\=" is not allowed\.$#'
+ identifier: equal.notAllowed
+ count: 3
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:createError\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:createError\(\) has parameter \$message with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:createParserOutdatedError\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:createParserOutdatedError\(\) has parameter \$error_message with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:downloadFile\(\) has invalid return type file\.$#'
+ identifier: class.notFound
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:downloadFile\(\) has parameter \$url with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:downloadFile\(\) should return file but returns bool\|string\.$#'
+ identifier: return.type
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:downloadFile\(\) should return file but returns null\.$#'
+ identifier: return.type
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:downloadFile\(\) should return file but returns string\|false\.$#'
+ identifier: return.type
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:extractNBIDfromIBAN\(\) has parameter \$iban with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:extractNBIDfromIBAN\(\) return type has no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:extractNBIDfromIBAN\(\) should return array but returns false\.$#'
+ identifier: return.type
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:getParser\(\) has invalid return type a\.$#'
+ identifier: class.notFound
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:getParser\(\) has parameter \$country_code with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:getParser\(\) should return a but returns null\.$#'
+ identifier: return.type
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:getParser\(\) should return a but returns object\.$#'
+ identifier: return.type
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:getParserList\(\) has invalid return type an\.$#'
+ identifier: class.notFound
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:getParserList\(\) should return an but returns list\\.$#'
+ identifier: return.type
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:updateEntries\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Method CRM_Bic_Parser_Parser\:\:updateEntries\(\) has parameter \$entries with no value type specified in iterable type array\.$#'
+ identifier: missingType.iterableValue
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Only booleans are allowed in a negated boolean, int given\.$#'
+ identifier: booleanNot.exprNotBoolean
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '''
+ #^PHPDoc tag @return has invalid value \(array\( 'count' \=\> nr of banks found
+ 'error' \=\> in case of an error\)\: Unexpected token "\(", expected TOKEN_HORIZONTAL_WS at offset 86 on line 4$#
+ '''
+ identifier: phpDoc.parseError
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Parameter \#3 \$value of function curl_setopt expects bool, int given\.$#'
+ identifier: argument.type
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^Variable \$countries might not be defined\.$#'
+ identifier: variable.undefined
+ count: 1
+ path: CRM/Bic/Parser/Parser.php
+
+ -
+ message: '#^In method "CRM_Bic_Upgrader\:\:enable", caught "Exception" must be rethrown\. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception\. More info\: http\://bit\.ly/failloud$#'
+ count: 2
+ path: CRM/Bic/Upgrader.php
+
+ -
+ message: '#^Method CRM_Bic_Upgrader\:\:enable\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: CRM/Bic/Upgrader.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 1
+ path: Civi/Bic/ActionProvider/Action/LookupBic.php
+
+ -
+ message: '#^Access to an undefined property object\:\:\$count\.$#'
+ identifier: property.notFound
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Access to an undefined property object\:\:\$country_code\.$#'
+ identifier: property.notFound
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Argument of an invalid type an\|list\ supplied for foreach, only iterables are supported\.$#'
+ identifier: foreach.nonIterable
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Call to an undefined method object\:\:fetch\(\)\.$#'
+ identifier: method.notFound
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '''
+ #^Call to deprecated method value\(\) of class CRM_Utils_Array\:
+ In most cases this can be replaced with
+ \$list\[\$key\] \?\? \$default
+ with the minor difference that when \$list\[\$key\] exists and is NULL, this function will always
+ return NULL\.$#
+ '''
+ identifier: staticMethod.deprecated
+ count: 2
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Call to method extractNBIDfromIBAN\(\) on an unknown class a\.$#'
+ identifier: class.notFound
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Call to method update\(\) on an unknown class a\.$#'
+ identifier: class.notFound
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Cannot access offset ''id'' on array\|int\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Cannot access offset ''values'' on array\|int\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
+ identifier: empty.notAllowed
+ count: 10
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Empty catch block\.$#'
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_findbyiban\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_get\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_get\(\) has parameter \$params with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_getfromiban\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_stats\(\) has invalid return type a\.$#'
+ identifier: class.notFound
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_stats\(\) has parameter \$params with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_stats\(\) should return a but returns array\.$#'
+ identifier: return.type
+ count: 3
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_update\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function civicrm_api3_bic_update\(\) has parameter \$params with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^In function "civicrm_api3_bic_get", caught "Exception" must be rethrown\. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception\. More info\: http\://bit\.ly/failloud$#'
+ count: 2
+ path: api/v3/Bic.php
+
+ -
+ message: '#^In function "civicrm_api3_bic_getfromiban", caught "Exception" must be rethrown\. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception\. More info\: http\://bit\.ly/failloud$#'
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^In function "civicrm_api3_bic_stats", caught "Exception" must be rethrown\. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception\. More info\: http\://bit\.ly/failloud$#'
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Loose comparison via "\!\=" is not allowed\.$#'
+ identifier: notEqual.notAllowed
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Loose comparison via "\=\=" is not allowed\.$#'
+ identifier: equal.notAllowed
+ count: 3
+ path: api/v3/Bic.php
+
+ -
+ message: '''
+ #^PHPDoc tag @phpstan\-param has invalid value \(array\{
+ country\: string, \# country code to update or 'all'
+ \} \$params\)\: Unexpected token "\#", expected type at offset 97 on line 5$#
+ '''
+ identifier: phpDoc.parseError
+ count: 1
+ path: api/v3/Bic.php
+
+ -
+ message: '#^Function bic_civicrm_alterAPIPermissions\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_alterAPIPermissions\(\) has parameter \$action with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_alterAPIPermissions\(\) has parameter \$entity with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_alterAPIPermissions\(\) has parameter \$params with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_alterAPIPermissions\(\) has parameter \$permissions with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_config\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_config\(\) has parameter \$config with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_container\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_enable\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_install\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_navigationMenu\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: bic.php
+
+ -
+ message: '#^Function bic_civicrm_navigationMenu\(\) has parameter \$menu with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: bic.php
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 7c5a0b0..e5ab3c7 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -1,3 +1,6 @@
+includes:
+ - phpstan-baseline.neon
+
parameters:
paths:
- api
From e9becf56f2a8a0b1a8b20d2a81dc42745b13da50 Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 14:01:37 +0200
Subject: [PATCH 08/12] Clone (soft) dependency extensions for PHPStan on
GitHub
---
.github/workflows/phpstan.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml
index efc0b1d..7d62b82 100644
--- a/.github/workflows/phpstan.yml
+++ b/.github/workflows/phpstan.yml
@@ -47,7 +47,9 @@ jobs:
composer update --no-progress --prefer-dist --${{ matrix.prefer }} --optimize-autoloader &&
composer composer-phpunit -- update --no-progress --prefer-dist &&
composer composer-phpstan -- update --no-progress --prefer-dist --optimize-autoloader &&
- composer --working-dir=ci update --no-progress --prefer-dist --${{ matrix.prefer }} --ignore-platform-req=ext-gd
+ composer --working-dir=ci update --no-progress --prefer-dist --${{ matrix.prefer }} --ignore-platform-req=ext-gd &&
+ git clone --depth=1 https://lab.civicrm.org/extensions/action-provider.git ../action-provider &&
+ git clone --depth=1 https://github.com/Project60/org.project60.sepa.git ../org.project60.sepa
- name: Run PHPStan
run: composer phpstan -- analyse -c phpstan.ci.neon
From 50cf331867be1bb7419c793746dea4d0c6335202 Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 15:14:51 +0200
Subject: [PATCH 09/12] Fix method docblock
---
Civi/Bic/ContainerSpecs.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Civi/Bic/ContainerSpecs.php b/Civi/Bic/ContainerSpecs.php
index 5f6d0bb..9a336ed 100644
--- a/Civi/Bic/ContainerSpecs.php
+++ b/Civi/Bic/ContainerSpecs.php
@@ -26,7 +26,7 @@
class ContainerSpecs implements CompilerPassInterface {
/**
- * Register BIC Actions
+ * {@inheritDoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('action_provider')) {
From 325686b5a091867b547dcbd01c147a5a1e77bb43 Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 15:32:10 +0200
Subject: [PATCH 10/12] Remove empty docs index
---
docs/index.md | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 docs/index.md
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index e10b99d..0000000
--- a/docs/index.md
+++ /dev/null
@@ -1 +0,0 @@
-# Introduction
From 5ba93d5198a8961ca0670ecdfe529f1d079238e1 Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 15:33:12 +0200
Subject: [PATCH 11/12] Add docs index as symlink to README
---
docs/index.md | 1 +
1 file changed, 1 insertion(+)
create mode 120000 docs/index.md
diff --git a/docs/index.md b/docs/index.md
new file mode 120000
index 0000000..32d46ee
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1 @@
+../README.md
\ No newline at end of file
From efcbc46433f850ae57c44470ca6600e83a03a611 Mon Sep 17 00:00:00 2001
From: Jens Schuppe
Date: Tue, 16 Sep 2025 15:33:37 +0200
Subject: [PATCH 12/12] Update baseline
---
phpstan-baseline.neon | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 3c5193c..2e810f7 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -703,6 +703,12 @@ parameters:
count: 1
path: Civi/Bic/ActionProvider/Action/LookupBic.php
+ -
+ message: '#^Method Civi\\Bic\\ContainerSpecs\:\:process\(\) has no return type specified\.$#'
+ identifier: missingType.return
+ count: 1
+ path: Civi/Bic/ContainerSpecs.php
+
-
message: '#^Access to an undefined property object\:\:\$count\.$#'
identifier: property.notFound