diff --git a/lib/Fhp/Action/GetBalance.php b/lib/Fhp/Action/GetBalance.php index 8a09242e..00d8f81e 100644 --- a/lib/Fhp/Action/GetBalance.php +++ b/lib/Fhp/Action/GetBalance.php @@ -24,7 +24,7 @@ */ class GetBalance extends PaginateableAction { - // Request (not available after serialization, i.e. not available in processResponse()). + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var SEPAAccount */ private $account; /** @var bool */ diff --git a/lib/Fhp/Action/GetDepotAufstellung.php b/lib/Fhp/Action/GetDepotAufstellung.php index e449406b..17e644e3 100644 --- a/lib/Fhp/Action/GetDepotAufstellung.php +++ b/lib/Fhp/Action/GetDepotAufstellung.php @@ -24,7 +24,7 @@ */ class GetDepotAufstellung extends PaginateableAction { - // Request (not available after serialization, i.e. not available in processResponse()). + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var SEPAAccount */ private $account; diff --git a/lib/Fhp/Action/GetSEPADirectDebitParameters.php b/lib/Fhp/Action/GetSEPADirectDebitParameters.php index 9ef1e858..80b87673 100644 --- a/lib/Fhp/Action/GetSEPADirectDebitParameters.php +++ b/lib/Fhp/Action/GetSEPADirectDebitParameters.php @@ -16,12 +16,11 @@ class GetSEPADirectDebitParameters extends BaseAction public const SEQUENCE_TYPES = ['FRST', 'OOFF', 'FNAL', 'RCUR']; public const DIRECT_DEBIT_TYPES = ['CORE', 'COR1', 'B2B']; + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var string */ private $directDebitType; - /** @var string */ private $seqType; - /** @var bool */ private $singleDirectDebit; @@ -43,6 +42,45 @@ public static function create(string $seqType, bool $singleDirectDebit, string $ return $result; } + /** + * @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023 + */ + public function serialize(): string + { + return serialize($this->__serialize()); + } + + public function __serialize(): array + { + return [ + parent::__serialize(), + $this->directDebitType, $this->seqType, $this->singleDirectDebit, + ]; + } + + /** + * @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023 + * + * @param string $serialized + * @return void + */ + public function unserialize($serialized) + { + self::__unserialize(unserialize($serialized)); + } + + public function __unserialize(array $serialized): void + { + list( + $parentSerialized, + $this->directDebitType, $this->seqType, $this->singleDirectDebit, + ) = $serialized; + + is_array($parentSerialized) ? + parent::__unserialize($parentSerialized) : + parent::unserialize($parentSerialized); + } + public static function getHixxesSegmentName(string $directDebitType, bool $singleDirectDebit): string { switch ($directDebitType) { diff --git a/lib/Fhp/Action/GetStatementOfAccount.php b/lib/Fhp/Action/GetStatementOfAccount.php index a581f991..8f623ec1 100644 --- a/lib/Fhp/Action/GetStatementOfAccount.php +++ b/lib/Fhp/Action/GetStatementOfAccount.php @@ -31,7 +31,7 @@ */ class GetStatementOfAccount extends PaginateableAction { - // Request (not available after serialization, i.e. not available in processResponse()). + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var SEPAAccount */ private $account; /** @var \DateTime */ @@ -93,7 +93,7 @@ public function __serialize(): array { return [ parent::__serialize(), - $this->account, $this->from, $this->to, $this->allAccounts, + $this->account, $this->from, $this->to, $this->allAccounts, $this->includeUnbooked, $this->bankName, ]; } @@ -113,7 +113,7 @@ public function __unserialize(array $serialized): void { list( $parentSerialized, - $this->account, $this->from, $this->to, $this->allAccounts, + $this->account, $this->from, $this->to, $this->allAccounts, $this->includeUnbooked, $this->bankName, ) = $serialized; diff --git a/lib/Fhp/Action/GetStatementOfAccountXML.php b/lib/Fhp/Action/GetStatementOfAccountXML.php index 4bb31497..f4ee1a55 100644 --- a/lib/Fhp/Action/GetStatementOfAccountXML.php +++ b/lib/Fhp/Action/GetStatementOfAccountXML.php @@ -24,7 +24,7 @@ */ class GetStatementOfAccountXML extends PaginateableAction { - // Request (not available after serialization, i.e. not available in processResponse()). + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var SEPAAccount */ private $account; /** @var \DateTime */ diff --git a/lib/Fhp/Action/SendInternationalCreditTransfer.php b/lib/Fhp/Action/SendInternationalCreditTransfer.php index 2634f24c..73416fcc 100644 --- a/lib/Fhp/Action/SendInternationalCreditTransfer.php +++ b/lib/Fhp/Action/SendInternationalCreditTransfer.php @@ -13,12 +13,11 @@ class SendInternationalCreditTransfer extends BaseAction { + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var SEPAAccount */ protected $account; - /** @var string */ protected $dtavzData; - /** @var string|null */ protected $dtavzVersion; @@ -36,6 +35,45 @@ public static function create(SEPAAccount $account, string $dtavzData, ?string $ return $result; } + /** + * @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023 + */ + public function serialize(): string + { + return serialize($this->__serialize()); + } + + public function __serialize(): array + { + return [ + parent::__serialize(), + $this->account, $this->dtavzData, $this->dtavzVersion, + ]; + } + + /** + * @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023 + * + * @param string $serialized + * @return void + */ + public function unserialize($serialized) + { + self::__unserialize(unserialize($serialized)); + } + + public function __unserialize(array $serialized): void + { + list( + $parentSerialized, + $this->account, $this->dtavzData, $this->dtavzVersion, + ) = $serialized; + + is_array($parentSerialized) ? + parent::__unserialize($parentSerialized) : + parent::unserialize($parentSerialized); + } + protected function createRequest(BPD $bpd, ?UPD $upd) { /** @var HIAUBSv9 $hiaubs */ diff --git a/lib/Fhp/Action/SendSEPADirectDebit.php b/lib/Fhp/Action/SendSEPADirectDebit.php index c02ab405..94630204 100644 --- a/lib/Fhp/Action/SendSEPADirectDebit.php +++ b/lib/Fhp/Action/SendSEPADirectDebit.php @@ -22,27 +22,24 @@ */ class SendSEPADirectDebit extends BaseAction { + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var SEPAAccount */ protected $account; - /** @var string */ protected $painMessage; - /** @var string */ protected $painNamespace; - /** @var float */ protected $ctrlSum; - /** @var bool */ protected $singleDirectDebit = false; - /** @var bool */ protected $tryToUseControlSumForSingleTransactions = false; - /** @var string */ private $coreType; + // There are no result fields. This action is simply marked as done to indicate that the transfer was executed. + public static function create(SEPAAccount $account, string $painMessage, bool $tryToUseControlSumForSingleTransactions = false): SendSEPADirectDebit { if (preg_match('/xmlns="(?[^"]+)"/s', $painMessage, $matches) === 1) { diff --git a/lib/Fhp/Action/SendSEPARealtimeTransfer.php b/lib/Fhp/Action/SendSEPARealtimeTransfer.php index 712b3bf4..c3bd9602 100644 --- a/lib/Fhp/Action/SendSEPARealtimeTransfer.php +++ b/lib/Fhp/Action/SendSEPARealtimeTransfer.php @@ -23,15 +23,17 @@ */ class SendSEPARealtimeTransfer extends BaseAction { + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var SEPAAccount */ private $account; /** @var string */ private $painMessage; /** @var string */ private $xmlSchema; - private bool $allowConversionToSEPATransfer = true; + // There are no result fields. This action is simply marked as done to indicate that the transfer was executed. + /** * @param SEPAAccount $account The account from which the transfer will be sent. * @param string $painMessage An XML-formatted ISO 20022 message. You may want to use github.com/nemiah/phpSepaXml @@ -52,6 +54,45 @@ public static function create(SEPAAccount $account, string $painMessage, bool $a return $result; } + /** + * @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023 + */ + public function serialize(): string + { + return serialize($this->__serialize()); + } + + public function __serialize(): array + { + return [ + parent::__serialize(), + $this->account, $this->painMessage, $this->xmlSchema, $this->allowConversionToSEPATransfer, + ]; + } + + /** + * @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023 + * + * @param string $serialized + * @return void + */ + public function unserialize($serialized) + { + self::__unserialize(unserialize($serialized)); + } + + public function __unserialize(array $serialized): void + { + list( + $parentSerialized, + $this->account, $this->painMessage, $this->xmlSchema, $this->allowConversionToSEPATransfer, + ) = $serialized; + + is_array($parentSerialized) ? + parent::__unserialize($parentSerialized) : + parent::unserialize($parentSerialized); + } + protected function createRequest(BPD $bpd, ?UPD $upd) { /** @var HIIPZSv1|HIIPZSv2 $hiipzs */ diff --git a/lib/Fhp/Action/SendSEPATransfer.php b/lib/Fhp/Action/SendSEPATransfer.php index 48cdae9b..6c119d37 100644 --- a/lib/Fhp/Action/SendSEPATransfer.php +++ b/lib/Fhp/Action/SendSEPATransfer.php @@ -19,6 +19,7 @@ */ class SendSEPATransfer extends BaseAction { + // Request (if you add a field here, update __serialize() and __unserialize() as well). /** @var SEPAAccount */ private $account; /** @var string */ @@ -26,6 +27,8 @@ class SendSEPATransfer extends BaseAction /** @var string */ private $xmlSchema; + // There are no result fields. This action is simply marked as done to indicate that the transfer was executed. + /** * @param SEPAAccount $account The account from which the transfer will be sent. * @param string $painMessage An XML-formatted ISO 20022 message. You may want to use github.com/nemiah/phpSepaXml @@ -44,6 +47,45 @@ public static function create(SEPAAccount $account, string $painMessage): SendSE return $result; } + /** + * @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023 + */ + public function serialize(): string + { + return serialize($this->__serialize()); + } + + public function __serialize(): array + { + return [ + parent::__serialize(), + $this->account, $this->painMessage, $this->xmlSchema, + ]; + } + + /** + * @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023 + * + * @param string $serialized + * @return void + */ + public function unserialize($serialized) + { + self::__unserialize(unserialize($serialized)); + } + + public function __unserialize(array $serialized): void + { + list( + $parentSerialized, + $this->account, $this->painMessage, $this->xmlSchema, + ) = $serialized; + + is_array($parentSerialized) ? + parent::__unserialize($parentSerialized) : + parent::unserialize($parentSerialized); + } + protected function createRequest(BPD $bpd, ?UPD $upd) { // ANALYSE XML FOR RECEIPTS AND PAYMENT DATE diff --git a/lib/Fhp/Protocol/Message.php b/lib/Fhp/Protocol/Message.php index 6d18b756..dddb3f64 100644 --- a/lib/Fhp/Protocol/Message.php +++ b/lib/Fhp/Protocol/Message.php @@ -222,11 +222,7 @@ public function findRueckmeldungen(int $code): array */ public function serialize(): string { - $result = ''; - foreach ($this->wrapperSegments as $segment) { - $result .= Serializer::serializeSegment($segment); - } - return $result; + return Serializer::serializeSegments($this->wrapperSegments); } /** @@ -351,7 +347,7 @@ public static function parse(string $rawMessage): Message * @param int $segmentNumber The number for the *first* segment, subsequent segment get the subsequent integers. * @return BaseSegment[] The same array, for chaining. */ - private static function setSegmentNumbers(array $segments, int $segmentNumber): array + public static function setSegmentNumbers(array $segments, int $segmentNumber): array { foreach ($segments as $segment) { $segment->segmentkopf->segmentnummer = $segmentNumber; diff --git a/lib/Fhp/Syntax/Serializer.php b/lib/Fhp/Syntax/Serializer.php index a5d14308..da2a7a7e 100644 --- a/lib/Fhp/Syntax/Serializer.php +++ b/lib/Fhp/Syntax/Serializer.php @@ -71,6 +71,15 @@ public static function serializeSegment(BaseSegment $segment): string return implode(Delimiter::ELEMENT, static::flattenAndTrimEnd($serializedElements)) . Delimiter::SEGMENT; } + /** + * @param BaseSegment[] $segments The segments to be serialized. + * @return string The concatenated HBCI wire format representation of the segments. + */ + public static function serializeSegments(array $segments): string + { + return implode(array_map(['self', 'serializeSegment'], $segments)); + } + /** * @param BaseSegment|BaseDeg|null $obj An object to be serialized. If null, all fields are implicitly null. * @param BaseDescriptor $descriptor The descriptor for the object to be serialized. diff --git a/lib/Tests/Fhp/Action/SendSEPATransferTest.php b/lib/Tests/Fhp/Action/SendSEPATransferTest.php new file mode 100644 index 00000000..03a81515 --- /dev/null +++ b/lib/Tests/Fhp/Action/SendSEPATransferTest.php @@ -0,0 +1,43 @@ +initDialog(); + $this->expectMessage($this->getSendTransferRequest(), static::SEND_TRANSFER_RESPONSE); + $originalAction = $this->runInitialRequest(); + + // The key behavior we need is that the action produces the same request again after (un)serialization. It is + // not necessary for us to check the field values inside the action directly. + $originalRequest = $originalAction->getNextRequest($this->fints->getBpd(), null); + $this->assertIsArray($originalRequest); + Message::setSegmentNumbers($originalRequest, 42); + + // Sanity-check that the request we're getting is something sensible (not empty string or so). + $serializedRequest = Serializer::serializeSegments($originalRequest); + $this->assertStringContainsString('HKCCS', $serializedRequest); + $this->assertStringContainsString('DE42000000001234567890', $serializedRequest); + + // Do a serialization roundtrip. + $serializedAction = serialize($originalAction); + $unserializedAction = unserialize($serializedAction); + + // Verify that the request is still the same. + $newRequest = $unserializedAction->getNextRequest($this->fints->getBpd(), null); + $this->assertIsArray($newRequest); + Message::setSegmentNumbers($newRequest, 42); + $this->assertEquals($originalRequest, $newRequest); + } +} diff --git a/lib/Tests/Fhp/Integration/DKB/SendSEPATransferTest.php b/lib/Tests/Fhp/Integration/DKB/SendSEPATransferTest.php index b409aee1..cdaa0116 100644 --- a/lib/Tests/Fhp/Integration/DKB/SendSEPATransferTest.php +++ b/lib/Tests/Fhp/Integration/DKB/SendSEPATransferTest.php @@ -81,7 +81,7 @@ class SendSEPATransferTest extends DKBIntegrationTestBase public const SEND_TAN_REQUEST = "HNHBK:1:3+000000000425+300+FAKEDIALOGIDabcdefghijklmnopqr+3'HNVSK:998:3+PIN:2+998+1+1::FAKEKUNDENSYSTEMIDabcdefghij+1:20190102:030405+2:2:13:@8@00000000:5:1+280:12030000:test?@user:V:0:0+0'HNVSD:999:1+@206@HNSHK:2:4+PIN:2+921+9999999+1+1+1::FAKEKUNDENSYSTEMIDabcdefghij+1+1:20190102:030405+1:999:1+6:10:19+280:12030000:test?@user:S:0:0'HKTAN:3:6+2++++2472-12-07-21.27.57.456789+N'HNSHA:4:2+9999999++12345:666555''HNHBS:5:1+3'"; public const SEND_TAN_RESPONSE = "HIRMG:3:2+0010::Nachricht entgegengenommen.'HIRMS:4:2:3+0010::Der Auftrag wurde entgegengenommen.'HITAN:5:6:3+2++2472-12-07-21.27.57.456789'"; - private function getSendTransferRequest(): string + protected function getSendTransferRequest(): string { // Note: strlen() is computed instead of hard-coded because it depends on the indentation in this file, which // may be changed by linters and other tools, and because it contains line breaks, which are different depending @@ -93,7 +93,7 @@ private function getSendTransferRequest(): string /** * @throws \Throwable */ - private function runInitialRequest(): SendSEPATransfer + protected function runInitialRequest(): SendSEPATransfer { $sendTransfer = SendSEPATransfer::create($this->getTestAccount(), static::PAIN_MESSAGE); $this->fints->execute($sendTransfer);