diff --git a/.travis.yml b/.travis.yml index f761f2d..e6a836b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,8 @@ language: php php: - - 5.5 - - 5.6 - - 7 - - hhvm + - 7.1 + - 7.2 env: matrix: @@ -16,10 +14,7 @@ before_script: script: - mkdir -p build/logs - - composer test - - # phpcs only on latest PHP - - if [[ $TRAVIS_PHP_VERSION = '5.6' ]]; then composer phpcs; fi + - composer ci after_script: - php vendor/bin/coveralls -v diff --git a/README.md b/README.md index 8afb209..0f0eee1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Usage require_once 'vendor/autoload.php'; $downloader = new FioApi\Downloader('TOKEN@todo'); -$transactionList = $downloader->downloadSince(new \DateTime('-1 week')); +$transactionList = $downloader->downloadSince(new \DateTimeImmutable('-1 week')); foreach ($transactionList->getTransactions() as $transaction) { var_dump($transaction); //object with getters @@ -27,7 +27,7 @@ foreach ($transactionList->getTransactions() as $transaction) { Requirements ------------ -Fio API PHP works with PHP 5.5, PHP 5.6 or PHP 7. +Fio API PHP works with PHP 7 or higher. Submitting bugs and feature requests ------------------------------------ @@ -39,6 +39,21 @@ Martin Hujer - - Changelog ---------- + +## 4.0.1 (2017-08-09) +- [#12](https://github.com/mhujer/fio-api-php/pull/12) handle empty transaction list (thx @soukicz!) + +## 4.0.0 (2017-08-04) +- [#9](https://github.com/mhujer/fio-api-php/pull/9) minimal supported version is PHP 7.1 +- [#9](https://github.com/mhujer/fio-api-php/pull/9)`DateTime` replaced with `DateTimeImmutable` (or `DateTimeInterface`) +- [#9](https://github.com/mhujer/fio-api-php/pull/9) strict types and primitive typehints are used everywhere + +## 3.0.0 (2016-11-24) +- dropped support for PHP <7 + +## 2.3.0 (2016-11-24) +- [#7](https://github.com/mhujer/fio-api-php/pull/7): added official composer CA bundle support (@soukicz) + ## 2.2.0 (2016-03-13) - [#2](https://github.com/mhujer/fio-api-php/pull/2): added [Kdyby/CurlCaBundle](https://github.com/Kdyby/CurlCaBundle) as an optional dependency (@mhujer) diff --git a/appveyor.yml b/appveyor.yml index d664949..09cf7c8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,13 +17,14 @@ init: install: - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) - IF %PHP%==1 cd c:\php - - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/php-5.6.16-Win32-VC11-x86.zip - - IF %PHP%==1 7z x php-5.6.16-Win32-VC11-x86.zip -y >nul + - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/php-7.1.0-Win32-VC14-x86.zip + - IF %PHP%==1 7z x php-7.1.0-Win32-VC14-x86.zip -y >nul - IF %PHP%==1 copy /Y php.ini-development php.ini - IF %PHP%==1 echo max_execution_time=1200 >> php.ini - IF %PHP%==1 echo date.timezone="UTC" >> php.ini - IF %PHP%==1 echo extension_dir=ext >> php.ini - IF %PHP%==1 echo extension=php_curl.dll >> php.ini + - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini - IF %PHP%==1 del /Q *.zip - IF %PHP%==1 cd .. diff --git a/composer.json b/composer.json index 13a9abd..c0fd32c 100644 --- a/composer.json +++ b/composer.json @@ -18,17 +18,17 @@ } ], "require": { - "php": ">=5.5", + "php": ">=7.1", "ext-curl": "*", "guzzlehttp/guzzle": "~6.1" }, "require-dev": { - "phpunit/phpunit": "~4.7|~5.0", - "squizlabs/php_codesniffer": "~2.3", + "phpunit/phpunit": "~6.3", + "squizlabs/php_codesniffer": "~3.0", "satooshi/php-coveralls": "^1.0" }, "suggest": { - "kdyby/curl-ca-bundle": "Provides regularly updated root certificates list" + "composer/ca-bundle": "Provides regularly updated root certificates list" }, "autoload": { "psr-4": { @@ -36,6 +36,10 @@ } }, "scripts": { + "ci": [ + "@phpcs", + "@test" + ], "test": "phpunit", "phpcs": "phpcs --standard=PSR2 src && phpcs --standard=PSR2 tests" } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c29a516..700340d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,14 +2,12 @@ diff --git a/src/FioApi/Account.php b/src/FioApi/Account.php index 17dbab3..5c8db35 100644 --- a/src/FioApi/Account.php +++ b/src/FioApi/Account.php @@ -1,4 +1,6 @@ accountNumber = $accountNumber; $this->bankCode = $bankCode; $this->currency = $currency; @@ -34,42 +34,27 @@ public function __construct($accountNumber, $bankCode, $currency, $iban, $bic) $this->bic = $bic; } - /** - * @return string - */ - public function getAccountNumber() + public function getAccountNumber(): string { return $this->accountNumber; } - /** - * @return string - */ - public function getBankCode() + public function getBankCode(): string { return $this->bankCode; } - /** - * @return string - */ - public function getCurrency() + public function getCurrency(): string { return $this->currency; } - /** - * @return string - */ - public function getIban() + public function getIban(): string { return $this->iban; } - /** - * @return string - */ - public function getBic() + public function getBic(): string { return $this->bic; } diff --git a/src/FioApi/Downloader.php b/src/FioApi/Downloader.php index 98aa931..458150a 100644 --- a/src/FioApi/Downloader.php +++ b/src/FioApi/Downloader.php @@ -1,8 +1,11 @@ urlBuilder = new UrlBuilder($token); $this->client = $client; } - /** - * @param string $path - */ - public function setCertificatePath($path) + public function setCertificatePath(string $path) { $this->certificatePath = $path; } - public function getCertificatePath() + public function getCertificatePath(): string { if ($this->certificatePath) { return $this->certificatePath; } - if (class_exists('\Kdyby\CurlCaBundle\CertificateHelper')) { + if (class_exists('\Composer\CaBundle\CaBundle')) { + return \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath(); + } elseif (class_exists('\Kdyby\CurlCaBundle\CertificateHelper')) { return \Kdyby\CurlCaBundle\CertificateHelper::getCaInfoFile(); } @@ -47,10 +48,7 @@ public function getCertificatePath() return __DIR__ . '/keys/Geotrust_PCA_G3_Root.pem'; } - /** - * @return \GuzzleHttp\ClientInterface - */ - public function getClient() + public function getClient(): ClientInterface { if (!$this->client) { $this->client = new \GuzzleHttp\Client(); @@ -58,12 +56,7 @@ public function getClient() return $this->client; } - /** - * @param \DateTime $from - * @param \DateTime $to - * @return TransactionList - */ - public function downloadFromTo(\DateTime $from, \DateTime $to) + public function downloadFromTo(\DateTimeInterface $from, \DateTimeInterface $to): TransactionList { $client = $this->getClient(); $url = $this->urlBuilder->buildPeriodsUrl($from, $to); @@ -85,15 +78,11 @@ public function downloadFromTo(\DateTime $from, \DateTime $to) throw $e; } - return TransactionList::create(json_decode($response->getBody())->accountStatement); + return TransactionList::create(json_decode($response->getBody()->getContents())->accountStatement); } - /** - * @param \DateTime $since - * @return TransactionList - */ - public function downloadSince(\DateTime $since) + public function downloadSince(\DateTimeInterface $since): TransactionList { - return $this->downloadFromTo($since, new \DateTime()); + return $this->downloadFromTo($since, new \DateTimeImmutable()); } } diff --git a/src/FioApi/Exceptions/InternalErrorException.php b/src/FioApi/Exceptions/InternalErrorException.php index b4ead6f..d82f862 100644 --- a/src/FioApi/Exceptions/InternalErrorException.php +++ b/src/FioApi/Exceptions/InternalErrorException.php @@ -1,4 +1,6 @@ id = $id; $this->date = $date; @@ -96,11 +98,11 @@ protected function __construct( * @param \stdClass $data Transaction data from JSON API response * @return Transaction */ - public static function create(\stdClass $data) + public static function create(\stdClass $data): Transaction { return new self( - $data->column22->value, //ID pohybu - new \DateTime($data->column0->value), //Datum + (string) $data->column22->value, //ID pohybu + new \DateTimeImmutable($data->column0->value), //Datum $data->column1->value, //Objem $data->column14->value, //Měna !empty($data->column2) ? $data->column2->value : null, //Protiúčet @@ -119,138 +121,87 @@ public static function create(\stdClass $data) ); } - /** - * @return int - */ - public function getId() + public function getId(): string { return $this->id; } - /** - * @return \DateTime - */ - public function getDate() + public function getDate(): \DateTimeImmutable { return $this->date; } - /** - * @return float - */ - public function getAmount() + public function getAmount(): float { return $this->amount; } - /** - * @return string - */ - public function getCurrency() + public function getCurrency(): string { return $this->currency; } - /** - * @return string - */ - public function getSenderAccountNumber() + public function getSenderAccountNumber(): ?string { return $this->senderAccountNumber; } - /** - * @return string - */ - public function getSenderBankCode() + public function getSenderBankCode(): ?string { return $this->senderBankCode; } - /** - * @return string - */ - public function getSenderBankName() + public function getSenderBankName(): ?string { return $this->senderBankName; } - /** - * @return int - */ - public function getConstantSymbol() + public function getConstantSymbol(): ?string { return $this->constantSymbol; } - /** - * @return string - */ - public function getVariableSymbol() + public function getVariableSymbol(): ?string { return $this->variableSymbol; } - /** - * @return int - */ - public function getSpecificSymbol() + public function getSpecificSymbol(): ?string { return $this->specificSymbol; } - /** - * @return string - */ - public function getUserIdentity() + public function getUserIdentity(): ?string { return $this->userIdentity; } - /** - * @return string - */ - public function getUserMessage() + public function getUserMessage(): ?string { return $this->userMessage; } - /** - * @return string - */ - public function getTransactionType() + public function getTransactionType(): string { return $this->transactionType; } - /** - * @return string - */ - public function getPerformedBy() + public function getPerformedBy(): ?string { return $this->performedBy; } - /** - * @return string - */ - public function getComment() + public function getComment(): ?string { return $this->comment; } - /** - * @return string - */ - public function getPaymentOrderId() + public function getPaymentOrderId(): ?float { return $this->paymentOrderId; } - /** - * @return string - */ - public function getSpecification() + public function getSpecification(): ?string { return $this->specification; } diff --git a/src/FioApi/TransactionList.php b/src/FioApi/TransactionList.php index 54dede1..5ed1e94 100644 --- a/src/FioApi/TransactionList.php +++ b/src/FioApi/TransactionList.php @@ -1,4 +1,6 @@ openingBalance = $openingBalance; @@ -72,7 +64,7 @@ protected function addTransaction(Transaction $transaction) * @param \stdClass $data Data from JSON API response * @return TransactionList */ - public static function create(\stdClass $data) + public static function create(\stdClass $data): TransactionList { $account = new Account( $data->info->accountId, @@ -85,8 +77,8 @@ public static function create(\stdClass $data) $transactionList = new self( $data->info->openingBalance, $data->info->closingBalance, - new \DateTime($data->info->dateStart), - new \DateTime($data->info->dateEnd), + new \DateTimeImmutable($data->info->dateStart), + new \DateTimeImmutable($data->info->dateEnd), $data->info->idFrom, $data->info->idTo, $data->info->idLastDownload, @@ -100,66 +92,42 @@ public static function create(\stdClass $data) return $transactionList; } - /** - * @return float - */ - public function getOpeningBalance() + public function getOpeningBalance(): float { return $this->openingBalance; } - /** - * @return float - */ - public function getClosingBalance() + public function getClosingBalance(): float { return $this->closingBalance; } - /** - * @return \DateTime - */ - public function getDateStart() + public function getDateStart(): \DateTimeImmutable { return $this->dateStart; } - /** - * @return \DateTime - */ - public function getDateEnd() + public function getDateEnd(): \DateTimeImmutable { return $this->dateEnd; } - /** - * @return int - */ - public function getIdFrom() + public function getIdFrom(): ?float { return $this->idFrom; } - /** - * @return int - */ - public function getIdTo() + public function getIdTo(): ?float { return $this->idTo; } - /** - * @return int - */ - public function getIdLastDownload() + public function getIdLastDownload(): ?int { return $this->idLastDownload; } - /** - * @return Account - */ - public function getAccount() + public function getAccount(): Account { return $this->account; } @@ -167,7 +135,7 @@ public function getAccount() /** * @return Transaction[] */ - public function getTransactions() + public function getTransactions(): array { return $this->transactions; } diff --git a/src/FioApi/UrlBuilder.php b/src/FioApi/UrlBuilder.php index 8a065f1..cf1a731 100644 --- a/src/FioApi/UrlBuilder.php +++ b/src/FioApi/UrlBuilder.php @@ -1,4 +1,6 @@ setToken($token); } - /** - * @return string - */ - public function getToken() + public function getToken(): string { return $this->token; } - /** - * @param string $token - */ - public function setToken($token) + public function setToken(string $token) { if (!$token) { throw new MissingTokenException( @@ -41,12 +32,7 @@ public function setToken($token) $this->token = $token; } - /** - * @param \DateTime $from - * @param \DateTime $to - * @return string - */ - public function buildPeriodsUrl(\DateTime $from, \DateTime $to) + public function buildPeriodsUrl(\DateTimeInterface $from, \DateTimeInterface $to): string { return sprintf( self::BASE_URL . 'periods/%s/%s/%s/transactions.json', diff --git a/tests/FioApi/AccountTest.php b/tests/FioApi/AccountTest.php index ef73bcb..d9a257a 100644 --- a/tests/FioApi/AccountTest.php +++ b/tests/FioApi/AccountTest.php @@ -1,8 +1,9 @@ assertEquals('214498596', $account->getAccountNumber()); - $this->assertEquals('2010', $account->getBankCode()); - $this->assertEquals('CZK', $account->getCurrency()); - $this->assertEquals('CZ9820100000000214498596', $account->getIban()); - $this->assertEquals('FIOBCZPPXXX', $account->getBic()); + $this->assertSame('214498596', $account->getAccountNumber()); + $this->assertSame('2010', $account->getBankCode()); + $this->assertSame('CZK', $account->getCurrency()); + $this->assertSame('CZ9820100000000214498596', $account->getIban()); + $this->assertSame('FIOBCZPPXXX', $account->getBic()); } } diff --git a/tests/FioApi/DownloaderTest.php b/tests/FioApi/DownloaderTest.php index 5985d1b..e4d5df5 100644 --- a/tests/FioApi/DownloaderTest.php +++ b/tests/FioApi/DownloaderTest.php @@ -1,4 +1,5 @@ $handler])); - $downloader->downloadSince(new \DateTime('-1 week')); + + $this->expectException(\FioApi\Exceptions\TooGreedyException::class); + + $downloader->downloadSince(new \DateTimeImmutable('-1 week')); } - /** - * @expectedException \FioApi\Exceptions\InternalErrorException - */ public function testInvalidTokenResultsInInternalErrorException() { $handler = HandlerStack::create(new MockHandler([ new Response(500), ])); $downloader = new Downloader('invalidToken', new Client(['handler' => $handler])); - $downloader->downloadSince(new \DateTime('-1 week')); + + $this->expectException(\FioApi\Exceptions\InternalErrorException::class); + + $downloader->downloadSince(new \DateTimeImmutable('-1 week')); } - /** - * @expectedException \GuzzleHttp\Exception\BadResponseException - * @expectedExceptionCode 418 - */ public function testUnknownResponseCodePassesOriginalException() { $handler = HandlerStack::create(new MockHandler([ new Response(418), ])); $downloader = new Downloader('validToken', new Client(['handler' => $handler])); - $downloader->downloadSince(new \DateTime('-1 week')); + + $this->expectException(\GuzzleHttp\Exception\BadResponseException::class); + $this->expectExceptionCode(418); + + $downloader->downloadSince(new \DateTimeImmutable('-1 week')); } public function testDownloaderDownloadsData() @@ -53,14 +54,16 @@ public function testDownloaderDownloadsData() new Response(200, [], file_get_contents(__DIR__ . '/data/example-response.json')), ])); $downloader = new Downloader('validToken', new Client(['handler' => $handler])); - $transactionList = $downloader->downloadSince(new \DateTime('-1 week')); - $this->assertInstanceOf('\FioApi\TransactionList', $transactionList); + + $transactionList = $downloader->downloadSince(new \DateTimeImmutable('-1 week')); + + $this->assertInstanceOf(TransactionList::class, $transactionList); } public function testDownloaderSetCertificatePath() { $downloader = new Downloader('validToken'); $downloader->setCertificatePath('foo.pem'); - $this->assertEquals('foo.pem', $downloader->getCertificatePath()); + $this->assertSame('foo.pem', $downloader->getCertificatePath()); } } diff --git a/tests/FioApi/TransactionListTest.php b/tests/FioApi/TransactionListTest.php index dbf0128..39b817b 100644 --- a/tests/FioApi/TransactionListTest.php +++ b/tests/FioApi/TransactionListTest.php @@ -1,8 +1,9 @@ accountStatement); - $this->assertSame(500, $transactionList->getOpeningBalance()); - $this->assertSame(1000, $transactionList->getClosingBalance()); - $this->assertEquals(new \DateTime('2015-03-30+0200'), $transactionList->getDateStart()); - $this->assertEquals(new \DateTime('2015-03-31+0200'), $transactionList->getDateEnd()); - $this->assertSame(1111111111, $transactionList->getIdFrom()); - $this->assertSame(1111111999, $transactionList->getIdTo()); + $this->assertSame(500.0, $transactionList->getOpeningBalance()); + $this->assertSame(1000.0, $transactionList->getClosingBalance()); + $this->assertEquals(new \DateTimeImmutable('2015-03-30+0200'), $transactionList->getDateStart()); + $this->assertEquals(new \DateTimeImmutable('2015-03-31+0200'), $transactionList->getDateEnd()); + $this->assertSame((float) 1111111111, $transactionList->getIdFrom()); + $this->assertSame((float) 1111111999, $transactionList->getIdTo()); $this->assertSame(null, $transactionList->getIdLastDownload()); - $this->assertInstanceOf('FioApi\Account', $transactionList->getAccount()); - $this->assertInstanceOf('FioApi\Transaction', $transactionList->getTransactions()[0]); + $this->assertInstanceOf(Account::class, $transactionList->getAccount()); + $this->assertInstanceOf(Transaction::class, $transactionList->getTransactions()[0]); + } + + public function testEmptyTransactionList() + { + $transactionList = json_decode(file_get_contents(__DIR__ . '/data/example-empty-response.json')); + + $transactionList = TransactionList::create($transactionList->accountStatement); + + $this->assertSame(0.0, $transactionList->getOpeningBalance()); + $this->assertSame(0.0, $transactionList->getClosingBalance()); + $this->assertEquals(new \DateTimeImmutable('2017-08-06+0200'), $transactionList->getDateStart()); + $this->assertEquals(new \DateTimeImmutable('2017-08-08+0200'), $transactionList->getDateEnd()); + $this->assertNull($transactionList->getIdFrom()); + $this->assertNull($transactionList->getIdTo()); + $this->assertNull($transactionList->getIdLastDownload()); + $this->assertInstanceOf(Account::class, $transactionList->getAccount()); + $this->assertCount(0, $transactionList->getTransactions()); } } diff --git a/tests/FioApi/TransactionTest.php b/tests/FioApi/TransactionTest.php index c4c5b46..6a31741 100644 --- a/tests/FioApi/TransactionTest.php +++ b/tests/FioApi/TransactionTest.php @@ -1,8 +1,9 @@ assertSame(1111111111, $transaction->getId()); - $this->assertEquals(new \DateTime('2015-03-30+0200'), $transaction->getDate()); + $this->assertSame('1111111111', $transaction->getId()); + $this->assertEquals(new \DateTimeImmutable('2015-03-30+0200'), $transaction->getDate()); $this->assertSame(127.0, $transaction->getAmount()); $this->assertSame('CZK', $transaction->getCurrency()); $this->assertSame('214498596', $transaction->getSenderAccountNumber()); @@ -25,7 +26,7 @@ public function testAccountValuesAreProperlySet() $this->assertSame('Bezhotovostní příjem', $transaction->getTransactionType()); $this->assertSame(null, $transaction->getPerformedBy()); $this->assertSame('Comment? Comment!', $transaction->getComment()); - $this->assertSame(1111122222, $transaction->getPaymentOrderId()); + $this->assertSame((float) 1111122222, $transaction->getPaymentOrderId()); $this->assertSame('1500.00 EUR', $transaction->getSpecification()); } } diff --git a/tests/FioApi/UrlBuilderTest.php b/tests/FioApi/UrlBuilderTest.php index 4e6ddd9..e1c38e2 100644 --- a/tests/FioApi/UrlBuilderTest.php +++ b/tests/FioApi/UrlBuilderTest.php @@ -1,36 +1,39 @@ expectException(\FioApi\Exceptions\MissingTokenException::class); + new UrlBuilder(''); } public function testTokenCanBeSetThroughConstructor() { $urlBuilder = new UrlBuilder('token1'); - $this->assertEquals('token1', $urlBuilder->getToken()); + $this->assertSame('token1', $urlBuilder->getToken()); } public function testTokenCanBeChangedThroughSetter() { $urlBuilder = new UrlBuilder('token1'); $urlBuilder->setToken('token2'); - $this->assertEquals('token2', $urlBuilder->getToken()); + $this->assertSame('token2', $urlBuilder->getToken()); } public function testBuildPeriodsUrlReturnValidUrl() { $urlBuilder = new UrlBuilder('token1'); - $this->assertEquals( + $this->assertSame( 'https://www.fio.cz/ib_api/rest/periods/token1/2015-03-25/2015-03-31/transactions.json', - $urlBuilder->buildPeriodsUrl(new \DateTime('2015-03-25'), new \DateTime('2015-03-31')) + $urlBuilder->buildPeriodsUrl( + new \DateTimeImmutable('2015-03-25'), + new \DateTimeImmutable('2015-03-31') + ) ); } } diff --git a/tests/FioApi/data/example-empty-response.json b/tests/FioApi/data/example-empty-response.json new file mode 100644 index 0000000..fccd0d8 --- /dev/null +++ b/tests/FioApi/data/example-empty-response.json @@ -0,0 +1,23 @@ +{ + "accountStatement": { + "info": { + "accountId": "260000000", + "bankId": "2010", + "currency": "EUR", + "iban": "CZ652010000000000", + "bic": "FIOBCZPPXXX", + "openingBalance": 0.00, + "closingBalance": 0.00, + "dateStart": "2017-08-06+0200", + "dateEnd": "2017-08-08+0200", + "yearList": null, + "idList": null, + "idFrom": null, + "idTo": null, + "idLastDownload": null + }, + "transactionList": { + "transaction": [] + } + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 52fb375..24a050e 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,3 +1,5 @@ addPsr4('FioApi\\', __DIR__ . '/FioApi');