diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 9160059..0000000 --- a/.coveralls.yml +++ /dev/null @@ -1 +0,0 @@ -service_name: travis-ci diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..7e0764d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: Test +on: [push, pull_request] +jobs: + test: + name: Test PHP ${{ matrix.php-version }} + runs-on: ubuntu-latest + strategy: + matrix: + php-version: ['8.0', '8.1', '8.2', '8.3', '8.4'] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + - name: PHP Version + run: php --version + - name: Composer Version + run: composer --version + - name: Install Dependencies + run: COMPOSER_MEMORY_LIMIT=-1 composer install + - name: Run Tests + run: vendor/bin/phpunit --coverage-clover ./coverage.xml + - name: Upload Coverage + if: ${{ matrix.php-version == '8.2' }} + uses: codecov/codecov-action@v1 + with: + file: ./coverage.xml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index eb67138..0000000 --- a/.travis.yml +++ /dev/null @@ -1,46 +0,0 @@ -language: php -dist: bionic -sudo: required - -matrix: - include: - - php: 5.3 - dist: precise - - php: 5.4 - dist: trusty - - php: 5.5 - dist: trusty - - php: 5.6 - dist: trusty - - php: 7.0 - dist: xenial - env: PHPUNIT=5.7 - - php: 7.1 - env: PHPUNIT=7.5 - - php: 7.2 - - php: 7.3 - env: COVERAGE=true - -install: - - composer install - - | - if [[ $PHPUNIT ]]; then - composer require "phpunit/phpunit:$PHPUNIT" - fi - -before_script: - - mkdir -p build/logs - -script: - - | - if [[ $PHPUNIT ]]; then - vendor/bin/phpunit --coverage-clover build/logs/clover.xml - else - phpunit --coverage-clover build/logs/clover.xml - fi - -after_success: - - | - if [[ $COVERAGE ]]; then - php vendor/bin/coveralls --config .coveralls.yml -v - fi diff --git a/LICENSE b/LICENSE index 7dcf161..ab5ad02 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016, 2017, 2018, 2019 Gravity Boulevard, LLC +Copyright (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Gravity Boulevard, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 302867a..81a1456 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,12 @@ # Holiday API PHP Library -[![License](https://img.shields.io/npm/l/holidayapi-php?style=for-the-badge)](https://github.com/holidayapi/holidayapi-php/blob/master/LICENSE) +[![License](https://img.shields.io/packagist/l/holidayapi/holidayapi-php?style=for-the-badge)](https://github.com/holidayapi/holidayapi-php/blob/master/LICENSE) ![PHP Version](https://img.shields.io/packagist/php-v/holidayapi/holidayapi-php?style=for-the-badge) -![Build Status](https://img.shields.io/travis/holidayapi/holidayapi-php/master?style=for-the-badge) -[![Coverage Status](https://img.shields.io/coveralls/github/holidayapi/holidayapi-php/master?style=for-the-badge)](https://coveralls.io/github/holidayapi/holidayapi-php?branch=master) +[![Code Coverage](https://img.shields.io/codecov/c/github/holidayapi/holidayapi-php?style=for-the-badge)](https://codecov.io/gh/holidayapi/holidayapi-php) Official PHP library for [Holiday API](https://holidayapi.com) providing quick and easy access to holiday information from applications written in PHP. -## Migrating from 1.x - -Please note, version 2.x of this library is a full rewrite of the 1.x series. -The interfacing to the library has been simplified and existing applications -upgrading to 2.x will need to be updated. - -| Version 1.x Syntax (Old) | Version 2.x Syntax (New) | -|--------------------------------------------|-----------------------------------------------------------| -| `$holiday_api = new \HolidayAPI\v1($key);` | `$holiday_api = new \HolidayAPI\Client(['key' => $key]);` | - -Version 1.x of the library can still be found -[here](https://github.com/joshtronic/php-holidayapi). - ## Documentation Full documentation of the Holiday API endpoints is available @@ -35,6 +21,7 @@ composer require holidayapi/holidayapi-php ## Usage ```php + $key]); @@ -64,12 +51,32 @@ try { #### Fetch all supported countries ```php +countries(); ``` -#### Search for a country by code or name +#### Fetch only countries with public holidays ```php +countries([ + 'public' => true, +]); +``` + +#### Fetch a supported country by code + +```php +countries([ + 'country' => 'NO', +]); +``` + +#### Search for countries by code or name + +```php +countries([ 'search' => 'united', ]); @@ -80,12 +87,23 @@ $holiday_api->countries([ #### Fetch all supported languages ```php +languages(); ``` -#### Search for a language by code or name +#### Fetch a supported language by code ```php +languages([ + 'language' => 'es', +]); +``` + +#### Search for languages by code or name + +```php +languages([ 'search' => 'Chinese', ]); @@ -96,6 +114,7 @@ $holiday_api->languages([ #### Fetch holidays for a specific year ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -105,6 +124,7 @@ $holiday_api->holidays([ #### Fetch holidays for a specific month ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -115,6 +135,7 @@ $holiday_api->holidays([ #### Fetch holidays for a specific day ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -126,6 +147,7 @@ $holiday_api->holidays([ #### Fetch upcoming holidays based on a specific date ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -138,6 +160,7 @@ $holiday_api->holidays([ #### Fetch previous holidays based on a specific date ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -150,6 +173,7 @@ $holiday_api->holidays([ #### Fetch only public holidays ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -160,6 +184,7 @@ $holiday_api->holidays([ #### Fetch holidays for a specific subdivision ```php +holidays([ 'country' => 'GB-ENG', 'year' => 2019, @@ -169,6 +194,7 @@ $holiday_api->holidays([ #### Include subdivision holidays with countrywide holidays ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -179,6 +205,7 @@ $holiday_api->holidays([ #### Search for a holiday by name ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -189,6 +216,7 @@ $holiday_api->holidays([ #### Translate holidays to another language ```php +holidays([ 'country' => 'US', 'year' => 2019, @@ -199,6 +227,7 @@ $holiday_api->holidays([ #### Fetch holidays for multiple countries ```php +holidays([ 'country' => 'US,GB,NZ', 'year' => 2019, @@ -209,3 +238,29 @@ $holiday_api->holidays([ 'year' => 2019, ]); ``` + +### Workday + +#### Fetch workday 7 business days after a date + +```php +workday([ + 'country' => 'US', + 'start' => '2019-07-01', + 'days' => 7, +]); +``` + +### Workdays + +#### Fetch number of workdays between two dates + +```php +workdays([ + 'country' => 'US', + 'start' => '2019-07-01', + 'end' => '2019-07-10', +]); +``` diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..657d8f7 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,20 @@ +codecov: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "reach,diff,flags,tree" + behavior: default + require_changes: no diff --git a/composer.json b/composer.json index c6fa09f..3129ca8 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "holidayapi/holidayapi-php", "description": "Official PHP library for Holiday API", - "version": "2.0.0", + "version": "4.0.0", "type": "library", "keywords": [ "calendar", @@ -17,12 +17,21 @@ "homepage": "https://holidayapi.com" }], "require": { - "php": ">=5.3.0" + "php": ">=8.0", + "ext-json": "*", + "ext-curl": "*" }, "require-dev": { - "satooshi/php-coveralls": "~1.0" + "phpunit/phpunit": "^9.0" }, "autoload": { - "psr-4": { "HolidayAPI\\": "src/" } + "psr-4": { + "HolidayAPI\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "HolidayAPI\\Tests\\": "tests/" + } } } diff --git a/phpunit.xml b/phpunit.xml index fa05f6b..92f2230 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,7 @@ handler->get($this->createUrl($endpoint, $request)); - - /* - $curl = curl_init(); - - curl_setopt_array($curl, array( - CURLOPT_URL => $this->createUrl($endpoint, $request), - CURLOPT_HEADER => false, - CURLOPT_SSL_VERIFYPEER => true, - CURLOPT_RETURNTRANSFER => true, - )); - - $response = curl_exec($curl); - - if ($error = curl_error($curl)) { - throw new \Exception($error); - } - - curl_close($curl); - $response = json_decode($response, true); - - if (!$response) { - throw new \Exception('Empty response received'); - } - - return $response; - */ } public function countries($request = array()) @@ -90,17 +64,6 @@ class Client public function holidays($request) { - if (!isset($request['country'])) { - throw new \Exception('Missing country'); - } elseif (!isset($request['year'])) { - throw new \Exception('Missing year'); - } elseif ( - isset($request['previous'], $request['upcoming']) - && $request['previous'] && $request['upcoming'] - ) { - throw new \Exception('Previous and upcoming are mutually exclusive'); - } - return $this->request('holidays', $request); } @@ -108,5 +71,15 @@ class Client { return $this->request('languages', $request); } + + public function workday($request) + { + return $this->request('workday', $request); + } + + public function workdays($request) + { + return $this->request('workdays', $request); + } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index c77611a..14f83c7 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -2,56 +2,72 @@ namespace HolidayAPI\Tests; use HolidayAPI\Client; use HolidayAPI\Request; +use PHPUnit\Framework\TestCase; -require __DIR__ . '/../vendor/autoload.php'; - -if ( - !class_exists('\PHPUnit_Framework_TestCase') - && class_exists('\PHPUnit\Framework\TestCase') -) { - class_alias('\PHPUnit\Framework\TestCase', '\PHPUnit_Framework_TestCase'); -} - -class ClientTest extends \PHPUnit_Framework_TestCase +class ClientTest extends TestCase { const BASE_URL = 'https://holidayapi.com/v1/'; const KEY = '8e4de28c-4b18-49f0-9aba-0bd6b424fc38'; public function testMissingKey() { + if (version_compare(PHP_VERSION, '7.3.0', '>=')) { + $assertRegExp = 'assertMatchesRegularExpression'; + } else { + $assertRegExp = 'assertRegExp'; + } + try { - $client = new Client(array()); + new Client(array()); } catch (\Exception $e) { - $this->assertRegExp('/missing api key/i', $e->getMessage()); + $this->$assertRegExp('/missing api key/i', $e->getMessage()); } } public function testInvalidKey() { + if (version_compare(PHP_VERSION, '7.3.0', '>=')) { + $assertRegExp = 'assertMatchesRegularExpression'; + } else { + $assertRegExp = 'assertRegExp'; + } + try { - $client = new Client(array( + new Client(array( 'key' => 'zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz', )); } catch (\Exception $e) { - $this->assertRegExp('/invalid api key/i', $e->getMessage()); + $this->$assertRegExp('/invalid api key/i', $e->getMessage()); } } public function testVersionTooLow() { + if (version_compare(PHP_VERSION, '7.3.0', '>=')) { + $assertRegExp = 'assertMatchesRegularExpression'; + } else { + $assertRegExp = 'assertRegExp'; + } + try { - $client = new Client(array('key' => self::KEY, 'version' => 0)); + new Client(array('key' => self::KEY, 'version' => 0)); } catch (\Exception $e) { - $this->assertRegExp('/invalid version/i', $e->getMessage()); + $this->$assertRegExp('/invalid version/i', $e->getMessage()); } } public function testVersionTooHigh() { + if (version_compare(PHP_VERSION, '7.3.0', '>=')) { + $assertRegExp = 'assertMatchesRegularExpression'; + } else { + $assertRegExp = 'assertRegExp'; + } + try { - $client = new Client(array('key' => self::KEY, 'version' => 2)); + new Client(array('key' => self::KEY, 'version' => 2)); } catch (\Exception $e) { - $this->assertRegExp('/invalid version/i', $e->getMessage()); + $this->$assertRegExp('/invalid version/i', $e->getMessage()); } } @@ -97,40 +113,6 @@ class ClientTest extends \PHPUnit_Framework_TestCase ), $client->countries()); } - public function testSearchCountries() - { - $url = self::BASE_URL . 'countries?key=' . self::KEY . '&search=Sao'; - - $request = new Request(array( - 'execute' => array( - $url => function () - { - return json_encode(array( - 'status' => 200, - 'countries' => array( - array( - 'code' => 'ST', - 'name' => 'Sao Tome and Principle', - ), - ), - )); - }, - ), - )); - - $client = new Client(array('key' => self::KEY, 'handler' => $request)); - - $this->assertEquals(array( - 'status' => 200, - 'countries' => array( - array( - 'code' => 'ST', - 'name' => 'Sao Tome and Principle', - ), - ), - ), $client->countries(array('search' => 'Sao'))); - } - public function testCountriesRaise4xxErrors() { $url = self::BASE_URL . 'countries?key=' . self::KEY; @@ -187,8 +169,7 @@ class ClientTest extends \PHPUnit_Framework_TestCase public function testReturnHolidays() { - $url = self::BASE_URL . 'holidays?key=' . self::KEY - . '&country=US&year=2015&month=7&day=4'; + $url = self::BASE_URL . 'holidays?key=' . self::KEY . '&country=US&year=2015&month=7&day=4'; $request = new Request(array( 'execute' => array( @@ -229,89 +210,6 @@ class ClientTest extends \PHPUnit_Framework_TestCase ))); } - public function testSearchHolidays() - { - $url = self::BASE_URL . 'holidays?key=' . self::KEY - . '&country=US&year=2015&search=Independence'; - - $request = new Request(array( - 'execute' => array( - $url => function () - { - return json_encode(array( - 'status' => 200, - 'holidays' => array( - array( - 'name' => 'Independence Day', - 'date' => '2015-07-04', - 'observed' => '2015-07-03', - 'public' => true, - ), - ), - )); - }, - ), - )); - - $client = new Client(array('key' => self::KEY, 'handler' => $request)); - - $this->assertEquals(array( - 'status' => 200, - 'holidays' => array( - array( - 'name' => 'Independence Day', - 'date' => '2015-07-04', - 'observed' => '2015-07-03', - 'public' => true, - ), - ), - ), $client->holidays(array( - 'country' => 'US', - 'year' => 2015, - 'search' => 'Independence', - ))); - } - - public function testCountryMissing() - { - $client = new Client(array('key' => self::KEY)); - - try { - $client->holidays(array('year' => 2015)); - } catch (\Exception $e) { - $this->assertRegExp('/missing country/i', $e->getMessage()); - } - } - - public function testYearMissing() - { - $client = new Client(array('key' => self::KEY)); - - try { - $client->holidays(array('country' => 'US')); - } catch (\Exception $e) { - $this->assertRegExp('/missing year/i', $e->getMessage()); - } - } - - public function testBothPreviousAndUpcoming() - { - $client = new Client(array('key' => self::KEY)); - - try { - $client->holidays(array( - 'country' => 'US', - 'year' => 2015, - 'month' => 7, - 'day' => 4, - 'upcoming' => true, - 'previous' => true, - )); - } catch (\Exception $e) { - $this->assertRegExp('/previous and upcoming/i', $e->getMessage()); - } - } - public function testHolidaysRaise4xxErrors() { $url = self::BASE_URL . 'holidays?key=' . self::KEY . '&country=US&year=2019'; @@ -400,40 +298,6 @@ class ClientTest extends \PHPUnit_Framework_TestCase ), $client->languages()); } - public function testSearchLanguages() - { - $url = self::BASE_URL . 'languages?key=' . self::KEY . '&search=Eng'; - - $request = new Request(array( - 'execute' => array( - $url => function () - { - return json_encode(array( - 'status' => 200, - 'languages' => array( - array( - 'code' => 'en', - 'name' => 'English', - ), - ), - )); - }, - ), - )); - - $client = new Client(array('key' => self::KEY, 'handler' => $request)); - - $this->assertEquals(array( - 'status' => 200, - 'languages' => array( - array( - 'code' => 'en', - 'name' => 'English', - ), - ), - ), $client->languages(array('search' => 'Eng'))); - } - public function testLanguagesRaise4xxErrors() { $url = self::BASE_URL . 'languages?key=' . self::KEY; @@ -487,5 +351,189 @@ class ClientTest extends \PHPUnit_Framework_TestCase $this->assertSame('Internal server error', $e->getMessage()); } } + + public function testReturnWorkday() + { + $url = self::BASE_URL . 'workday?key=' . self::KEY . '&country=US&start=2019-07-01&days=10'; + + $request = new Request(array( + 'execute' => array( + $url => function () + { + return json_encode(array( + 'status' => 200, + 'workday' => array( + 'date' => '2019-07-16', + ), + )); + }, + ), + )); + + $client = new Client(array('key' => self::KEY, 'handler' => $request)); + + $this->assertEquals(array( + 'status' => 200, + 'workday' => array( + 'date' => '2019-07-16', + ), + ), $client->workday(array( + 'country' => 'US', + 'start' => '2019-07-01', + 'days' => 10, + ))); + } + + public function testWorkdayRaise4xxErrors() + { + $url = self::BASE_URL . 'workday?key=' . self::KEY . '&country=US&start=2019-07-01&days=10'; + + $request = new Request(array( + 'execute' => array( + $url => function () + { + return json_encode(array( + 'status' => 429, + 'error' => 'Rate limit exceeded', + )); + }, + ), + )); + + $client = new Client(array('key' => self::KEY, 'handler' => $request)); + + try { + $client->workday(array( + 'country' => 'US', + 'start' => '2019-07-01', + 'days' => 10, + )); + } catch (\Exception $e) { + $this->assertSame(429, $e->getCode()); + $this->assertSame('Rate limit exceeded', $e->getMessage()); + } + } + + public function testWorkdayRaise5xxErrors() + { + $url = self::BASE_URL . 'workday?key=' . self::KEY . '&country=US&start=2019-07-01&days=10'; + + $request = new Request(array( + 'execute' => array( + $url => function () + { + return false; + }, + ), + 'error' => array( + $url => function () + { + return 'Internal server error'; + }, + ), + )); + + $client = new Client(array('key' => self::KEY, 'handler' => $request)); + + try { + $client->workday(array( + 'country' => 'US', + 'start' => '2019-07-01', + 'days' => 10, + )); + } catch (\Exception $e) { + $this->assertSame('Internal server error', $e->getMessage()); + } + } + + public function testReturnWorkdays() + { + $url = self::BASE_URL . 'workdays?key=' . self::KEY . '&country=US&start=2019-07-01&end=2019-07-10'; + + $request = new Request(array( + 'execute' => array( + $url => function () + { + return json_encode(array( + 'status' => 200, + 'workdays' => 7, + )); + }, + ), + )); + + $client = new Client(array('key' => self::KEY, 'handler' => $request)); + + $this->assertEquals(array( + 'status' => 200, + 'workdays' => 7, + ), $client->workdays(array( + 'country' => 'US', + 'start' => '2019-07-01', + 'end' => '2019-07-10', + ))); + } + + public function testWorkdaysRaise4xxErrors() + { + $url = self::BASE_URL . 'workdays?key=' . self::KEY . '&country=US&start=2019-07-01&end=2019-07-10'; + + $request = new Request(array( + 'execute' => array( + $url => function () + { + return json_encode(array( + 'status' => 429, + 'error' => 'Rate limit exceeded', + )); + }, + ), + )); + + $client = new Client(array('key' => self::KEY, 'handler' => $request)); + + try { + $client->workdays(array( + 'country' => 'US', + 'start' => '2019-07-01', + 'end' => '2019-07-10', + )); + } catch (\Exception $e) { + $this->assertSame(429, $e->getCode()); + $this->assertSame('Rate limit exceeded', $e->getMessage()); + } + } + + public function testWorkdaysRaise5xxErrors() + { + $url = self::BASE_URL . 'workdays?key=' . self::KEY . '&country=US&start=2019-07-01&end=2019-07-10'; + + $request = new Request(array( + 'execute' => array( + $url => function () + { + return false; + }, + ), + 'error' => array( + $url => function () + { + return 'Internal server error'; + }, + ), + )); + + $client = new Client(array('key' => self::KEY, 'handler' => $request)); + + try { + $client->workdays(array( + 'country' => 'US', + 'start' => '2019-07-01', + 'end' => '2019-07-10', + )); + } catch (\Exception $e) { + $this->assertSame('Internal server error', $e->getMessage()); + } + } } diff --git a/tests/RequestTest.php b/tests/RequestTest.php index 31594f3..f42776d 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -1,18 +1,9 @@ =')) { + $assertRegExp = 'assertMatchesRegularExpression'; + } else { + $assertRegExp = 'assertRegExp'; + } + $url = 'https://holidayapi.com'; $request = new Request(array( @@ -38,7 +35,7 @@ class RequestTest extends \PHPUnit_Framework_TestCase try { $request->get($url); } catch (\Exception $e) { - $this->assertRegExp('/empty response/i', $e->getMessage()); + $this->$assertRegExp('/empty response/i', $e->getMessage()); } } }