From 7e95c1616bbb9e148ba15d2c6b6771ab008cc1d3 Mon Sep 17 00:00:00 2001 From: Fabian Beiner Date: Thu, 2 Jan 2020 15:00:38 +0100 Subject: [PATCH] Welcome v1.0.0, fully compatible with all endpoints. --- .editorconf | 7 +- .gitattributes | 81 ++-- .gitignore | 7 +- .scrutinizer.yml | 37 +- .styleci.yml | 16 +- .travis.yml | 15 +- CHANGELOG.md | 26 +- CODE_OF_CONDUCT.md | 102 ++--- CONTRIBUTORS.md | 4 - README.md | 67 +--- composer.json | 8 +- composer.lock | 361 ++++++++++-------- src/FabianBeiner/Todoist/TodoistClient.php | 100 ++--- .../Todoist/TodoistCommentsTrait.php | 144 +++---- src/FabianBeiner/Todoist/TodoistException.php | 3 +- src/FabianBeiner/Todoist/TodoistHelpers.php | 81 ++++ .../Todoist/TodoistLabelsTrait.php | 96 ++--- .../Todoist/TodoistProjectsTrait.php | 98 ++--- .../Todoist/TodoistSectionsTrait.php | 118 ++++++ .../Todoist/TodoistTasksTrait.php | 107 +++--- .../Todoist/TodoistClientTest.php | 190 ++++++--- .../Todoist/TodoistExceptionTest.php | 10 +- 22 files changed, 943 insertions(+), 735 deletions(-) create mode 100644 src/FabianBeiner/Todoist/TodoistHelpers.php create mode 100644 src/FabianBeiner/Todoist/TodoistSectionsTrait.php diff --git a/.editorconf b/.editorconf index 818b968..76e991f 100644 --- a/.editorconf +++ b/.editorconf @@ -1,15 +1,18 @@ -# EditorConfig — http://editorconfig.org/ +# EditorConfig — https://editorconfig.org/ root = true [*] charset = utf-8 end_of_line = lf -indent_size = 4 +indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true +[*.php] +indent_size = 4 + [*.md] trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes index 4afb4de..e551946 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -# Define Git attributes (https://git-scm.com/docs/gitattributes) +# Defining Git attributes (https://git-scm.com/docs/gitattributes) # As per default, as well as a fallback, we let Git handle line endings # automatically for files detected as text. @@ -17,8 +17,8 @@ CODE_OF_CONDUCT.md export-ignore ISSUE_TEMPLATE.md export-ignore phpunit.xml.dist export-ignore -# The following settings are based on the November 30th, 2017 release of -# https://raw.githubusercontent.com/alexkaratarakis/gitattributes/master/Web.gitattributes +# The following settings are based on the May 10th, 2019 release of +# https://github.com/alexkaratarakis/gitattributes/blob/master/Web.gitattributes # by Alexander Karatarakis. # Archives @@ -39,55 +39,69 @@ phpunit.xml.dist export-ignore *.ra binary # Code +*.bash text eol=lf *.bat text eol=crlf +*.cmd text eol=crlf *.coffee text *.css text -*.html text -*.htm text +*.html text diff=html +*.htm text diff=html *.inc text *.ini text *.json text *.js text *.jsx text *.less text +*.ls text +*.map text -diff *.od text *.onlydata text -*.php text +*.php text diff=php *.pl text -*.py text -*.rb text +*.ps1 text eol=crlf +*.py text diff=python +*.rb text diff=ruby *.sass text *.scm text -*.scss text +*.scss text diff=css *.sh text eol=lf *.sql text *.styl text *.tag text *.ts text *.tsx text -*.xhtml text +*.xhtml text diff=html *.xml text # Configs -*.bowerrc text -*.cnf text -*.config text -*.conf text -*.dist text -*.iml text -*.npmignore text -*.yaml text -*.yml text -.browserslistrc text -.editorconfig text -.gitattributes text -.gitconfig text -.htaccess text -browserslist text -makefile text -Makefile text +*.bowerrc text +*.cnf text +*.config text +*.conf text +*.dist text +*.iml text +*.lock text -diff +*.npmignore text +*.yaml text +*.yml text +.babelrc text +.browserslistrc text +.editorconfig text +.env text +.gitattributes text +.gitconfig text +.htaccess text +browserslist text +Makefile text +makefile text +package-lock.json text -diff + +# Docker +*.dockerignore text +Dockerfile text # Documentation +*.ipynb text *.markdown text *.mdown text *.md text @@ -106,8 +120,8 @@ CONTRIBUTING text COPYING text copyright text INSTALL text -LICENSE text license text +LICENSE text NEWS text readme text TODO text @@ -124,11 +138,16 @@ TODO text *.woff2 binary *.woff binary +# Heroku +.slugignore text +Procfile text + # Images *.ai binary *.bmp binary *.eps binary *.gif binary +*.gifv binary *.ico binary *.jng binary *.jp2 binary @@ -157,9 +176,8 @@ TODO text .stylelintrc text # Misc -*.lock text -*.log text -*.url text +*.log text +*.url text # Templates *.dot text @@ -176,6 +194,7 @@ TODO text *.tmpl text *.tpl text *.twig text +*.vue text # Video *.3gp binary diff --git a/.gitignore b/.gitignore index 5d310e5..498176f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,5 @@ -.AppleDouble -.DS_Store -.LSOverride +.phpunit.result.cache .php_cs.cache -composer.phar -Desktop.ini /.idea/ /vendor/ phpunit.xml -Thumbs.db diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 016f52d..b1a2ed0 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,18 +1,25 @@ +build: + nodes: + analysis: + tests: + override: + - php-scrutinizer-run + filter: - excluded_paths: [tests/*] + excluded_paths: [tests/*] checks: - php: - remove_extra_empty_lines: true - remove_php_closing_tag: true - remove_trailing_whitespace: true - fix_use_statements: - remove_unused: true - preserve_multiple: false - preserve_blanklines: true - order_alphabetically: true - fix_php_opening_tag: true - fix_linefeed: true - fix_line_ending: true - fix_identation_4spaces: true - fix_doc_comments: true \ No newline at end of file + php: + remove_extra_empty_lines: true + remove_php_closing_tag: true + remove_trailing_whitespace: true + fix_use_statements: + remove_unused: true + preserve_multiple: false + preserve_blanklines: true + order_alphabetically: true + fix_php_opening_tag: true + fix_linefeed: true + fix_line_ending: true + fix_identation_4spaces: true + fix_doc_comments: true diff --git a/.styleci.yml b/.styleci.yml index 4011d93..c706e6d 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,16 +1,18 @@ preset: recommended +finder: + path: + - "src" + - "tests" + enabled: - align_equals - concat_with_spaces - not_operator_with_space disabled: - - concat_without_spaces - unalign_equals - - trailing_comma_in_multiline_array - -finder: - path: - - "src" - - "tests" + - concat_without_spaces + - phpdoc_separation + - phpdoc_no_package + - cast_spaces diff --git a/.travis.yml b/.travis.yml index 3892bbd..8569f0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,19 @@ language: php +dist: bionic + php: - - 7.1 - 7.2 - 7.3 - -sudo: false + - 7.4 cache: directories: - - $HOME/.composer/cache + - $HOME/.composer/cache/files before_script: - - travis_retry composer self-update - - travis_retry composer update --no-interaction --prefer-dist + - travis_retry composer self-update && composer --version + - travis_retry composer update --prefer-dist --no-interaction script: - vendor/bin/phpunit --coverage-clover=coverage.xml - -after_success: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4abc9..215633b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ -# Change Log -All notable changes to this project will be documented in this file. +# Changelog +All notable changes to this project get documented in this file. _Unless I forget it. Sorry._ + +## 1.0.0 - 2020-01-02 +**This version is finally compatible with every available endpoint of the official REST API. Please check out the Wiki for more information on the methods, and many examples.** + +### Changed +- Heavily updated the code. This might have introduced a few breaking changes, but I am currently not sure. 🤷‍ +- Improved almost everything, and added the Section endpoints. + +## 0.8.2 - 2020-01-01 +### Changed +- `getAllTasks` has now an optional ID for a project. +- Updated Composer packages. + +## 0.8.1 - 2019-11-28 +### Fixed +- There was a typo in the endpoint of the reopen method. +### Changed +- Some coding style changes, updated *.md & Dot files etc. +- Updated Composer packages. + +## 0.8.0 - 2019-07-19 +- _Changelog missing. Sorry!_ ## 0.7.1 - 2018-05-29 ### Fixed diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 4355b19..8ba846f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,75 +2,83 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards -Examples of behavior that contributes to creating a positive environment -include: +Examples of behavior that contributes to a positive environment for our community include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community -Examples of unacceptable behavior by participants include: +Examples of unacceptable behavior include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission +* Publishing others' private information, such as a physical or email + address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting -## Our Responsibilities +## Enforcement Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope -This Code of Conduct applies within all project spaces, and it also applies when -an individual is representing the project or its community in public spaces. -Examples of representing a project or community include using an official -project e-mail address, posting via an official social media account, or acting -as an appointed representative at an online or offline event. Representation of -a project may be further defined and clarified by project maintainers. +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at fb@fabianbeiner.de. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. + +### 2. Warning -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +**Community Impact**: A violation through a single incident or series of actions. + +**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the project community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, +available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 704f669..38f3623 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -4,8 +4,4 @@ These amazing people have contributed to this repository. * **[Balazs Csaba](https://github.com/balazscsaba2006)** - * Added the `TodoistTasksTrait` - * Contributed overall improvements to the code base - * Added tests - Thank you very much! ⭐ diff --git a/README.md b/README.md index 63b5462..6b6ee08 100644 --- a/README.md +++ b/README.md @@ -3,23 +3,16 @@ # PHP Client for Todoist **This repository contains a PHP client library that provides a native interface to the official -[Todoist REST API](https://developer.todoist.com/rest/v1/).** - -*The project is not created by, affiliated with, or supported by Doist.* +[Todoist REST API (v1)](https://developer.todoist.com/rest/v1/).** ## Requirements -- [PHP](https://php.net/): >=7.0 -- [guzzlehttp/guzzle](http://docs.guzzlephp.org/en/stable/): ~6.3 +- [PHP](https://php.net/): >=7.2 +- [Guzzle](https://github.com/guzzle/guzzle): ~6.5 ## Installation -The recommended way is using **[Composer](https://getcomposer.org/)**. You also can **[download the latest release](https://github.com/FabianBeiner/Todoist-PHP-API-Library/releases)** and -start from there. - -### Composer - -If you don’t have Composer installed, follow the [installation instructions](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos). +The recommended way is using **[Composer](https://getcomposer.org/)**. If you don’t have Composer installed, follow the [install instructions](hhttps://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos). Once Composer is installed, execute the following command in your project root to install this library: @@ -27,63 +20,37 @@ Once Composer is installed, execute the following command in your project root t composer require fabian-beiner/todoist-php-api-library ``` -Finally, remember to include the autoloader to your project: +Finally, include the autoloader to your project: ```php require __DIR__ . '/vendor/autoload.php'; ``` -## Obtain your API token - -[Click here](https://todoist.com/prefs/integrations). Your API token is listed at the bottom of this page. - -If the link doesn’t work, open the [Todoist web app](https://todoist.com/app), click on the gear icon ![gear icon image](.github/gear-icon.png), select “Settings,” then “Integrations.” - ## Usage ```php -$Todoist = new FabianBeiner\Todoist\TodoistClient('YOUR_API_TOKEN'); +$Todoist = new FabianBeiner\Todoist\Todoist('YOUR_API_TOKEN'); ``` -## Methods & Examples +[Please have a look at the Wiki of this project.](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki) It contains a list of all available methods and related usage examples. -### [“Projects” methods and examples](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Projects#projects-methods-and-examples) - -* [Get all projects](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Projects#get-all-projects) -* [Create a new project](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Projects#create-a-new-project) -* [Get a project](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Projects#get-a-project) -* [Update a project](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Projects#update-actually-rename-a-project) -* [Delete a project](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Projects#delete-a-project) - -### [“Tasks” methods and examples](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Tasks) - -* [Get tasks](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Tasks) -* [Create a new task](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Tasks) -* [Get a task](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Tasks) -* [Update a task](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Tasks) -* [Close a task](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Tasks) -* [Reopen a task](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Tasks) -* [Delete a task](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Tasks) +## Obtain your API token -### [“Comments” methods and examples](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Comments#comments-methods-and-examples) +[Click here](https://todoist.com/prefs/integrations) to find your API token at the bottom of that page. -* [Get all comments](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Comments#get-all-comments) -* [Create a new comment](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Comments#create-a-new-comment) -* [Get a comment](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Comments#get-a-comment) -* [Update a comment](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Comments#update-a-comment) -* [Delete a comment](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Comments#delete-a-comment) +If the link doesn’t work, open the [Todoist web app](https://todoist.com/app), click on the gear icon ![gear icon image](.github/gear-icon.png), select “Settings,” then “Integrations.” -### [“Labels” methods and examples](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Labels#labels-methods-and-examples) +## Changelog -* [Get all labels](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Labels#get-all-labels) -* [Create a new label](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Labels#create-a-new-label) -* [Get a label](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Labels#get-a-label) -* [Update a label](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Labels#update-actually-rename-a-label) -* [Delete a label](https://github.com/FabianBeiner/Todoist-PHP-API-Library/wiki/Methods:-Labels#delete-a-label) +👉 [CHANGELOG.md](https://github.com/FabianBeiner/Todoist-PHP-API-Library/blob/master/CHANGELOG.md) ## Contributing I’d be happy if you contribute to this library. Please try to follow the existing coding style and use proper comments in your commit message. Thanks! 🙇 ## License -Please see the [license file](https://github.com/FabianBeiner/Todoist-PHP-API-Library/blob/master/LICENSE) for more information. +👉 [LICENSE](https://github.com/FabianBeiner/Todoist-PHP-API-Library/blob/master/LICENSE) + +## Disclaimer + +The project is not created by, affiliated with, or supported by Doist. diff --git a/composer.json b/composer.json index 5fbd0da..8586b64 100644 --- a/composer.json +++ b/composer.json @@ -35,18 +35,18 @@ "source": "https://github.com/FabianBeiner/Todoist-PHP-API-Library/releases" }, "require": { - "guzzlehttp/guzzle": "~6.3", - "php": ">=7.0", + "guzzlehttp/guzzle": "~6.5", + "php": ">=7.2", "ext-json": "*" }, "require-dev": { - "phpunit/phpunit": "^7.2" + "phpunit/phpunit": "^8" }, "scripts": { "test": "phpunit" }, "scripts-descriptions": { - "test": "Run all phpunit tests." + "test": "Run all PHPUnit tests." }, "autoload": { "psr-0": { diff --git a/composer.lock b/composer.lock index a3f7613..ee55b64 100644 --- a/composer.lock +++ b/composer.lock @@ -4,48 +4,50 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5c6d24111f0e15aeb029009d67b10bae", + "content-hash": "482e8d809cd364ffd1ce5c8547e27071", "packages": [ { "name": "guzzlehttp/guzzle", - "version": "6.3.3", + "version": "6.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/43ece0e75098b7ecd8d13918293029e555a50f82", + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82", "shasum": "" }, "require": { + "ext-json": "*", "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", + "guzzlehttp/psr7": "^1.6.1", "php": ">=5.5" }, "require-dev": { "ext-curl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" + "psr/log": "^1.1" }, "suggest": { + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "6.5-dev" } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\": "src/" - } + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -69,7 +71,7 @@ "rest", "web service" ], - "time": "2018-04-22T15:46:56+00:00" + "time": "2019-12-23T11:57:10+00:00" }, { "name": "guzzlehttp/promises", @@ -287,16 +289,16 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", "shasum": "" }, "require": { @@ -339,20 +341,20 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "time": "2019-10-21T16:45:58+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.1", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7", + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7", "shasum": "" }, "require": { @@ -387,7 +389,7 @@ "object", "object graph" ], - "time": "2019-04-07T13:18:21+00:00" + "time": "2019-12-15T19:12:40+00:00" }, { "name": "phar-io/manifest", @@ -493,35 +495,33 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", "shasum": "" }, "require": { - "php": ">=5.5" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "phpunit/phpunit": "~6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -543,31 +543,32 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "time": "2018-08-07T13:53:10+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.1", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", - "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", "shasum": "" }, "require": { "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", + "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", + "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", "webmozart/assert": "^1.0" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", + "doctrine/instantiator": "^1.0.5", "mockery/mockery": "^1.0", + "phpdocumentor/type-resolver": "0.4.*", "phpunit/phpunit": "^6.4" }, "type": "library", @@ -594,41 +595,40 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-04-30T17:48:53+00:00" + "time": "2019-12-28T18:55:12+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.1", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "^7.1", + "mockery/mockery": "~1", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -641,37 +641,38 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2019-08-22T18:11:29+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.1", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc", + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", + "phpspec/phpspec": "^2.5 || ^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.10.x-dev" } }, "autoload": { @@ -704,44 +705,44 @@ "spy", "stub" ], - "time": "2019-06-13T12:50:23+00:00" + "time": "2019-12-22T21:05:45+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "6.1.4", + "version": "7.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", + "php": "^7.2", + "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", + "phpunit/php-token-stream": "^3.1.1", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1 || ^4.0", + "sebastian/environment": "^4.2.2", "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" + "theseer/tokenizer": "^1.1.3" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^8.2.2" }, "suggest": { - "ext-xdebug": "^2.6.0" + "ext-xdebug": "^2.7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.1-dev" + "dev-master": "7.0-dev" } }, "autoload": { @@ -767,7 +768,7 @@ "testing", "xunit" ], - "time": "2018-10-31T16:06:48+00:00" + "time": "2019-11-20T13:55:58+00:00" }, { "name": "phpunit/php-file-iterator", @@ -911,16 +912,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "3.0.2", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c" + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c", - "reference": "c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", "shasum": "" }, "require": { @@ -933,7 +934,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -956,57 +957,56 @@ "keywords": [ "tokenizer" ], - "time": "2019-07-08T05:24:54+00:00" + "time": "2019-09-17T06:23:10+00:00" }, { "name": "phpunit/phpunit", - "version": "7.5.14", + "version": "8.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2834789aeb9ac182ad69bfdf9ae91856a59945ff" + "reference": "7870c78da3c5e4883eaef36ae47853ebb3cb86f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2834789aeb9ac182ad69bfdf9ae91856a59945ff", - "reference": "2834789aeb9ac182ad69bfdf9ae91856a59945ff", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7870c78da3c5e4883eaef36ae47853ebb3cb86f2", + "reference": "7870c78da3c5e4883eaef36ae47853ebb3cb86f2", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.1", + "doctrine/instantiator": "^1.2.0", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.9.1", + "phar-io/manifest": "^1.0.3", + "phar-io/version": "^2.0.1", + "php": "^7.2", + "phpspec/prophecy": "^1.8.1", + "phpunit/php-code-coverage": "^7.0.7", + "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.0", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", + "phpunit/php-timer": "^2.1.2", + "sebastian/comparator": "^3.0.2", + "sebastian/diff": "^3.0.2", + "sebastian/environment": "^4.2.2", + "sebastian/exporter": "^3.1.1", + "sebastian/global-state": "^3.0.0", "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", + "sebastian/resource-operations": "^2.0.1", + "sebastian/type": "^1.1.3", "sebastian/version": "^2.0.1" }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" - }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-soap": "*", "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "phpunit/php-invoker": "^2.0.0" }, "bin": [ "phpunit" @@ -1014,7 +1014,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.5-dev" + "dev-master": "8.5-dev" } }, "autoload": { @@ -1040,7 +1040,7 @@ "testing", "xunit" ], - "time": "2019-07-15T06:24:08+00:00" + "time": "2019-12-25T14:49:39+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1209,16 +1209,16 @@ }, { "name": "sebastian/environment", - "version": "4.2.2", + "version": "4.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", "shasum": "" }, "require": { @@ -1258,20 +1258,20 @@ "environment", "hhvm" ], - "time": "2019-05-05T09:05:15+00:00" + "time": "2019-11-20T08:46:58+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", "shasum": "" }, "require": { @@ -1298,6 +1298,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1306,17 +1310,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -1325,27 +1325,30 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "time": "2019-09-14T09:02:43+00:00" }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.2", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "ext-dom": "*", + "phpunit/phpunit": "^8.0" }, "suggest": { "ext-uopz": "*" @@ -1353,7 +1356,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1376,7 +1379,7 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "time": "2019-02-01T05:30:01+00:00" }, { "name": "sebastian/object-enumerator", @@ -1565,6 +1568,52 @@ "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "time": "2018-10-04T04:07:39+00:00" }, + { + "name": "sebastian/type", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", + "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", + "shasum": "" + }, + "require": { + "php": "^7.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "time": "2019-07-02T08:10:15+00:00" + }, { "name": "sebastian/version", "version": "2.0.1", @@ -1610,16 +1659,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.11.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", "shasum": "" }, "require": { @@ -1631,7 +1680,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -1647,13 +1696,13 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -1664,7 +1713,7 @@ "polyfill", "portable" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "theseer/tokenizer", @@ -1708,32 +1757,29 @@ }, { "name": "webmozart/assert", - "version": "1.4.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", + "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", "symfony/polyfill-ctype": "^1.8" }, + "conflict": { + "vimeo/psalm": "<3.6.0" + }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -1755,7 +1801,7 @@ "check", "validate" ], - "time": "2018-12-25T11:19:39+00:00" + "time": "2019-11-24T13:36:37+00:00" } ], "aliases": [], @@ -1764,7 +1810,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.0" + "php": ">=7.2", + "ext-json": "*" }, "platform-dev": [] } diff --git a/src/FabianBeiner/Todoist/TodoistClient.php b/src/FabianBeiner/Todoist/TodoistClient.php index 460f193..5fb3c74 100644 --- a/src/FabianBeiner/Todoist/TodoistClient.php +++ b/src/FabianBeiner/Todoist/TodoistClient.php @@ -5,108 +5,76 @@ * * @author Fabian Beiner * @license https://opensource.org/licenses/MIT MIT - * - * @version 0.8.0 <2019-07-19> - * + * @version 1.0.0 <2020-01-02> * @see https://github.com/FabianBeiner/Todoist-PHP-API-Library */ namespace FabianBeiner\Todoist; use GuzzleHttp\Client as GuzzleClient; -use GuzzleHttp\Promise\PromiseInterface; -use GuzzleHttp\RequestOptions; -use function strlen; /** * Class TodoistClient. + * + * @package FabianBeiner\Todoist */ class TodoistClient extends GuzzleClient { /* * Use Traits. */ - use TodoistCommentsTrait, TodoistLabelsTrait, TodoistProjectsTrait, TodoistTasksTrait; + use TodoistCommentsTrait; + use TodoistHelpers; + use TodoistLabelsTrait; + use TodoistProjectsTrait; + use TodoistSectionsTrait; + use TodoistTasksTrait; /** - * @var string The URL of the Todoist REST API. + * @var string URL of the Todoist REST API. */ protected $restApiUrl = 'https://api.todoist.com/rest/v1/'; /** - * Todoist constructor. + * @var string 2-letter code that specifies the language for due_string parameters. + */ + protected $defaultInputLanguage = 'en'; + + /** + * @var array All valid languages. + */ + protected $validLanguages = ['en', 'da', 'pl', 'zh', 'ko', 'de', 'pt', 'ja', 'it', 'fr', 'sv', 'ru', 'es', 'nl']; + + /** + * TodoistClient constructor. * - * @param string $apiToken The API token to access the Todoist API. - * @param array $config Configuration to be passed to Guzzle client. + * @param string $apiToken API token to access the Todoist API. + * @param string $languageCode 2-letter code that specifies the language for due_string parameters. * * @throws \FabianBeiner\Todoist\TodoistException */ - public function __construct(string $apiToken, array $config = []) + public function __construct(string $apiToken, string $languageCode = 'en') { $apiToken = trim($apiToken); if (40 !== strlen($apiToken)) { throw new TodoistException('The provided API token is invalid.'); } + $languageCode = strtolower(trim($languageCode)); + if (in_array($languageCode, $this->validLanguages)) { + $this->defaultInputLanguage = $languageCode; + } + $defaults = [ 'headers' => [ - 'Accept-Encoding' => 'gzip' + 'Accept-Encoding' => 'gzip', + 'Authorization' => sprintf('Bearer %s', $apiToken), ], 'http_errors' => false, - 'timeout' => 5 + 'timeout' => 10, + 'base_uri' => $this->restApiUrl, ]; - $config = array_replace_recursive($defaults, $config); - $config['base_uri'] = $this->restApiUrl; - $config['headers']['Authorization'] = sprintf('Bearer %s', $apiToken); - - parent::__construct($config); - } - - /** - * Wrapper on Guzzle's requestAsync method. - * - * @param string $method - * @param string $uri - * @param array $options - * - * @throws \Exception - * - * @return PromiseInterface - */ - public function requestAsync($method, $uri = '', array $options = []): PromiseInterface - { - // Ensure the “X-Request-Id” header gets regenerated for every call. - $options['headers']['X-Request-Id'] = bin2hex(random_bytes(16)); - - return parent::requestAsync($method, $uri, $options); - } - - /** - * Prepare Guzzle request data. - * - * @param array $data - * - * @return array - */ - protected function prepareRequestData(array $data = []): array - { - array_walk_recursive($data, 'trim'); - - return [RequestOptions::JSON => $data]; - } - - /** - * Validates an ID to be a positive integer. - * - * @param mixed $id - * - * @return bool - */ - protected function validateId($id): bool - { - $filterOptions = ['options' => ['min_range' => 0]]; - - return (bool) filter_var($id, FILTER_VALIDATE_INT, $filterOptions); + parent::__construct($defaults); } } diff --git a/src/FabianBeiner/Todoist/TodoistCommentsTrait.php b/src/FabianBeiner/Todoist/TodoistCommentsTrait.php index 680284c..5bd50d3 100644 --- a/src/FabianBeiner/Todoist/TodoistCommentsTrait.php +++ b/src/FabianBeiner/Todoist/TodoistCommentsTrait.php @@ -5,7 +5,6 @@ * * @author Fabian Beiner * @license https://opensource.org/licenses/MIT MIT - * * @see https://github.com/FabianBeiner/Todoist-PHP-API-Library */ @@ -13,145 +12,117 @@ /** * Trait TodoistCommentsTrait. + * + * @package FabianBeiner\Todoist */ trait TodoistCommentsTrait { /** - * Alias for getAllComments('project', $projectId). + * Alias for getAllComments('task', $taskId). * - * @param int $projectId ID of the project. + * @param int $taskId The ID of the task. * - * @return array|bool Array with all comments (can be empty), or false on failure. + * @return array|bool An array containing all comments, or false on failure. */ - public function getAllCommentsByProject(int $projectId) + public function getAllCommentsByTask($taskId) { - return $this->getAllComments('project', $projectId); + return $this->getAllComments('task', $taskId); } /** - * Get all comments. + * Get all the comments. * - * @param string $type Can be "project" or "task". - * @param int $typeId ID of the project/task. + * @param string $commentType Type can be "project" or "task." + * @param int $typeId The ID of the project/task. * - * @return array|bool Array with all comments (can be empty), or false on failure. + * @return array|bool An array containing all comments, or false on failure. */ - public function getAllComments(string $type, int $typeId) + public function getAllComments(string $commentType, int $typeId) { - $type = strtolower($type); - - if (('project' !== $type && 'task' !== $type) || ! $this->validateId($typeId)) { + $commentType = strtolower($commentType); + if (('project' !== $commentType && 'task' !== $commentType) || ! $this->validateId($typeId)) { return false; } - $query = http_build_query([$type . '_id' => $typeId], null, '&', PHP_QUERY_RFC3986); + $query = http_build_query([$commentType . '_id' => $typeId], null, '&', PHP_QUERY_RFC3986); /** @var object $result Result of the GET request. */ $result = $this->get('comments?' . $query); - $status = $result->getStatusCode(); - if (204 === $status) { - return []; - } - if (200 === $status) { - return json_decode($result->getBody()->getContents()); - } - - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** - * Validates an ID to be a positive integer. - * - * @param mixed $id - * - * @return bool - */ - abstract protected function validateId($id): bool; - - /** - * Alias for getAllComments('task', $taskId). + * Alias for getAllComments('project', $projectId). * - * @param int $taskId ID of the task. + * @param int $projectId The ID of the project. * - * @return array|bool Array with all comments (can be empty), or false on failure. + * @return array|bool An array containing all comments, or false on failure. */ - public function getAllCommentsByTask($taskId) + public function getAllCommentsByProject($projectId) { - return $this->getAllComments('task', $taskId); + return $this->getAllComments('project', $projectId); } /** - * Alias for createComment('project', $projectId, $comment). + * Alias for createComment('task', $taskId, $comment). * - * @param int $projectId ID of the project. - * @param string $comment Comment to be added. + * @param int $taskId The ID of the task. + * @param string $comment The comment. * - * @return object|bool Object with values of the new comment, or false on failure. + * @return array|bool An array containing the values of the new comment, or false on failure. */ - public function createCommentForProject(int $projectId, string $comment) + public function createCommentForTask(int $taskId, string $comment) { - return $this->createComment('project', $projectId, $comment); + return $this->createComment('task', $taskId, $comment); } /** * Create a new comment. * - * @param string $type Can be "project" or "task". - * @param int $typeId ID of the project/task. - * @param string $comment Comment to be added. + * @param string $commentType Type can be "project" or "task." + * @param int $typeId The ID of the project/task. + * @param string $comment The comment. * - * @return object|bool Object with values of the new comment, or false on failure. + * @return array|bool An array containing the values of the new comment, or false on failure. */ - public function createComment(string $type, int $typeId, string $comment) + public function createComment(string $commentType, int $typeId, string $comment) { - $type = strtolower($type); - - if (('project' !== $type && 'task' !== $type) || '' === $comment || ! $this->validateId($typeId)) { + $commentType = strtolower($commentType); + if (('project' !== $commentType && 'task' !== $commentType) || ! $this->validateId($typeId)) { return false; } - $data = $this->prepareRequestData([ - $type . '_id' => $typeId, - 'content' => $comment, - ]); + $data = $this->preparePostData( + [ + $commentType . '_id' => $typeId, + 'content' => $comment, + ] + ); /** @var object $result Result of the POST request. */ $result = $this->post('comments', $data); - if (200 === $result->getStatusCode()) { - return json_decode($result->getBody()->getContents()); - } - - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** - * Prepare Guzzle request data. - * - * @param array $data - * - * @return array - */ - abstract protected function prepareRequestData(array $data = []): array; - - /** - * Alias for createComment('task', $projectId, $comment). + * Alias for createComment('project', $projectId, $comment). * - * @param int $taskId ID of the task. - * @param string $comment Comment to be added. + * @param int $projectId The ID of the project. + * @param string $comment The comment. * - * @return object|bool Object with values values of the new comment, or false on failure. + * @return array|bool An array containing the values of the new comment, or false on failure. */ - public function createCommentForTask(int $taskId, string $comment) + public function createCommentForProject(int $projectId, string $comment) { - return $this->createComment('task', $taskId, $comment); + return $this->createComment('project', $projectId, $comment); } /** * Get a comment. * - * @param int $commentId ID of the comment. + * @param int $commentId The ID of the comment. * - * @return object|bool Object with values of the comment, or false on failure. + * @return array|bool An array containing the values of the comment, or false on failure. */ public function getComment(int $commentId) { @@ -162,28 +133,25 @@ public function getComment(int $commentId) /** @var object $result Result of the GET request. */ $result = $this->get('comments/' . $commentId); - if (200 === $result->getStatusCode()) { - return json_decode($result->getBody()->getContents()); - } - - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** * Update a comment. * - * @param int $commentId ID of the comment. - * @param string $content New content of the comment. + * @param int $commentId The ID of the comment. + * @param string $content The new content of the comment. * * @return bool True on success, false on failure. */ public function updateComment(int $commentId, string $content): bool { - if ('' === $content || ! $this->validateId($commentId)) { + $content = filter_var($content, FILTER_SANITIZE_STRING); + if ( ! strlen($content) || ! $this->validateId($commentId)) { return false; } - $data = $this->prepareRequestData(['content' => $content]); + $data = $this->preparePostData(['content' => $content]); /** @var object $result Result of the POST request. */ $result = $this->post('comments/' . $commentId, $data); @@ -193,7 +161,7 @@ public function updateComment(int $commentId, string $content): bool /** * Delete a comment. * - * @param int $commentId ID of the comment. + * @param int $commentId The ID of the comment. * * @return bool True on success, false on failure. */ diff --git a/src/FabianBeiner/Todoist/TodoistException.php b/src/FabianBeiner/Todoist/TodoistException.php index 015c8f4..d6084ab 100644 --- a/src/FabianBeiner/Todoist/TodoistException.php +++ b/src/FabianBeiner/Todoist/TodoistException.php @@ -5,7 +5,6 @@ * * @author Fabian Beiner * @license https://opensource.org/licenses/MIT MIT - * * @see https://github.com/FabianBeiner/Todoist-PHP-API-Library */ @@ -15,6 +14,8 @@ /** * Class TodoistException. + * + * @package FabianBeiner\Todoist */ class TodoistException extends Exception { diff --git a/src/FabianBeiner/Todoist/TodoistHelpers.php b/src/FabianBeiner/Todoist/TodoistHelpers.php new file mode 100644 index 0000000..bbd5e4f --- /dev/null +++ b/src/FabianBeiner/Todoist/TodoistHelpers.php @@ -0,0 +1,81 @@ + + * @license https://opensource.org/licenses/MIT MIT + * @see https://github.com/FabianBeiner/Todoist-PHP-API-Library + */ + +namespace FabianBeiner\Todoist; + +use GuzzleHttp\RequestOptions; + +/** + * Trait TodoistHelpers. + * + * @package FabianBeiner\Todoist + */ +trait TodoistHelpers +{ + /** + * Handles the response of a call. + * + * @param int $statusCode The HTTP status code. + * @param string $content The content of the call. + * + * @throws TodoistException Exception. + * @return array|bool An array with or without data, false on failure. + */ + final protected function handleResponse(int $statusCode, string $content) + { + switch ($statusCode) { + case 200: + return json_decode($content, true); + case 204: + return []; + case 401: + case 403: + throw new TodoistException('Unable to access the API. Is the API token valid?'); + case 402: + throw new TodoistException('A non-premium user used a premium-only feature.'); + default: + return false; + } + } + + /** + * Prepare POST data for Guzzle. + * + * @param array $data The POST data as an array. + * + * @throws \Exception Exception. + * @return array Trimmed array with JSON data. + */ + final protected function preparePostData(array $data = []): array + { + array_walk_recursive($data, 'trim'); + + return array_merge( + ['headers' => [ + 'X-Request-Id' => bin2hex(random_bytes(16)), + ]], + [RequestOptions::JSON => $data] + ); + } + + /** + * Validates an ID to be a positive integer. + * + * @param int $validateId The ID to be validated. + * + * @return bool True on success, false on failure. + */ + final protected function validateId(int $validateId): bool + { + return (bool)filter_var($validateId, FILTER_VALIDATE_INT, [ + 'options' => ['min_range' => 1], + ]); + } +} diff --git a/src/FabianBeiner/Todoist/TodoistLabelsTrait.php b/src/FabianBeiner/Todoist/TodoistLabelsTrait.php index 2013bea..902c369 100644 --- a/src/FabianBeiner/Todoist/TodoistLabelsTrait.php +++ b/src/FabianBeiner/Todoist/TodoistLabelsTrait.php @@ -5,7 +5,6 @@ * * @author Fabian Beiner * @license https://opensource.org/licenses/MIT MIT - * * @see https://github.com/FabianBeiner/Todoist-PHP-API-Library */ @@ -13,69 +12,53 @@ /** * Trait TodoistLabelsTrait. + * + * @package FabianBeiner\Todoist */ trait TodoistLabelsTrait { /** - * Get all labels. + * Get all the labels. * - * @return array|bool Array with all labels (can be empty), or false on failure. + * @return array|bool An array containing all user labels, or false on failure. */ public function getAllLabels() { /** @var object $result Result of the GET request. */ $result = $this->get('labels'); - $status = $result->getStatusCode(); - if (204 === $status) { - return []; - } - if (200 === $status) { - return json_decode($result->getBody()->getContents()); - } - - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** * Create a new label. * - * @param string $name Name of the label. + * @param string $labelName The name of the label. + * @param array $optionalParameters Optional parameters, see + * https://developer.todoist.com/rest/v1/#create-a-new-label. * - * @return object|bool Object with values of the new label, or false on failure. + * @return array|bool An array containing the values of the new label, or false on failure. */ - public function createLabel(string $name) + public function createLabel(string $labelName, array $optionalParameters = []) { - if ('' === $name) { + $labelName = filter_var($labelName, FILTER_SANITIZE_STRING); + if ( ! strlen($labelName)) { return false; } - $data = $this->prepareRequestData(['name' => $name]); + $postData = $this->preparePostData(array_merge(['name' => $labelName], $optionalParameters)); /** @var object $result Result of the POST request. */ - $result = $this->post('labels', $data); - - if (200 === $result->getStatusCode()) { - return json_decode($result->getBody()->getContents()); - } + $result = $this->post('labels', $postData); - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } - /** - * Prepare Guzzle request data. - * - * @param array $data - * - * @return array - */ - abstract protected function prepareRequestData(array $data = []): array; - /** * Get a label. * - * @param int $labelId ID of the label. + * @param int $labelId The ID of the label. * - * @return object|bool Object with values of the label, or false on failure. + * @return array|bool An array containing the label data related to the given id, or false on failure. */ public function getLabel(int $labelId) { @@ -86,52 +69,27 @@ public function getLabel(int $labelId) /** @var object $result Result of the GET request. */ $result = $this->get('labels/' . $labelId); - if (200 === $result->getStatusCode()) { - return json_decode($result->getBody()->getContents()); - } - - return false; - } - - /** - * Validates an ID to be a positive integer. - * - * @param mixed $id - * - * @return bool - */ - abstract protected function validateId($id): bool; - - /** - * Alias for updateLabel(). - * - * @param int $labelId ID of the label. - * @param string $name New name of the label. - * - * @return bool True on success, false on failure. - */ - public function renameLabel(int $labelId, string $name): bool - { - return $this->updateLabel($labelId, $name); + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** - * Update (actually rename…) a label. + * Update a label. * - * @param int $labelId ID of the label. - * @param string $name New name of the label. + * @param int $labelId The ID of the label. + * @param string $newLabelName The new name of the label. * * @return bool True on success, false on failure. */ - public function updateLabel(int $labelId, string $name): bool + public function updateLabel(int $labelId, string $newLabelName): bool { - if ('' === $name || ! $this->validateId($labelId)) { + $newLabelName = filter_var($newLabelName, FILTER_SANITIZE_STRING); + if ( ! strlen($newLabelName) || ! $this->validateId($labelId)) { return false; } - $data = $this->prepareRequestData(['name' => $name]); + $postData = $this->preparePostData(['name' => $newLabelName]); /** @var object $result Result of the POST request. */ - $result = $this->post('labels/' . $labelId, $data); + $result = $this->post('labels/' . $labelId, $postData); return 204 === $result->getStatusCode(); } @@ -139,7 +97,7 @@ public function updateLabel(int $labelId, string $name): bool /** * Delete a label. * - * @param int $labelId ID of the label. + * @param int $labelId The ID of the label. * * @return bool True on success, false on failure. */ diff --git a/src/FabianBeiner/Todoist/TodoistProjectsTrait.php b/src/FabianBeiner/Todoist/TodoistProjectsTrait.php index 0d4759d..c0a0d13 100644 --- a/src/FabianBeiner/Todoist/TodoistProjectsTrait.php +++ b/src/FabianBeiner/Todoist/TodoistProjectsTrait.php @@ -5,7 +5,6 @@ * * @author Fabian Beiner * @license https://opensource.org/licenses/MIT MIT - * * @see https://github.com/FabianBeiner/Todoist-PHP-API-Library */ @@ -13,69 +12,53 @@ /** * Trait TodoistProjectsTrait. + * + * @package FabianBeiner\Todoist */ trait TodoistProjectsTrait { /** - * Get all projects. + * Get all the projects. * - * @return array|bool Array with all projects (can be empty), or false on failure. + * @return array|bool An array containing all active user projects, or false on failure. */ public function getAllProjects() { /** @var object $result Result of the GET request. */ $result = $this->get('projects'); - $status = $result->getStatusCode(); - if (204 === $status) { - return []; - } - if (200 === $status) { - return json_decode($result->getBody()->getContents()); - } - - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** * Create a new project. * - * @param string $name Name of the project. + * @param string $projectName The name of the new project. + * @param array $optionalParameters Optional parameters, see + * https://developer.todoist.com/rest/v1/#create-a-new-project. * - * @return object|bool Object with values of the new project, or false on failure. + * @return array|bool An array containing the values of the new project, or false on failure. */ - public function createProject(string $name) + public function createProject(string $projectName, array $optionalParameters = []) { - if ('' === $name) { + $projectName = filter_var($projectName, FILTER_SANITIZE_STRING); + if ( ! strlen($projectName)) { return false; } - $data = $this->prepareRequestData(['name' => $name]); + $postData = $this->preparePostData(array_merge(['name' => $projectName], $optionalParameters)); /** @var object $result Result of the POST request. */ - $result = $this->post('projects', $data); - - if (200 === $result->getStatusCode()) { - return json_decode($result->getBody()->getContents()); - } + $result = $this->post('projects', $postData); - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } - /** - * Prepare Guzzle request data. - * - * @param array $data - * - * @return array - */ - abstract protected function prepareRequestData(array $data = []): array; - /** * Get a project. * - * @param int $projectId ID of the project. + * @param int $projectId The ID of the project. * - * @return object|bool Object with values of the project, or false on failure. + * @return array|bool An array containing the project data related to the given id, or false on failure. */ public function getProject(int $projectId) { @@ -86,52 +69,27 @@ public function getProject(int $projectId) /** @var object $result Result of the GET request. */ $result = $this->get('projects/' . $projectId); - if (200 === $result->getStatusCode()) { - return json_decode($result->getBody()->getContents()); - } - - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** - * Validates an ID to be a positive integer. + * Update a project. * - * @param mixed $id - * - * @return bool - */ - abstract protected function validateId($id): bool; - - /** - * Alias for updateProject(). - * - * @param int $projectId ID of the project. - * @param string $name New name of the project. - * - * @return bool True on success, false on failure. - */ - public function renameProject(int $projectId, string $name): bool - { - return $this->updateProject($projectId, $name); - } - - /** - * Update (actually rename...) a project. - * - * @param int $projectId ID of the project. - * @param string $name New name of the project. + * @param int $projectId The ID of the project. + * @param string $newProjectName The new name of the project. * * @return bool True on success, false on failure. */ - public function updateProject(int $projectId, string $name): bool + public function updateProject(int $projectId, string $newProjectName): bool { - if ('' === $name || ! $this->validateId($projectId)) { + $newProjectName = filter_var($newProjectName, FILTER_SANITIZE_STRING); + if ( ! strlen($newProjectName) || ! $this->validateId($projectId)) { return false; } - $data = $this->prepareRequestData(['name' => $name]); + $postData = $this->preparePostData(['name' => $newProjectName]); /** @var object $result Result of the POST request. */ - $result = $this->post('projects/' . $projectId, $data); + $result = $this->post('projects/' . $projectId, $postData); return 204 === $result->getStatusCode(); } @@ -139,13 +97,13 @@ public function updateProject(int $projectId, string $name): bool /** * Delete a project. * - * @param int $projectId ID of the project. + * @param int $projectId The ID of the project. * * @return bool True on success, false on failure. */ public function deleteProject(int $projectId): bool { - if ($projectId <= 0 || ! $projectId || ! filter_var($projectId, FILTER_VALIDATE_INT)) { + if ( ! $this->validateId($projectId)) { return false; } diff --git a/src/FabianBeiner/Todoist/TodoistSectionsTrait.php b/src/FabianBeiner/Todoist/TodoistSectionsTrait.php new file mode 100644 index 0000000..b5160c3 --- /dev/null +++ b/src/FabianBeiner/Todoist/TodoistSectionsTrait.php @@ -0,0 +1,118 @@ + + * @license https://opensource.org/licenses/MIT MIT + * @see https://github.com/FabianBeiner/Todoist-PHP-API-Library + */ + +namespace FabianBeiner\Todoist; + +/** + * Trait TodoistSectionsTrait. + * + * @package FabianBeiner\Todoist + */ +trait TodoistSectionsTrait +{ + /** + * Get all the sections. + * + * @return array|bool An array containing all user sections, or false on failure. + */ + public function getAllSections() + { + /** @var object $result Result of the GET request. */ + $result = $this->get('sections'); + + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); + } + + /** + * Create a new section. + * + * @param string $sectionName The name of the section. + * @param int $projectId The project ID this section should belong to. + * @param array $optionalParameters Optional parameters, see + * https://developer.todoist.com/rest/v1/#create-a-new-section. + * + * @return array|bool An array containing the values of the new section, or false on failure. + */ + public function createSection(string $sectionName, int $projectId, array $optionalParameters = []) + { + $sectionName = filter_var($sectionName, FILTER_SANITIZE_STRING); + if ( ! strlen($sectionName) || ! $this->validateId($projectId)) { + return false; + } + + $postData = $this->preparePostData( + array_merge(['name' => $sectionName, 'project_id' => $projectId], $optionalParameters) + ); + /** @var object $result Result of the POST request. */ + $result = $this->post('sections', $postData); + + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); + } + + /** + * Get a single section. + * + * @param int $sectionId The ID of the section. + * + * @return array|bool An array containing the section data related to the given id, or false on failure. + */ + public function getSection(int $sectionId) + { + if ( ! $this->validateId($sectionId)) { + return false; + } + + /** @var object $result Result of the GET request. */ + $result = $this->get('sections/' . $sectionId); + + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); + } + + /** + * Update a section. + * + * @param int $sectionId The ID of the section. + * @param string $newSectionName The new name of the section. + * + * @return bool True on success, false on failure. + */ + public function updateSection(int $sectionId, string $newSectionName): bool + { + $newSectionName = filter_var($newSectionName, FILTER_SANITIZE_STRING); + if ( ! strlen($newSectionName) || ! $this->validateId($sectionId)) { + return false; + } + + $postData = $this->preparePostData(['name' => $newSectionName]); + /** @var object $result Result of the POST request. */ + $result = $this->post('sections/' . $sectionId, $postData); + + return 204 === $result->getStatusCode(); + } + + /** + * Delete a section. + * + * @param int $sectionId The ID of the section. + * + * @return bool True on success, false on failure. + */ + public function deleteSection(int $sectionId): bool + { + if ( ! $this->validateId($sectionId)) { + return false; + } + + /** @var object $result Result of the DELETE request. */ + $result = $this->delete('sections/' . $sectionId); + + return 204 === $result->getStatusCode(); + } +} diff --git a/src/FabianBeiner/Todoist/TodoistTasksTrait.php b/src/FabianBeiner/Todoist/TodoistTasksTrait.php index c938945..81b283a 100644 --- a/src/FabianBeiner/Todoist/TodoistTasksTrait.php +++ b/src/FabianBeiner/Todoist/TodoistTasksTrait.php @@ -5,7 +5,6 @@ * * @author Fabian Beiner * @license https://opensource.org/licenses/MIT MIT - * * @see https://github.com/FabianBeiner/Todoist-PHP-API-Library */ @@ -13,61 +12,62 @@ /** * Trait TodoistTasksTrait. + * + * @package FabianBeiner\Todoist */ trait TodoistTasksTrait { /** - * Get all tasks. + * Get active tasks. + * + * @param array $options Possibility to add non-required parameters, see + * https://developer.todoist.com/rest/v1/#get-active-tasks. * - * @return array|bool Array with all tasks (can be empty), or false on failure. + * @return array|bool Returns an array containing all user active tasks, or false on failure. */ - public function getAllTasks() + public function getAllTasks(array $options = []) { - $result = $this->get('tasks'); - - $status = $result->getStatusCode(); - if (204 === $status) { - return []; - } - if (200 === $status) { - return json_decode($result->getBody()->getContents()); + $path = 'tasks'; + if (count($options)) { + $query = http_build_query($options, null, '&', PHP_QUERY_RFC3986); + $path = $path . $query; } + /** @var object $result Result of the GET request. */ + $result = $this->get($path); - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** * Create a new task. * - * @param string $content Content of the task. - * @param array $options Possibility to add non-required parameters. + * @param string $content The content of the task. + * @param array $options Possibility to add non-required parameters, see + * https://developer.todoist.com/rest/v1/#create-a-new-task. * - * @return object|bool Object with values of the new task, or false on failure. + * @return array|bool An array containing the values of the new task, or false on failure. */ public function createTask(string $content, array $options = []) { - if ('' === $content) { + $content = filter_var($content, FILTER_SANITIZE_STRING); + if ( ! strlen($content)) { return false; } unset($options['content']); - $data = $this->prepareRequestData(array_merge(['content' => $content], $options)); + $data = $this->preparePostData(array_merge(['content' => $content], $options)); + /** @var object $result Result of the POST request. */ $result = $this->post('tasks', $data); - $status = $result->getStatusCode(); - if (200 === $status) { - return json_decode($result->getBody()->getContents()); - } - - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** * Get a task. * - * @param int $taskId ID of the task. + * @param int $taskId The ID of the task. * - * @return array|bool Array with values of the task, or false on failure. + * @return array|bool An array containing the task data related to the given id, or false on failure. */ public function getTask(int $taskId) { @@ -75,33 +75,31 @@ public function getTask(int $taskId) return false; } + /** @var object $result Result of the GET request. */ $result = $this->get('tasks/' . $taskId); - $status = $result->getStatusCode(); - if (200 === $status) { - return json_decode($result->getBody()->getContents()); - } - - return false; + return $this->handleResponse($result->getStatusCode(), $result->getBody()->getContents()); } /** * Update a task. * - * @param int $taskId ID of the task. - * @param string $content Content of the task. - * @param array $options Possibility to add non-required parameters. + * @param int $taskId The ID of the task. + * @param string|null $content The content of the task or null. + * @param array $options Possibility to add non-required parameters, see + * https://developer.todoist.com/rest/v1/#update-a-task. * - * @return bool + * @return bool True on success, false on failure. */ - public function updateTask(int $taskId, string $content, array $options = []): bool + public function updateTask(int $taskId, string $content = null, array $options = []): bool { - if ('' === $content) { + $content = filter_var($content, FILTER_SANITIZE_STRING); + if ( ! $this->validateId($taskId)) { return false; } - unset($options['content']); - $data = $this->prepareRequestData(array_merge(['content' => $content], $options)); + $data = $this->preparePostData(array_merge($options, ['content' => $content])); + /** @var object $result Result of the POST request. */ $result = $this->post('tasks/' . $taskId, $data); return 204 === $result->getStatusCode(); @@ -110,7 +108,7 @@ public function updateTask(int $taskId, string $content, array $options = []): b /** * Close a task. * - * @param int $taskId ID of the task. + * @param int $taskId The ID of the task. * * @return bool True on success, false on failure. */ @@ -120,6 +118,7 @@ public function closeTask(int $taskId): bool return false; } + /** @var object $result Result of the POST request. */ $result = $this->post('tasks/' . $taskId . '/close'); return 204 === $result->getStatusCode(); @@ -128,7 +127,7 @@ public function closeTask(int $taskId): bool /** * Reopen a task. * - * @param int $taskId ID of the task. + * @param int $taskId The ID of the task. * * @return bool True on success, false on failure. */ @@ -138,7 +137,8 @@ public function reopenTask(int $taskId): bool return false; } - $result = $this->post('tasks/' . $taskId . '/repoen'); + /** @var object $result Result of the POST request. */ + $result = $this->post('tasks/' . $taskId . '/reopen'); return 204 === $result->getStatusCode(); } @@ -146,7 +146,7 @@ public function reopenTask(int $taskId): bool /** * Delete a task. * - * @param int $taskId ID of the task. + * @param int $taskId The ID of the task. * * @return bool True on success, false on failure. */ @@ -156,26 +156,9 @@ public function deleteTask(int $taskId): bool return false; } + /** @var object $result Result of the DELETE request. */ $result = $this->delete('tasks/' . $taskId); return 204 === $result->getStatusCode(); } - - /** - * Prepare Guzzle request data. - * - * @param array $data - * - * @return array - */ - abstract protected function prepareRequestData(array $data = []): array; - - /** - * Validates an ID to be a positive integer. - * - * @param mixed $id - * - * @return bool - */ - abstract protected function validateId($id): bool; } diff --git a/tests/FabianBeiner/Todoist/TodoistClientTest.php b/tests/FabianBeiner/Todoist/TodoistClientTest.php index a2f2e71..eb7dbbe 100644 --- a/tests/FabianBeiner/Todoist/TodoistClientTest.php +++ b/tests/FabianBeiner/Todoist/TodoistClientTest.php @@ -1,41 +1,55 @@ apiToken = getenv('TODOIST_TOKEN'); - $this->projectName = uniqid(); + $this->apiToken = getenv('TODOIST_TOKEN'); + $this->testName = 'PHPUnit-' . uniqid(); + $this->Todoist = new TodoistClient($this->apiToken); parent::setUp(); } /** * Test the configuration. - * - * @param array $options - * - * @throws \FabianBeiner\Todoist\TodoistException */ - public function testConfiguration(array $options = []) + public function testConfiguration() { - $Todoist = new TodoistClient($this->apiToken, $options); - $config = $Todoist->getConfig(); + $config = $this->Todoist->getConfig(); $baseUri = $config['base_uri']; $headers = $config['headers']; @@ -47,70 +61,160 @@ public function testConfiguration(array $options = []) $this->assertEquals(sprintf('Bearer %s', $this->apiToken), $headers['Authorization']); } - /** - * @throws \FabianBeiner\Todoist\TodoistException - */ public function testGetAllProjects() { - $Todoist = new TodoistClient($this->apiToken); - $allProjects = $Todoist->getAllProjects(); - $this->assertObjectHasAttribute('id', $allProjects[0]); + $allProjects = $this->Todoist->getAllProjects(); + $this->assertArrayHasKey('id', $allProjects[0]); } /** - * @throws \FabianBeiner\Todoist\TodoistException - * * @return int ID of the created project. */ public function testCreateProject() { - $Todoist = new TodoistClient($this->apiToken); - $createProject = $Todoist->createProject($this->projectName); - $this->assertObjectHasAttribute('name', $createProject); - $this->assertEquals($this->projectName, $createProject->name); + $createProject = $this->Todoist->createProject($this->testName); + $this->assertArrayHasKey('name', $createProject); + $this->assertEquals($this->testName, $createProject['name']); - return $createProject->id; + return $createProject['id']; } /** * @depends testCreateProject * - * @param $id + * @param $projectId + */ + public function testGetProject($projectId) + { + $project = $this->Todoist->getProject($projectId); + $this->assertArrayHasKey('name', $project); + } + + /** + * @depends testCreateProject * - * @throws \FabianBeiner\Todoist\TodoistException + * @param $projectId */ - public function testGetProject($id) + public function testUpdateProject($projectId) { - $Todoist = new TodoistClient($this->apiToken); - $project = $Todoist->getProject($id); - $this->assertObjectHasAttribute('name', $project); + $success = $this->Todoist->updateProject($projectId, $this->testName . '-Renamed'); + $this->assertTrue($success); } /** * @depends testCreateProject * - * @param $id + * @param $projectId + */ + public function testDeleteProject($projectId) + { + $success = $this->Todoist->deleteProject($projectId); + $this->assertTrue($success); + } + + public function testGetAllLabels() + { + $allLabels = $this->Todoist->getAllLabels(); + $this->assertArrayHasKey('id', $allLabels[0]); + } + + /** + * @return int ID of the created label. + */ + public function testCreateLabel() + { + $createLabel = $this->Todoist->createLabel($this->testName); + $this->assertArrayHasKey('name', $createLabel); + $this->assertEquals($this->testName, $createLabel['name']); + + return $createLabel['id']; + } + + /** + * @depends testCreateLabel * - * @throws \FabianBeiner\Todoist\TodoistException + * @param $labelId + */ + public function testGetLabel($labelId) + { + $label = $this->Todoist->getLabel($labelId); + $this->assertArrayHasKey('name', $label); + } + + /** + * @depends testCreateLabel + * + * @param $labelId */ - public function testUpdateProject($id) + public function testUpdateLabel($labelId) { - $Todoist = new TodoistClient($this->apiToken); - $success = $Todoist->updateProject($id, $this->projectName . '-Renamed'); + $success = $this->Todoist->updateLabel($labelId, $this->testName . '-Renamed'); $this->assertTrue($success); } /** - * @depends testCreateProject + * @depends testCreateLabel * - * @param $id + * @param $labelId + */ + public function testDeleteLabel($labelId) + { + $success = $this->Todoist->deleteLabel($labelId); + $this->assertTrue($success); + } + + /** + * @depends testCreateSection + */ + public function testGetAllSections() + { + $allSections = $this->Todoist->getAllSections(); + $this->assertArrayHasKey('id', $allSections[0]); + } + + /** + * @return int ID of the created section. + */ + public function testCreateSection() + { + $allProjects = $this->Todoist->getAllProjects(); + $createSection = $this->Todoist->createSection($this->testName, $allProjects[0]['id']); + $this->assertArrayHasKey('name', $createSection); + $this->assertEquals($this->testName, $createSection['name']); + + return $createSection['id']; + } + + /** + * @depends testCreateSection * - * @throws \FabianBeiner\Todoist\TodoistException + * @param $sectionId + */ + public function testGetSection($sectionId) + { + $section = $this->Todoist->getSection($sectionId); + $this->assertArrayHasKey('name', $section); + } + + /** + * @depends testCreateSection + * + * @param $sectionId + */ + public function testUpdateSection($sectionId) + { + $success = $this->Todoist->updateSection($sectionId, $this->testName . '-Renamed'); + $this->assertTrue($success); + } + + /** + * @depends testCreateSection + * + * @param $sectionId */ - public function testDeleteProject($id) + public function testDeleteSection($sectionId) { - $Todoist = new TodoistClient($this->apiToken); - $success = $Todoist->deleteProject($id); + $success = $this->Todoist->deleteSection($sectionId); $this->assertTrue($success); } } diff --git a/tests/FabianBeiner/Todoist/TodoistExceptionTest.php b/tests/FabianBeiner/Todoist/TodoistExceptionTest.php index 6badad7..8892a30 100644 --- a/tests/FabianBeiner/Todoist/TodoistExceptionTest.php +++ b/tests/FabianBeiner/Todoist/TodoistExceptionTest.php @@ -1,5 +1,7 @@ expectException('\FabianBeiner\Todoist\TodoistException'); + $this->expectExceptionMessage('Some message.'); + throw new TodoistException('Some message.'); } }