Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ version: 2.1
workflows:
php-tests:
jobs:
# The only job that needs no Postmark credentials, so it is the only one that
# gives a fork PR any signal at all. Everything below is an integration suite
# against the live API and cannot start without the tokens above.
- static-analysis:
name: static
- unit-tests:
name: php81
version: "8.1"
Expand All @@ -25,6 +30,26 @@ workflows:
- php83

jobs:
static-analysis:
docker:
- image: cimg/php:8.1
steps:
- checkout
- run:
name: Install dependencies
command: composer install --no-interaction
- run:
name: PHPStan
command: vendor/bin/phpstan analyse --memory-limit=1G --no-progress
# php-cs-fixer is deliberately NOT a gate yet: its config was missing
# setFinder(), so the fixer has never run here and 8 files are already
# non-conforming on main. A check that ships red just teaches people to
# ignore it. Run it for visibility, promote it to a gate after a
# formatting pass.
- run:
name: php-cs-fixer (advisory)
command: vendor/bin/php-cs-fixer fix --dry-run --diff || true

unit-tests:
parameters:
version:
Expand Down
1 change: 1 addition & 0 deletions .php-cs-fixer.cache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"php":"8.4.4","version":"3.82.2:v3.82.2#684ed3ab41008a2a4848de8bde17eb168c596247","indent":" ","lineEnding":"\n","rules":{"blank_line_before_statement":{"statements":["break","case","continue","declare","default","exit","goto","include","include_once","phpdoc","require","require_once","return","switch","throw","try","yield","yield_from"]},"combine_consecutive_issets":true,"combine_consecutive_unsets":true,"empty_loop_body":true,"explicit_indirect_variable":true,"explicit_string_variable":true,"fully_qualified_strict_types":{"import_symbols":true},"heredoc_to_nowdoc":true,"method_argument_space":{"after_heredoc":true},"method_chaining_indentation":true,"multiline_comment_opening_closing":true,"multiline_whitespace_before_semicolons":{"strategy":"new_line_for_chained_calls"},"no_extra_blank_lines":{"tokens":["attribute","break","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","return","square_brace_block","switch","throw","use"]},"no_superfluous_elseif":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"remove_inheritdoc":true},"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","negative_instanceof","others","return","switch_case","yield","yield_from"]},"no_useless_else":true,"no_useless_return":true,"no_whitespace_before_comma_in_array":{"after_heredoc":true},"operator_linebreak":true,"ordered_class_elements":true,"ordered_types":true,"php_unit_data_provider_method_order":true,"php_unit_internal_class":true,"php_unit_test_class_requires_covers":true,"phpdoc_add_missing_param_annotation":true,"phpdoc_no_empty_return":true,"phpdoc_order_by_value":true,"phpdoc_types_order":true,"phpdoc_var_annotation_correct_order":true,"protected_to_private":true,"return_assignment":true,"self_static_accessor":true,"single_line_comment_style":true,"single_line_empty_body":true,"string_implicit_backslashes":true,"trailing_comma_in_multiline":{"after_heredoc":true},"whitespace_after_comma_in_array":{"ensure_single_space":true},"array_indentation":true,"array_syntax":true,"cast_spaces":true,"concat_space":{"spacing":"one"},"function_declaration":true,"new_with_parentheses":{"anonymous_class":false},"single_space_around_construct":true,"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_anonymous_functions":true,"allow_single_line_empty_anonymous_classes":true},"class_definition":{"single_line":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":{"stick_comment_to_next_continuous_control_statement":true},"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"align_multiline_comment":true,"backtick_to_shell_exec":true,"class_attributes_separation":{"elements":{"method":"one"}},"class_reference_name_casing":true,"clean_namespace":true,"declare_parentheses":true,"echo_tag_syntax":true,"empty_loop_condition":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"global_namespace_import":{"import_classes":true,"import_constants":false,"import_functions":false},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"native_function_casing":true,"native_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_null_property_initialization":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_singleline":true,"no_unneeded_braces":{"namespaces":true},"no_unneeded_import_alias":true,"no_unset_cast":true,"no_unused_imports":true,"no_useless_concat_operator":true,"no_useless_nullsafe_operator":true,"normalize_index_brace":true,"nullable_type_declaration":true,"nullable_type_declaration_for_default_null_value":true,"object_operator_without_whitespace":true,"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":{"groups":[["Annotation","NamedArgumentConstructor","Target"],["author","copyright","license"],["category","package","subpackage"],["property","property-read","property-write"],["deprecated","link","see","since"]]},"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_var_without_name":true,"semicolon_after_instruction":true,"simple_to_complex_string_variable":true,"single_line_comment_spacing":true,"single_quote":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trim_array_spaces":true,"type_declaration_spaces":true,"types_spaces":true,"yoda_style":true,"octal_notation":true,"assign_null_coalescing_to_coalesce_equal":true,"heredoc_indentation":true,"list_syntax":true,"ternary_to_null_coalescing":true},"hashes":{"tests\/PostmarkClientEmailTest.php":"c6189d0e828bef819878206cae49dbcd","tests\/PostmarkClientWebhooksTest.php":"585fcc9be39f0412ec8298922f33b9d8","tests\/TestingKeys.php":"f1aa3b802551905390490b5ce09ec34b","tests\/PostmarkAdminClientSenderSignatureTest.php":"c2d40ed8bad3b14c5e6398cf2edc10d9","tests\/PostmarkClickClientStatisticsTest.php":"ccb6cfc8173c4b786030d50bba0c58a2","tests\/PostmarkClientOutboundMessageTest.php":"34e138738bc9892d17b208e20e9a4e83","tests\/PostmarkClientBounceTest.php":"bf0060f3b5411deaf651173ef8387074","tests\/PostmarkClientMessageStreamsTest.php":"48ec12b95adb57931f0c35278426dba8","tests\/PostmarkClientBaseTest.php":"6149e7f01e42a5445eb5c3e83a062e50","tests\/PostmarkAdminClientDataRemovalTest.php":"13205d7a55903ab42ce2ac86a9ccd938","tests\/PostmarkAdminClientDomainTest.php":"845e28e63b4dcb8c744e405945d381cf","tests\/PostmarkClientTemplatesTest.php":"b824a3904df9ccc64edc5029a1e2d00a","tests\/PostmarkClientSuppressionsTest.php":"015d38b7cccd8120d8b099ae722baa69","tests\/PostmarkClientRuleTriggerTest.php":"ee9e25d7a6774ba4e5f38354f9e8b9cc","tests\/PostmarkClientEmailsAsStringOrArrayTest.php":"0a2b5dd12fef42de883817e377351a24","tests\/PostmarkClientInboundMessageTest.php":"0c263bb4ae0981952ef1a0ce87cf07e2","tests\/PostmarkClientStatisticsTest.php":"675729a36aa26f01cdf6ff7da3c07510","tests\/PostmarkAdminClientServersTest.php":"11141b71f1e95f2baca3dbc651c77353","tests\/NullableGetterRegressionTest.php":"2cf50c9303ac009d7a0f572035c4ab29","tests\/PostmarkClientServerTest.php":"ebf5e3733d395f2b20342d4d2684e9bf","src\/Postmark\/Models\/TemplatedPostmarkMessage.php":"e455a3be856931632026d15cf5bc9cc3","src\/Postmark\/Models\/PostmarkServerList.php":"21cd63c25fa6e4f99a944e0becec3a96","src\/Postmark\/Models\/PostmarkMessageEventDetails.php":"34d8bdd7ae8183c14435128e21b87981","src\/Postmark\/Models\/PostmarkBounceSummary.php":"90c82841ae3a370f49893ed60c412ca6","src\/Postmark\/Models\/PostmarkSenderSignatureList.php":"5c1578eeb8bb84412cc87ff99c687166","src\/Postmark\/Models\/PostmarkSenderSignature.php":"ac5b31d38d030070d93fb9a6375e35cf","src\/Postmark\/Models\/PostmarkOutboundMessageList.php":"abb423d0ef1c5baf77d81df4f4c81482","src\/Postmark\/Models\/PostmarkOpenList.php":"fb6281c45b8e805f58a121136af504e0","src\/Postmark\/Models\/PostmarkMessageEvents.php":"d27d171db159c8bf3faa868433c75869","src\/Postmark\/Models\/PostmarkBounce.php":"d73075c5c9919a4944cd52e22ce70754","src\/Postmark\/Models\/Suppressions\/SuppressionChangeRequest.php":"8e8271b70e590166a8dbb498fc59b3c2","src\/Postmark\/Models\/Suppressions\/PostmarkSuppression.php":"4d1464f50a9898e99ce928dfe258c60e","src\/Postmark\/Models\/Suppressions\/PostmarkSuppressionList.php":"90a83e66d868b0d43a69568a3e2db69d","src\/Postmark\/Models\/Suppressions\/PostmarkSuppressionResultList.php":"45fad5831e774a8984caf93ddfab3036","src\/Postmark\/Models\/Suppressions\/PostmarkSuppressionRequestResult.php":"e4039e2888ecf651561cf4bac43f1386","src\/Postmark\/Models\/TemplateValidationResponse.php":"a05045943ae4983806498f1f9bccea3a","src\/Postmark\/Models\/PostmarkInboundMessageList.php":"66a07b58d6937fe4bc11c6d1930f3b97","src\/Postmark\/Models\/PostmarkTemplate.php":"b51cbf08e371d28175a1a5acf3cedb40","src\/Postmark\/Models\/PostmarkOutboundMessageDetail.php":"e665fef48b1390243bd2f5aadb72240e","src\/Postmark\/Models\/PostmarkServer.php":"c3f18e4b6e396d94746c0807777c6ef8","src\/Postmark\/Models\/PostmarkGeographyInfo.php":"49c01a2844600bb255f19bd5f8494268","src\/Postmark\/Models\/PostmarkException.php":"f21bfb5735097cbb1c3e876bde566144","src\/Postmark\/Models\/MessageStream\/PostmarkMessageStream.php":"cecc3949f34964fac38239f080e4b7e8","src\/Postmark\/Models\/MessageStream\/PostmarkMessageStreamArchivalConfirmation.php":"50306bc8a2c9d62c9c0be389b6caa953","src\/Postmark\/Models\/MessageStream\/PostmarkMessageStreamList.php":"891813edea671e2d412385ce24e52368","src\/Postmark\/Models\/PostmarkAddressFull.php":"56044b2f62aa46ea0f19f123086e6608","src\/Postmark\/Models\/PostmarkInboundMessage.php":"4257b6b11f5624cfaa2b186bc4d6be8e","src\/Postmark\/Models\/PostmarkMessageBase.php":"4adae71023b65df6848ca6bad240b7a2","src\/Postmark\/Models\/PostmarkDomain.php":"0f01599fb3e58bca08349f20f857cd95","src\/Postmark\/Models\/PostmarkOutboundMessage.php":"48e6a4becc306bf8813f196fc78492d0","src\/Postmark\/Models\/PostmarkMessage.php":"d7276a2851c6b323973096a0e9679406","src\/Postmark\/Models\/PostmarkBounceActivation.php":"7050e8d9b6073b707873ab92d2a084d1","src\/Postmark\/Models\/PostmarkInboundRuleTriggerList.php":"58c01717b4fa742534e218d8be9f0fcd","src\/Postmark\/Models\/PostmarkBounceList.php":"2395d35eabf6b330e2c6cb066abfea6e","src\/Postmark\/Models\/PostmarkDomainList.php":"f46a4bf5176cb750232a41faa163b811","src\/Postmark\/Models\/PostmarkClickList.php":"528061f329b80de653c437bdae9cf6b9","src\/Postmark\/Models\/DataRemovalRequestResponse.php":"e3f6610a0922446b5e01a839e9ffa63a","src\/Postmark\/Models\/PostmarkDeliveryStats.php":"e5d84610536116f895f40c653ec87a31","src\/Postmark\/Models\/PostmarkMessageEvent.php":"967037219abc864be94d89f3d3038de2","src\/Postmark\/Models\/PostmarkAgentInfo.php":"5f8957854fb21e93c2934688457e9b79","src\/Postmark\/Models\/PostmarkInboundRuleTrigger.php":"44227d72abf002f2ba23994fa998d412","src\/Postmark\/Models\/PostmarkDomainDetails.php":"2128512b05cb217d830c6226185f3a3b","src\/Postmark\/Models\/CaseInsensitiveArray.php":"3c8aa907f8b7e79e5ecf08432fc021a5","src\/Postmark\/Models\/PostmarkResponse.php":"dc3bcfad9a37dd7b97e7e7dcb5ef2796","src\/Postmark\/Models\/Webhooks\/HttpHeader.php":"6c9fc858979d0ca34fdfc39d9536456a","src\/Postmark\/Models\/Webhooks\/WebhookConfigurationClickTrigger.php":"b82384095172c86cc1072c43342503b0","src\/Postmark\/Models\/Webhooks\/WebhookConfigurationTriggers.php":"cb5350ada0d232ea36c020e41d0fff46","src\/Postmark\/Models\/Webhooks\/WebhookConfigurationListingResponse.php":"4733a3d475d5b52b2fc5c2743ecd6ce9","src\/Postmark\/Models\/Webhooks\/WebhookConfiguration.php":"5ab81444a62d64f1d85869cc477ea6cb","src\/Postmark\/Models\/Webhooks\/WebhookConfigurationSubscriptionChangeTrigger.php":"bbee62123b08297d28d2dd52ec60d204","src\/Postmark\/Models\/Webhooks\/WebhookConfigurationDeliveryTrigger.php":"1d875b40b6535cccf3ffc1b3030bdd8d","src\/Postmark\/Models\/Webhooks\/WebhookConfigurationBounceTrigger.php":"b763de47c7c320063b04118033b87e1e","src\/Postmark\/Models\/Webhooks\/HttpAuth.php":"d4c34c6e4ebae420bbf08535bf1cf137","src\/Postmark\/Models\/Webhooks\/WebhookConfigurationOpenTrigger.php":"5ff452fc311dc83394f265196ad743de","src\/Postmark\/Models\/Webhooks\/WebhookConfigurationSpamComplaintTrigger.php":"7c50a14e07222a3dec8a941b608c50db","src\/Postmark\/Models\/PostmarkOpen.php":"2266b1e18680094df52c9136b640ab5f","src\/Postmark\/Models\/PostmarkClick.php":"3b54e12ff2e4c98e669e159e6134a8fb","src\/Postmark\/Models\/PostmarkTemplateList.php":"df822b2ebc5fb948f231e33e306b4e49","src\/Postmark\/Models\/DynamicResponseModel.php":"34e0d013d12c010ffef05e014a6beb16","src\/Postmark\/Models\/PostmarkAttachment.php":"7d9c282ed6a89246541c08d6df437fd8","src\/Postmark\/Models\/PostmarkMessageDump.php":"3524696b5daea922b8f9b9f9b701fdeb","src\/Postmark\/Models\/Stats\/PostmarkOutboundLocationStats.php":"886735d7eac843e5b3d9975ab6ccfb14","src\/Postmark\/Models\/Stats\/PostmarkOutboundReadStats.php":"d505d00e413b2b93ad4caf0b67d8b15b","src\/Postmark\/Models\/Stats\/PostmarkOutboundSpamComplaintStats.php":"83732226e4a6d96bbd4a7f1153f8cda7","src\/Postmark\/Models\/Stats\/PostmarkOutboundClickStats.php":"3a6ef3e4859d94be8054f7e7a61a1d96","src\/Postmark\/Models\/Stats\/PostmarkOutboundOverviewStats.php":"1c4fb85dc878be47e0a249b181506569","src\/Postmark\/Models\/Stats\/PostmarkOutboundOpenStats.php":"12e00350058f2464d7c6924ac340b38f","src\/Postmark\/Models\/Stats\/PostmarkOutboundPlatformStats.php":"7614dcc71613177f418303155624f92a","src\/Postmark\/Models\/Stats\/PostmarkOutboundBounceStats.php":"e772ce2fab9b2ca066a316e4c5665041","src\/Postmark\/Models\/Stats\/PostmarkOutboundSentStats.php":"0c6737386f4de6652181c1443b646ecd","src\/Postmark\/Models\/Stats\/PostmarkOutboundTrackedStats.php":"09125390bc5f0e997aa4774c83e01854","src\/Postmark\/Models\/PostmarkBounceDump.php":"00b9dda4fc32c54596d924c1d1737041","src\/Postmark\/PostmarkClient.php":"54d297cd04796e96a1d6061288f0b772","src\/Postmark\/PostmarkAdminClient.php":"916c134f5da322b2637ea8266652ecc6","src\/Postmark\/PostmarkClientBase.php":"dc8362666c0622eedeed5ad2f34a9d37"}}
9 changes: 7 additions & 2 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@
* @see https://activecampaign.atlassian.net/wiki/spaces/DEV/pages/24051783/ActiveCampaign+PHP+Coding+Style+Standards
* @see https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/doc/ruleSets/index.rst
*/
$finder = PhpCsFixer\Finder::create()->in(__DIR__);
$finder = PhpCsFixer\Finder::create()
->in(__DIR__)
->exclude(['vendor']);
$config = new PhpCsFixer\Config();

return $config->setRules([
// setFinder() was missing, so `php-cs-fixer fix` with no path argument aborted with
// "You must call one of in() or append() methods before iterating over a Finder" —
// the fixer has never actually been runnable in this repo.
return $config->setFinder($finder)->setRules([
'@PhpCsFixer' => true,
'@PHP82Migration' => true,
'concat_space' => ['spacing' => 'one'], // This is required by [PER coding style rule 6.2 binary operators](https://www.php-fig.org/per/coding-style/#62-binary-operators)
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v7.0.1](https://github.com/ActiveCampaign/postmark-php/tree/v7.0.1)

### Fixed
- **Getters backed by nullable properties no longer throw `TypeError`.** Each of these promised a
non-nullable return while its constructor explicitly assigns `null` when the API omits the
field, so they were guaranteed to fatal on a perfectly valid response:
`PostmarkOpen::getGeo()`, `PostmarkOpen::getClient()`, `PostmarkOpen::getOS()`,
`PostmarkMessageBase::getMetadata()`, `PostmarkMessageBase::getMessageStream()` and
`WebhookConfiguration::getHttpAuth()` now declare nullable return types.
Reported against `getGeo()` for a broadcast-stream message with no geo data.
(`PostmarkClick` already declared its equivalents loosely, which is why clicks worked and
opens did not.)
- **`PostmarkClient::getBounces()` accepts a MessageID again.** The `$messageID` filter was
changed to `?int` in `c7a4371` and released from v5.0.1 onward. Postmark MessageIDs are GUIDs,
so the filter has been unusable since — `getBounces(1, 0, null, null, null, null, $guid)` threw
`Argument #7 ($messageID) must be of type ?int, string given`. It is `?string` again, matching
the pre-v5.0.1 documented type.

### Notes for upgraders
These are all bug fixes and none should require a change on your side. Widening a return type to
nullable is covariant, so a subclass that overrides one of the getters with the narrower type
stays compatible. The one thing to be aware of: a subclass that overrides `getBounces()` with
`?int $messageID` will need to change to `?string`, since parameter types are contravariant. If
you were catching the `TypeError` from any of the getters above as a workaround, you can drop it.

### Internal
- `.php-cs-fixer.dist.php` never called `setFinder()`, so `php-cs-fixer` aborted with
"You must call one of in() or append() methods" and had never actually run. Fixed.
- CI gained a credential-free `static-analysis` job running PHPStan, which is the only check in
this repo a fork PR can currently exercise.

## [v7.0.0](https://github.com/ActiveCampaign/postmark-php/tree/v7.0.0)

### Added
Expand Down
Loading