Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/3rdparty.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>2021-08-19 10:25:13 +0300
committerCarl Schwan <carl@carlschwan.eu>2021-08-25 16:40:07 +0300
commit56dadbd34ec5a2cc44fb9c1b8f10e58620827426 (patch)
tree8d69e1db03f0585df35d0363b851add8800e93ca /egulias
parent3359f61c34b5d64ae25eeab6609b3a45ad426c2b (diff)
Bump egulias/email-validator from 2.1.25 to 3.1.1
Bumps [egulias/email-validator](https://github.com/egulias/EmailValidator) from 2.1.25 to 3.1.1. - [Release notes](https://github.com/egulias/EmailValidator/releases) - [Changelog](https://github.com/egulias/EmailValidator/blob/3.x/CHANGELOG.md) - [Commits](https://github.com/egulias/EmailValidator/compare/2.1.25...3.1.1) --- updated-dependencies: - dependency-name: egulias/email-validator dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Carl Schwan <carl@carlschwan.eu>
Diffstat (limited to 'egulias')
-rw-r--r--egulias/email-validator/CONTRIBUTING.md153
-rw-r--r--egulias/email-validator/LICENSE2
-rw-r--r--egulias/email-validator/composer.json14
-rw-r--r--egulias/email-validator/composer.lock4440
-rw-r--r--egulias/email-validator/src/EmailLexer.php168
-rw-r--r--egulias/email-validator/src/EmailParser.php130
-rw-r--r--egulias/email-validator/src/EmailValidator.php10
-rw-r--r--egulias/email-validator/src/Exception/AtextAfterCFWS.php9
-rw-r--r--egulias/email-validator/src/Exception/CRLFAtTheEnd.php9
-rw-r--r--egulias/email-validator/src/Exception/CRLFX2.php9
-rw-r--r--egulias/email-validator/src/Exception/CRNoLF.php9
-rw-r--r--egulias/email-validator/src/Exception/CharNotAllowed.php9
-rw-r--r--egulias/email-validator/src/Exception/CommaInDomain.php9
-rw-r--r--egulias/email-validator/src/Exception/ConsecutiveAt.php9
-rw-r--r--egulias/email-validator/src/Exception/ConsecutiveDot.php9
-rw-r--r--egulias/email-validator/src/Exception/DomainAcceptsNoMail.php9
-rw-r--r--egulias/email-validator/src/Exception/DomainHyphened.php9
-rw-r--r--egulias/email-validator/src/Exception/DotAtEnd.php9
-rw-r--r--egulias/email-validator/src/Exception/DotAtStart.php9
-rw-r--r--egulias/email-validator/src/Exception/ExpectingAT.php9
-rw-r--r--egulias/email-validator/src/Exception/ExpectingATEXT.php9
-rw-r--r--egulias/email-validator/src/Exception/ExpectingCTEXT.php9
-rw-r--r--egulias/email-validator/src/Exception/ExpectingDTEXT.php9
-rw-r--r--egulias/email-validator/src/Exception/ExpectingDomainLiteralClose.php9
-rw-r--r--egulias/email-validator/src/Exception/ExpectingQPair.php9
-rw-r--r--egulias/email-validator/src/Exception/InvalidEmail.php14
-rw-r--r--egulias/email-validator/src/Exception/LocalOrReservedDomain.php9
-rw-r--r--egulias/email-validator/src/Exception/NoDNSRecord.php9
-rw-r--r--egulias/email-validator/src/Exception/NoDomainPart.php9
-rw-r--r--egulias/email-validator/src/Exception/NoLocalPart.php9
-rw-r--r--egulias/email-validator/src/Exception/UnclosedComment.php9
-rw-r--r--egulias/email-validator/src/Exception/UnclosedQuotedString.php9
-rw-r--r--egulias/email-validator/src/Exception/UnopenedComment.php9
-rw-r--r--egulias/email-validator/src/MessageIDParser.php93
-rw-r--r--egulias/email-validator/src/Parser.php78
-rw-r--r--egulias/email-validator/src/Parser/Comment.php103
-rw-r--r--egulias/email-validator/src/Parser/CommentStrategy/CommentStrategy.php18
-rw-r--r--egulias/email-validator/src/Parser/CommentStrategy/DomainComment.php37
-rw-r--r--egulias/email-validator/src/Parser/CommentStrategy/LocalComment.php37
-rw-r--r--egulias/email-validator/src/Parser/DomainLiteral.php212
-rw-r--r--egulias/email-validator/src/Parser/DomainPart.php503
-rw-r--r--egulias/email-validator/src/Parser/DoubleQuote.php87
-rw-r--r--egulias/email-validator/src/Parser/FoldingWhiteSpace.php82
-rw-r--r--egulias/email-validator/src/Parser/IDLeftPart.php16
-rw-r--r--egulias/email-validator/src/Parser/IDRightPart.php29
-rw-r--r--egulias/email-validator/src/Parser/LocalPart.php209
-rw-r--r--egulias/email-validator/src/Parser/Parser.php249
-rw-r--r--egulias/email-validator/src/Parser/PartParser.php63
-rw-r--r--egulias/email-validator/src/Result/InvalidEmail.php46
-rw-r--r--egulias/email-validator/src/Result/MultipleErrors.php54
-rw-r--r--egulias/email-validator/src/Result/Reason/AtextAfterCFWS.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/CRLFAtTheEnd.php19
-rw-r--r--egulias/email-validator/src/Result/Reason/CRLFX2.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/CRNoLF.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/CharNotAllowed.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/CommaInDomain.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/CommentsInIDRight.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/ConsecutiveAt.php17
-rw-r--r--egulias/email-validator/src/Result/Reason/ConsecutiveDot.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/DetailedReason.php13
-rw-r--r--egulias/email-validator/src/Result/Reason/DomainAcceptsNoMail.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/DomainHyphened.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/DomainTooLong.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/DotAtEnd.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/DotAtStart.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/ExceptionFound.php26
-rw-r--r--egulias/email-validator/src/Result/Reason/ExpectingATEXT.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/ExpectingCTEXT.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/ExpectingDTEXT.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/ExpectingDomainLiteralClose.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/LabelTooLong.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/LocalOrReservedDomain.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/NoDNSRecord.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/NoDomainPart.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/NoLocalPart.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/RFCWarnings.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/Reason.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/SpoofEmail.php17
-rw-r--r--egulias/email-validator/src/Result/Reason/UnOpenedComment.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/UnableToGetDNSRecord.php19
-rw-r--r--egulias/email-validator/src/Result/Reason/UnclosedComment.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/UnclosedQuotedString.php16
-rw-r--r--egulias/email-validator/src/Result/Reason/UnusualElements.php26
-rw-r--r--egulias/email-validator/src/Result/Result.php27
-rw-r--r--egulias/email-validator/src/Result/SpoofEmail.php14
-rw-r--r--egulias/email-validator/src/Result/ValidEmail.php27
-rw-r--r--egulias/email-validator/src/Validation/DNSCheckValidation.php64
-rw-r--r--egulias/email-validator/src/Validation/EmailValidation.php8
-rw-r--r--egulias/email-validator/src/Validation/Error/RFCWarnings.php11
-rw-r--r--egulias/email-validator/src/Validation/Error/SpoofEmail.php11
-rw-r--r--egulias/email-validator/src/Validation/Extra/SpoofCheckValidation.php (renamed from egulias/email-validator/src/Validation/SpoofCheckValidation.php)17
-rw-r--r--egulias/email-validator/src/Validation/MessageIDValidation.php51
-rw-r--r--egulias/email-validator/src/Validation/MultipleErrors.php32
-rw-r--r--egulias/email-validator/src/Validation/MultipleValidationWithAnd.php51
-rw-r--r--egulias/email-validator/src/Validation/NoRFCWarningsValidation.php10
-rw-r--r--egulias/email-validator/src/Validation/RFCValidation.php24
-rw-r--r--egulias/email-validator/src/Warning/DomainTooLong.php14
-rw-r--r--egulias/email-validator/src/Warning/LabelTooLong.php14
-rw-r--r--egulias/email-validator/src/Warning/Warning.php2
99 files changed, 6795 insertions, 1207 deletions
diff --git a/egulias/email-validator/CONTRIBUTING.md b/egulias/email-validator/CONTRIBUTING.md
new file mode 100644
index 00000000..7b79e108
--- /dev/null
+++ b/egulias/email-validator/CONTRIBUTING.md
@@ -0,0 +1,153 @@
+# Contributing
+
+When contributing to this repository make sure to follow the Pull request process below.
+Reduce to the minimum 3rd party dependencies.
+
+Please note we have a [code of conduct](#Code of Conduct), please follow it in all your interactions with the project.
+
+## Pull Request Process
+
+When doing a PR to v2 remember that you also have to do the PR port to v3, or tests confirming the bug is not reproducible.
+
+1. Supported version is v3. If you are fixing a bug in v2, please port to v3
+2. Use the title as a brief description of the changes
+3. Describe the changes you are proposing
+ 1. If adding an extra validation state the benefits of adding it and the problem is solving
+ 2. Document in the readme, by adding it to the list
+4. Provide appropiate tests for the code you are submitting: aim to keep the existing coverage percentage.
+5. Add your Twitter handle (if you have) so we can thank you there.
+
+## License
+By contributing, you agree that your contributions will be licensed under its MIT License.
+
+## Code of Conduct
+
+### Our Pledge
+
+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 a positive environment for our
+community include:
+
+* 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 include:
+
+* 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 email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+### Enforcement Responsibilities
+
+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.
+
+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 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 to the community leaders responsible for enforcement at <emailvalidatorrfc.ccreport@gmail.com>.
+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
+
+**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 community.
+
+### Attribution
+
+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][v2.0].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
+at [https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/egulias/email-validator/LICENSE b/egulias/email-validator/LICENSE
index c34d2c19..1f0f2678 100644
--- a/egulias/email-validator/LICENSE
+++ b/egulias/email-validator/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2016 Eduardo Gulias Davis
+Copyright (c) 2013-2021 Eduardo Gulias Davis
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/egulias/email-validator/composer.json b/egulias/email-validator/composer.json
index a275696a..d598d1b6 100644
--- a/egulias/email-validator/composer.json
+++ b/egulias/email-validator/composer.json
@@ -9,18 +9,18 @@
],
"extra": {
"branch-alias": {
- "dev-master": "2.1.x-dev"
+ "dev-master": "3.0.x-dev"
}
},
"require": {
- "php": ">=5.5",
- "doctrine/lexer": "^1.0.1",
- "symfony/polyfill-intl-idn": "^1.10"
+ "php": ">=7.2",
+ "doctrine/lexer": "^1.2",
+ "symfony/polyfill-intl-idn": "^1.15"
},
"require-dev": {
- "dominicsayers/isemail": "^3.0.7",
- "phpunit/phpunit": "^4.8.36|^7.5.15",
- "satooshi/php-coveralls": "^1.0.1"
+ "php-coveralls/php-coveralls": "^2.2",
+ "phpunit/phpunit": "^8.5.8|^9.3.3",
+ "vimeo/psalm": "^4"
},
"suggest": {
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
diff --git a/egulias/email-validator/composer.lock b/egulias/email-validator/composer.lock
new file mode 100644
index 00000000..c575e7fc
--- /dev/null
+++ b/egulias/email-validator/composer.lock
@@ -0,0 +1,4440 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "a77d36b64bc1213fecf4d4f92d759c3b",
+ "packages": [
+ {
+ "name": "doctrine/lexer",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/lexer.git",
+ "reference": "e864bbf5904cb8f5bb334f99209b48018522f042"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042",
+ "reference": "e864bbf5904cb8f5bb334f99209b48018522f042",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^6.0",
+ "phpstan/phpstan": "^0.11.8",
+ "phpunit/phpunit": "^8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+ "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "lexer",
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/lexer/issues",
+ "source": "https://github.com/doctrine/lexer/tree/1.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-05-25T17:44:05+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "2d63434d922daf7da8dd863e7907e67ee3031483"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483",
+ "reference": "2d63434d922daf7da8dd863e7907e67ee3031483",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "symfony/polyfill-intl-normalizer": "^1.10",
+ "symfony/polyfill-php72": "^1.10"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.22-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Idn\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Trevor Rowbotham",
+ "email": "trevor.rowbotham@pm.me"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "idn",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-22T09:19:47+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
+ "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.22-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-22T09:19:47+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "v1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+ "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.22-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-07T16:49:33+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "amphp/amp",
+ "version": "v2.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/amp.git",
+ "reference": "efca2b32a7580087adb8aabbff6be1dc1bb924a9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/efca2b32a7580087adb8aabbff6be1dc1bb924a9",
+ "reference": "efca2b32a7580087adb8aabbff6be1dc1bb924a9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "dev-master",
+ "amphp/phpunit-util": "^1",
+ "ext-json": "*",
+ "jetbrains/phpstorm-stubs": "^2019.3",
+ "phpunit/phpunit": "^6.0.9 | ^7",
+ "psalm/phar": "^3.11@dev",
+ "react/promise": "^2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Amp\\": "lib"
+ },
+ "files": [
+ "lib/functions.php",
+ "lib/Internal/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A non-blocking concurrency framework for PHP applications.",
+ "homepage": "http://amphp.org/amp",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "awaitable",
+ "concurrency",
+ "event",
+ "event-loop",
+ "future",
+ "non-blocking",
+ "promise"
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.org/amphp",
+ "issues": "https://github.com/amphp/amp/issues",
+ "source": "https://github.com/amphp/amp/tree/v2.5.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2021-01-10T17:06:37+00:00"
+ },
+ {
+ "name": "amphp/byte-stream",
+ "version": "v1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/byte-stream.git",
+ "reference": "f0c20cf598a958ba2aa8c6e5a71c697d652c7088"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/byte-stream/zipball/f0c20cf598a958ba2aa8c6e5a71c697d652c7088",
+ "reference": "f0c20cf598a958ba2aa8c6e5a71c697d652c7088",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^2",
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "dev-master",
+ "amphp/phpunit-util": "^1.4",
+ "friendsofphp/php-cs-fixer": "^2.3",
+ "jetbrains/phpstorm-stubs": "^2019.3",
+ "phpunit/phpunit": "^6 || ^7 || ^8",
+ "psalm/phar": "^3.11.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Amp\\ByteStream\\": "lib"
+ },
+ "files": [
+ "lib/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A stream abstraction to make working with non-blocking I/O simple.",
+ "homepage": "http://amphp.org/byte-stream",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "io",
+ "non-blocking",
+ "stream"
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.org/amphp",
+ "issues": "https://github.com/amphp/byte-stream/issues",
+ "source": "https://github.com/amphp/byte-stream/tree/master"
+ },
+ "time": "2020-06-29T18:35:05+00:00"
+ },
+ {
+ "name": "composer/package-versions-deprecated",
+ "version": "1.11.99.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/package-versions-deprecated.git",
+ "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/7413f0b55a051e89485c5cb9f765fe24bb02a7b6",
+ "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.1.0 || ^2.0",
+ "php": "^7 || ^8"
+ },
+ "replace": {
+ "ocramius/package-versions": "1.11.99"
+ },
+ "require-dev": {
+ "composer/composer": "^1.9.3 || ^2.0@dev",
+ "ext-zip": "^1.13",
+ "phpunit/phpunit": "^6.5 || ^7"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PackageVersions\\Installer",
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PackageVersions\\": "src/PackageVersions"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be"
+ }
+ ],
+ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
+ "support": {
+ "issues": "https://github.com/composer/package-versions-deprecated/issues",
+ "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.1"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-11T10:22:58+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
+ "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.54",
+ "symfony/phpunit-bridge": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-13T08:59:24+00:00"
+ },
+ {
+ "name": "composer/xdebug-handler",
+ "version": "1.4.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "f28d44c286812c714741478d968104c5e604a1d4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4",
+ "reference": "f28d44c286812c714741478d968104c5e604a1d4",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0",
+ "psr/log": "^1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Composer\\XdebugHandler\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
+ }
+ ],
+ "description": "Restarts a process without Xdebug.",
+ "keywords": [
+ "Xdebug",
+ "performance"
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/1.4.5"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-13T08:04:11+00:00"
+ },
+ {
+ "name": "dnoegel/php-xdg-base-dir",
+ "version": "v0.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dnoegel/php-xdg-base-dir.git",
+ "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
+ "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "XdgBaseDir\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "implementation of xdg base directory specification for php",
+ "support": {
+ "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues",
+ "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1"
+ },
+ "time": "2019-12-04T15:06:13+00:00"
+ },
+ {
+ "name": "doctrine/instantiator",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
+ "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^8.0",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
+ "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan-phpunit": "^0.12",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/1.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-10T18:47:58+00:00"
+ },
+ {
+ "name": "felixfbecker/advanced-json-rpc",
+ "version": "v3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git",
+ "reference": "06f0b06043c7438959dbdeed8bb3f699a19be22e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/06f0b06043c7438959dbdeed8bb3f699a19be22e",
+ "reference": "06f0b06043c7438959dbdeed8bb3f699a19be22e",
+ "shasum": ""
+ },
+ "require": {
+ "netresearch/jsonmapper": "^1.0 || ^2.0",
+ "php": "^7.1 || ^8.0",
+ "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "AdvancedJsonRpc\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "ISC"
+ ],
+ "authors": [
+ {
+ "name": "Felix Becker",
+ "email": "felix.b@outlook.com"
+ }
+ ],
+ "description": "A more advanced JSONRPC implementation",
+ "support": {
+ "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues",
+ "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.0"
+ },
+ "time": "2021-01-10T17:48:47+00:00"
+ },
+ {
+ "name": "felixfbecker/language-server-protocol",
+ "version": "1.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/felixfbecker/php-language-server-protocol.git",
+ "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730",
+ "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "*",
+ "squizlabs/php_codesniffer": "^3.1",
+ "vimeo/psalm": "^4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "LanguageServerProtocol\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "ISC"
+ ],
+ "authors": [
+ {
+ "name": "Felix Becker",
+ "email": "felix.b@outlook.com"
+ }
+ ],
+ "description": "PHP classes for the Language Server Protocol",
+ "keywords": [
+ "language",
+ "microsoft",
+ "php",
+ "server"
+ ],
+ "support": {
+ "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
+ "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1"
+ },
+ "time": "2021-02-22T14:02:09+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79",
+ "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^1.4",
+ "guzzlehttp/psr7": "^1.7",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "php-http/client-integration-tests": "^3.0",
+ "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+ "psr/log": "^1.1"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/alexeyshockov",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/gmponos",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-10T11:47:56+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "60d379c243457e073cff02bc323a2a86cb355631"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631",
+ "reference": "60d379c243457e073cff02bc323a2a86cb355631",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^4.4 || ^5.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/1.4.0"
+ },
+ "time": "2020-09-30T07:37:28+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3",
+ "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0",
+ "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "ext-zlib": "*",
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/1.7.0"
+ },
+ "time": "2020-09-30T07:37:11+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.10.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
+ "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "replace": {
+ "myclabs/deep-copy": "self.version"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.0",
+ "doctrine/common": "^2.6",
+ "phpunit/phpunit": "^7.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ },
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-13T09:40:50+00:00"
+ },
+ {
+ "name": "netresearch/jsonmapper",
+ "version": "v2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cweiske/jsonmapper.git",
+ "reference": "e0f1e33a71587aca81be5cffbb9746510e1fe04e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/e0f1e33a71587aca81be5cffbb9746510e1fe04e",
+ "reference": "e0f1e33a71587aca81be5cffbb9746510e1fe04e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-spl": "*",
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.35 || ~5.7 || ~6.4 || ~7.0",
+ "squizlabs/php_codesniffer": "~3.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "JsonMapper": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "OSL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Christian Weiske",
+ "email": "cweiske@cweiske.de",
+ "homepage": "http://github.com/cweiske/jsonmapper/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Map nested JSON structures onto PHP classes",
+ "support": {
+ "email": "cweiske@cweiske.de",
+ "issues": "https://github.com/cweiske/jsonmapper/issues",
+ "source": "https://github.com/cweiske/jsonmapper/tree/master"
+ },
+ "time": "2020-04-16T18:48:43+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v4.10.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+ "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4"
+ },
+ "time": "2020-12-20T10:01:03+00:00"
+ },
+ {
+ "name": "openlss/lib-array2xml",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nullivex/lib-array2xml.git",
+ "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90",
+ "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "LSS": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Bryan Tong",
+ "email": "bryan@nullivex.com",
+ "homepage": "https://www.nullivex.com"
+ },
+ {
+ "name": "Tony Butler",
+ "email": "spudz76@gmail.com",
+ "homepage": "https://www.nullivex.com"
+ }
+ ],
+ "description": "Array2XML conversion library credit to lalit.org",
+ "homepage": "https://www.nullivex.com",
+ "keywords": [
+ "array",
+ "array conversion",
+ "xml",
+ "xml conversion"
+ ],
+ "support": {
+ "issues": "https://github.com/nullivex/lib-array2xml/issues",
+ "source": "https://github.com/nullivex/lib-array2xml/tree/master"
+ },
+ "time": "2019-03-29T20:06:56+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
+ "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/master"
+ },
+ "time": "2020-06-27T14:33:11+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "bae7c545bef187884426f042434e561ab1ddb182"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182",
+ "reference": "bae7c545bef187884426f042434e561ab1ddb182",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.1.0"
+ },
+ "time": "2021-02-23T14:00:09+00:00"
+ },
+ {
+ "name": "php-coveralls/php-coveralls",
+ "version": "v2.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-coveralls/php-coveralls.git",
+ "reference": "909381bd40a17ae6e9076051f0d73293c1c091af"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/909381bd40a17ae6e9076051f0d73293c1c091af",
+ "reference": "909381bd40a17ae6e9076051f0d73293c1c091af",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-simplexml": "*",
+ "guzzlehttp/guzzle": "^6.0 || ^7.0",
+ "php": "^5.5 || ^7.0 || ^8.0",
+ "psr/log": "^1.0",
+ "symfony/config": "^2.1 || ^3.0 || ^4.0 || ^5.0",
+ "symfony/console": "^2.1 || ^3.0 || ^4.0 || ^5.0",
+ "symfony/stopwatch": "^2.0 || ^3.0 || ^4.0 || ^5.0",
+ "symfony/yaml": "^2.0.5 || ^3.0 || ^4.0 || ^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0 || ^7.0 || ^8.0 || ^9.0",
+ "sanmai/phpunit-legacy-adapter": "^6.1 || ^8.0"
+ },
+ "suggest": {
+ "symfony/http-kernel": "Allows Symfony integration"
+ },
+ "bin": [
+ "bin/php-coveralls"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PhpCoveralls\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Kitamura Satoshi",
+ "email": "with.no.parachute@gmail.com",
+ "homepage": "https://www.facebook.com/satooshi.jp",
+ "role": "Original creator"
+ },
+ {
+ "name": "Takashi Matsuo",
+ "email": "tmatsuo@google.com"
+ },
+ {
+ "name": "Google Inc"
+ },
+ {
+ "name": "Dariusz Ruminski",
+ "email": "dariusz.ruminski@gmail.com",
+ "homepage": "https://github.com/keradus"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/php-coveralls/php-coveralls/graphs/contributors"
+ }
+ ],
+ "description": "PHP client library for Coveralls API",
+ "homepage": "https://github.com/php-coveralls/php-coveralls",
+ "keywords": [
+ "ci",
+ "coverage",
+ "github",
+ "test"
+ ],
+ "support": {
+ "issues": "https://github.com/php-coveralls/php-coveralls/issues",
+ "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.4.3"
+ },
+ "time": "2020-12-24T09:17:03+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-common",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+ "homepage": "http://www.phpdoc.org",
+ "keywords": [
+ "FQSEN",
+ "phpDocumentor",
+ "phpdoc",
+ "reflection",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
+ "time": "2020-06-27T09:03:43+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-docblock",
+ "version": "5.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
+ "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "php": "^7.2 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.2",
+ "phpdocumentor/type-resolver": "^1.3",
+ "webmozart/assert": "^1.9.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ },
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "account@ijaap.nl"
+ }
+ ],
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master"
+ },
+ "time": "2020-09-03T19:13:55+00:00"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+ "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0"
+ },
+ "require-dev": {
+ "ext-tokenizer": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
+ },
+ "time": "2020-09-17T18:55:26+00:00"
+ },
+ {
+ "name": "phpspec/prophecy",
+ "version": "1.12.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpspec/prophecy.git",
+ "reference": "245710e971a030f42e08f4912863805570f23d39"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/245710e971a030f42e08f4912863805570f23d39",
+ "reference": "245710e971a030f42e08f4912863805570f23d39",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.2",
+ "php": "^7.2 || ~8.0, <8.1",
+ "phpdocumentor/reflection-docblock": "^5.2",
+ "sebastian/comparator": "^3.0 || ^4.0",
+ "sebastian/recursion-context": "^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "^6.0",
+ "phpunit/phpunit": "^8.0 || ^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.11.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Prophecy\\": "src/Prophecy"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Konstantin Kudryashov",
+ "email": "ever.zet@gmail.com",
+ "homepage": "http://everzet.com"
+ },
+ {
+ "name": "Marcello Duarte",
+ "email": "marcello.duarte@gmail.com"
+ }
+ ],
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
+ "homepage": "https://github.com/phpspec/prophecy",
+ "keywords": [
+ "Double",
+ "Dummy",
+ "fake",
+ "mock",
+ "spy",
+ "stub"
+ ],
+ "support": {
+ "issues": "https://github.com/phpspec/prophecy/issues",
+ "source": "https://github.com/phpspec/prophecy/tree/1.12.2"
+ },
+ "time": "2020-12-19T10:15:11+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "7.0.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bb7c9a210c72e4709cdde67f8b7362f672f2225c",
+ "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=7.2",
+ "phpunit/php-file-iterator": "^2.0.2",
+ "phpunit/php-text-template": "^1.2.1",
+ "phpunit/php-token-stream": "^3.1.1 || ^4.0",
+ "sebastian/code-unit-reverse-lookup": "^1.0.1",
+ "sebastian/environment": "^4.2.2",
+ "sebastian/version": "^2.0.1",
+ "theseer/tokenizer": "^1.1.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.2.2"
+ },
+ "suggest": {
+ "ext-xdebug": "^2.7.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.0-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": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.14"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-12-02T13:39:03+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4b49fb70f067272b659ef0174ff9ca40fdaa6357",
+ "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-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": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T08:25:21+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
+ },
+ "time": "2015-06-21T13:50:34+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "2.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662",
+ "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.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": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T08:20:02+00:00"
+ },
+ {
+ "name": "phpunit/php-token-stream",
+ "version": "3.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+ "reference": "472b687829041c24b25f475e14c2f38a09edf1c2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/472b687829041c24b25f475e14c2f38a09edf1c2",
+ "reference": "472b687829041c24b25f475e14c2f38a09edf1c2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Wrapper around PHP's tokenizer extension.",
+ "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+ "keywords": [
+ "tokenizer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-token-stream/issues",
+ "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "abandoned": true,
+ "time": "2020-11-30T08:38:46+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "8.5.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "c25f79895d27b6ecd5abfa63de1606b786a461a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c25f79895d27b6ecd5abfa63de1606b786a461a3",
+ "reference": "c25f79895d27b6ecd5abfa63de1606b786a461a3",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.3.1",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.10.0",
+ "phar-io/manifest": "^2.0.1",
+ "phar-io/version": "^3.0.2",
+ "php": ">=7.2",
+ "phpspec/prophecy": "^1.10.3",
+ "phpunit/php-code-coverage": "^7.0.12",
+ "phpunit/php-file-iterator": "^2.0.2",
+ "phpunit/php-text-template": "^1.2.1",
+ "phpunit/php-timer": "^2.1.2",
+ "sebastian/comparator": "^3.0.2",
+ "sebastian/diff": "^3.0.2",
+ "sebastian/environment": "^4.2.3",
+ "sebastian/exporter": "^3.1.2",
+ "sebastian/global-state": "^3.0.0",
+ "sebastian/object-enumerator": "^3.0.3",
+ "sebastian/resource-operations": "^2.0.1",
+ "sebastian/type": "^1.1.3",
+ "sebastian/version": "^2.0.1"
+ },
+ "require-dev": {
+ "ext-pdo": "*"
+ },
+ "suggest": {
+ "ext-soap": "*",
+ "ext-xdebug": "*",
+ "phpunit/php-invoker": "^2.0.0"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.5-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": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.14"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/donate.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-01-17T07:37:30+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+ "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/master"
+ },
+ "time": "2017-02-14T16:28:37+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client/tree/master"
+ },
+ "time": "2020-06-29T06:28:15+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/master"
+ },
+ "time": "2016-08-06T14:39:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.3"
+ },
+ "time": "2020-03-23T09:12:05+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
+ "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T08:15:22+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "1071dfcef776a57013124ff35e1fc41ccd294758"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758",
+ "reference": "1071dfcef776a57013124ff35e1fc41ccd294758",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "sebastian/diff": "^3.0",
+ "sebastian/exporter": "^3.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T08:04:30+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
+ "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5 || ^8.0",
+ "symfony/process": "^2 || ^3.3 || ^4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:59:04+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "4.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
+ "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:53:42+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "3.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e",
+ "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0",
+ "sebastian/recursion-context": "^3.0"
+ },
+ "require-dev": {
+ "ext-mbstring": "*",
+ "phpunit/phpunit": "^6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "http://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:47:53+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b",
+ "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2",
+ "sebastian/object-reflector": "^1.1.1",
+ "sebastian/recursion-context": "^3.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^8.0"
+ },
+ "suggest": {
+ "ext-uopz": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:43:24+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "3.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
+ "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0",
+ "sebastian/object-reflector": "^1.1.1",
+ "sebastian/recursion-context": "^3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:40:27+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
+ "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "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"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:37:18+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb",
+ "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:34:24+00:00"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3",
+ "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides a list of PHP built-in functions that operate on resources",
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:30:19+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4",
+ "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4",
+ "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",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/1.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:25:11+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
+ "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-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": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/master"
+ },
+ "time": "2016-10-03T07:35:21+00:00"
+ },
+ {
+ "name": "symfony/config",
+ "version": "v5.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/config.git",
+ "reference": "50e0e1314a3b2609d32b6a5a0d0fb5342494c4ab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/config/zipball/50e0e1314a3b2609d32b6a5a0d0fb5342494c4ab",
+ "reference": "50e0e1314a3b2609d32b6a5a0d0fb5342494c4ab",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1",
+ "symfony/filesystem": "^4.4|^5.0",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-php80": "^1.15"
+ },
+ "conflict": {
+ "symfony/finder": "<4.4"
+ },
+ "require-dev": {
+ "symfony/event-dispatcher": "^4.4|^5.0",
+ "symfony/finder": "^4.4|^5.0",
+ "symfony/messenger": "^4.4|^5.0",
+ "symfony/service-contracts": "^1.1|^2",
+ "symfony/yaml": "^4.4|^5.0"
+ },
+ "suggest": {
+ "symfony/yaml": "To use the yaml reference dumper"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Config\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/config/tree/v5.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-27T10:15:41+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v5.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/89d4b176d12a2946a1ae4e34906a025b7b6b135a",
+ "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php73": "^1.8",
+ "symfony/polyfill-php80": "^1.15",
+ "symfony/service-contracts": "^1.1|^2",
+ "symfony/string": "^5.1"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<4.4",
+ "symfony/dotenv": "<5.1",
+ "symfony/event-dispatcher": "<4.4",
+ "symfony/lock": "<4.4",
+ "symfony/process": "<4.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0"
+ },
+ "require-dev": {
+ "psr/log": "~1.0",
+ "symfony/config": "^4.4|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/event-dispatcher": "^4.4|^5.0",
+ "symfony/lock": "^4.4|^5.0",
+ "symfony/process": "^4.4|^5.0",
+ "symfony/var-dumper": "^4.4|^5.0"
+ },
+ "suggest": {
+ "psr/log": "For using the console logger",
+ "symfony/event-dispatcher": "",
+ "symfony/lock": "",
+ "symfony/process": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v5.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-28T22:06:19+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+ "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/master"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-09-07T11:33:47+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v5.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "262d033b57c73e8b59cd6e68a45c528318b15038"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/262d033b57c73e8b59cd6e68a45c528318b15038",
+ "reference": "262d033b57c73e8b59cd6e68a45c528318b15038",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v5.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-27T10:01:46+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
+ "reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.22-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-07T16:49:33+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170",
+ "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.22-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-22T09:19:47+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
+ "reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.22-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-22T09:19:47+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php73",
+ "version": "v1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php73.git",
+ "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+ "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.22-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php73\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-07T16:49:33+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
+ "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.22-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-07T16:49:33+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+ "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/container": "^1.0"
+ },
+ "suggest": {
+ "symfony/service-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/master"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-09-07T11:33:47+00:00"
+ },
+ {
+ "name": "symfony/stopwatch",
+ "version": "v5.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "b12274acfab9d9850c52583d136a24398cdf1a0c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b12274acfab9d9850c52583d136a24398cdf1a0c",
+ "reference": "b12274acfab9d9850c52583d136a24398cdf1a0c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/service-contracts": "^1.0|^2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Stopwatch\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides a way to profile code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/stopwatch/tree/v5.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-27T10:15:41+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v5.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "c95468897f408dd0aca2ff582074423dd0455122"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122",
+ "reference": "c95468897f408dd0aca2ff582074423dd0455122",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php80": "~1.15"
+ },
+ "require-dev": {
+ "symfony/error-handler": "^4.4|^5.0",
+ "symfony/http-client": "^4.4|^5.0",
+ "symfony/translation-contracts": "^1.1|^2",
+ "symfony/var-exporter": "^4.4|^5.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "files": [
+ "Resources/functions.php"
+ ],
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v5.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-25T15:14:59+00:00"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v5.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "338cddc6d74929f6adf19ca5682ac4b8e109cdb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/338cddc6d74929f6adf19ca5682ac4b8e109cdb0",
+ "reference": "338cddc6d74929f6adf19ca5682ac4b8e109cdb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "conflict": {
+ "symfony/console": "<4.4"
+ },
+ "require-dev": {
+ "symfony/console": "^4.4|^5.0"
+ },
+ "suggest": {
+ "symfony/console": "For validating YAML files using the lint command"
+ },
+ "bin": [
+ "Resources/bin/yaml-lint"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Loads and dumps YAML files",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/yaml/tree/v5.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-02-03T04:42:09+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "75a63c33a8577608444246075ea0af0d052e452a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a",
+ "reference": "75a63c33a8577608444246075ea0af0d052e452a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/master"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2020-07-12T23:59:07+00:00"
+ },
+ {
+ "name": "vimeo/psalm",
+ "version": "4.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vimeo/psalm.git",
+ "reference": "bca09d74adc704c4eaee36a3c3e9d379e290fc3b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vimeo/psalm/zipball/bca09d74adc704c4eaee36a3c3e9d379e290fc3b",
+ "reference": "bca09d74adc704c4eaee36a3c3e9d379e290fc3b",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^2.1",
+ "amphp/byte-stream": "^1.5",
+ "composer/package-versions-deprecated": "^1.8.0",
+ "composer/semver": "^1.4 || ^2.0 || ^3.0",
+ "composer/xdebug-handler": "^1.1",
+ "dnoegel/php-xdg-base-dir": "^0.1.1",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "felixfbecker/advanced-json-rpc": "^3.0.3",
+ "felixfbecker/language-server-protocol": "^1.5",
+ "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
+ "nikic/php-parser": "^4.10.1",
+ "openlss/lib-array2xml": "^1.0",
+ "php": "^7.1|^8",
+ "sebastian/diff": "^3.0 || ^4.0",
+ "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0",
+ "webmozart/path-util": "^2.3"
+ },
+ "provide": {
+ "psalm/psalm": "self.version"
+ },
+ "require-dev": {
+ "amphp/amp": "^2.4.2",
+ "bamarni/composer-bin-plugin": "^1.2",
+ "brianium/paratest": "^4.0||^6.0",
+ "ext-curl": "*",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpdocumentor/reflection-docblock": "^5",
+ "phpmyadmin/sql-parser": "5.1.0||dev-master",
+ "phpspec/prophecy": ">=1.9.0",
+ "phpunit/phpunit": "^9.0",
+ "psalm/plugin-phpunit": "^0.13",
+ "slevomat/coding-standard": "^6.3.11",
+ "squizlabs/php_codesniffer": "^3.5",
+ "symfony/process": "^4.3",
+ "weirdan/prophecy-shim": "^1.0 || ^2.0"
+ },
+ "suggest": {
+ "ext-igbinary": "^2.0.5"
+ },
+ "bin": [
+ "psalm",
+ "psalm-language-server",
+ "psalm-plugin",
+ "psalm-refactor",
+ "psalter"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.x-dev",
+ "dev-3.x": "3.x-dev",
+ "dev-2.x": "2.x-dev",
+ "dev-1.x": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psalm\\": "src/Psalm/"
+ },
+ "files": [
+ "src/functions.php",
+ "src/spl_object_id.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matthew Brown"
+ }
+ ],
+ "description": "A static analysis tool for finding errors in PHP applications",
+ "keywords": [
+ "code",
+ "inspection",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/vimeo/psalm/issues",
+ "source": "https://github.com/vimeo/psalm/tree/4.6.2"
+ },
+ "time": "2021-02-26T02:24:18+00:00"
+ },
+ {
+ "name": "webmozart/assert",
+ "version": "1.9.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
+ "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.3 || ^7.0 || ^8.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<0.12.20",
+ "vimeo/psalm": "<3.9.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36 || ^7.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.9.1"
+ },
+ "time": "2020-07-08T17:02:28+00:00"
+ },
+ {
+ "name": "webmozart/path-util",
+ "version": "2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozart/path-util.git",
+ "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
+ "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "webmozart/assert": "~1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.6",
+ "sebastian/version": "^1.0.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\PathUtil\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.",
+ "support": {
+ "issues": "https://github.com/webmozart/path-util/issues",
+ "source": "https://github.com/webmozart/path-util/tree/2.3.0"
+ },
+ "time": "2015-12-17T08:42:14+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=7.2"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.0.0"
+}
diff --git a/egulias/email-validator/src/EmailLexer.php b/egulias/email-validator/src/EmailLexer.php
index 59dcd587..41e9ea94 100644
--- a/egulias/email-validator/src/EmailLexer.php
+++ b/egulias/email-validator/src/EmailLexer.php
@@ -7,37 +7,52 @@ use Doctrine\Common\Lexer\AbstractLexer;
class EmailLexer extends AbstractLexer
{
//ASCII values
- const C_DEL = 127;
+ const S_EMPTY = null;
const C_NUL = 0;
- const S_AT = 64;
- const S_BACKSLASH = 92;
- const S_DOT = 46;
+ const S_HTAB = 9;
+ const S_LF = 10;
+ const S_CR = 13;
+ const S_SP = 32;
+ const EXCLAMATION = 33;
const S_DQUOTE = 34;
+ const NUMBER_SIGN = 35;
+ const DOLLAR = 36;
+ const PERCENTAGE = 37;
+ const AMPERSAND = 38;
const S_SQUOTE = 39;
+ const S_OPENPARENTHESIS = 40;
+ const S_CLOSEPARENTHESIS = 41;
+ const ASTERISK = 42;
+ const S_PLUS = 43;
+ const S_COMMA = 44;
+ const S_HYPHEN = 45;
+ const S_DOT = 46;
+ const S_SLASH = 47;
+ const S_COLON = 58;
+ const S_SEMICOLON = 59;
+ const S_LOWERTHAN = 60;
+ const S_EQUAL = 61;
+ const S_GREATERTHAN = 62;
+ const QUESTIONMARK = 63;
+ const S_AT = 64;
+ const S_OPENBRACKET = 91;
+ const S_BACKSLASH = 92;
+ const S_CLOSEBRACKET = 93;
+ const CARET = 94;
+ const S_UNDERSCORE = 95;
const S_BACKTICK = 96;
- const S_OPENPARENTHESIS = 49;
- const S_CLOSEPARENTHESIS = 261;
- const S_OPENBRACKET = 262;
- const S_CLOSEBRACKET = 263;
- const S_HYPHEN = 264;
- const S_COLON = 265;
- const S_DOUBLECOLON = 266;
- const S_SP = 267;
- const S_HTAB = 268;
- const S_CR = 269;
- const S_LF = 270;
- const S_IPV6TAG = 271;
- const S_LOWERTHAN = 272;
- const S_GREATERTHAN = 273;
- const S_COMMA = 274;
- const S_SEMICOLON = 275;
- const S_OPENQBRACKET = 276;
- const S_CLOSEQBRACKET = 277;
- const S_SLASH = 278;
- const S_EMPTY = null;
+ const S_OPENCURLYBRACES = 123;
+ const S_PIPE = 124;
+ const S_CLOSECURLYBRACES = 125;
+ const S_TILDE = 126;
+ const C_DEL = 127;
+ const INVERT_QUESTIONMARK= 168;
+ const INVERT_EXCLAMATION = 173;
const GENERIC = 300;
- const CRLF = 301;
+ const S_IPV6TAG = 301;
const INVALID = 302;
+ const CRLF = 1310;
+ const S_DOUBLECOLON = 5858;
const ASCII_INVALID_FROM = 127;
const ASCII_INVALID_TO = 199;
@@ -47,6 +62,8 @@ class EmailLexer extends AbstractLexer
* @var array
*/
protected $charValue = array(
+ '{' => self::S_OPENCURLYBRACES,
+ '}' => self::S_CLOSECURLYBRACES,
'(' => self::S_OPENPARENTHESIS,
')' => self::S_CLOSEPARENTHESIS,
'<' => self::S_LOWERTHAN,
@@ -71,10 +88,23 @@ class EmailLexer extends AbstractLexer
"\n" => self::S_LF,
"\r\n" => self::CRLF,
'IPv6' => self::S_IPV6TAG,
- '{' => self::S_OPENQBRACKET,
- '}' => self::S_CLOSEQBRACKET,
'' => self::S_EMPTY,
'\0' => self::C_NUL,
+ '*' => self::ASTERISK,
+ '!' => self::EXCLAMATION,
+ '&' => self::AMPERSAND,
+ '^' => self::CARET,
+ '$' => self::DOLLAR,
+ '%' => self::PERCENTAGE,
+ '~' => self::S_TILDE,
+ '|' => self::S_PIPE,
+ '_' => self::S_UNDERSCORE,
+ '=' => self::S_EQUAL,
+ '+' => self::S_PLUS,
+ '¿' => self::INVERT_QUESTIONMARK,
+ '?' => self::QUESTIONMARK,
+ '#' => self::NUMBER_SIGN,
+ '¡' => self::INVERT_EXCLAMATION,
);
/**
@@ -94,7 +124,9 @@ class EmailLexer extends AbstractLexer
*
* @var array
*
+ * @psalm-suppress NonInvariantDocblockPropertyType
* @psalm-var array{value:string, type:null|int, position:int}
+ * @psalm-suppress NonInvariantDocblockPropertyType
*/
public $token;
@@ -114,6 +146,16 @@ class EmailLexer extends AbstractLexer
'position' => 0,
];
+ /**
+ * @var string
+ */
+ private $accumulator = '';
+
+ /**
+ * @var bool
+ */
+ private $hasToRecord = false;
+
public function __construct()
{
$this->previous = $this->token = self::$nullToken;
@@ -173,10 +215,18 @@ class EmailLexer extends AbstractLexer
*/
public function moveNext()
{
+ if ($this->hasToRecord && $this->previous === self::$nullToken) {
+ $this->accumulator .= $this->token['value'];
+ }
+
$this->previous = $this->token;
$hasNext = parent::moveNext();
$this->token = $this->token ?: self::$nullToken;
+ if ($this->hasToRecord) {
+ $this->accumulator .= $this->token['value'];
+ }
+
return $hasNext;
}
@@ -188,7 +238,7 @@ class EmailLexer extends AbstractLexer
protected function getCatchablePatterns()
{
return array(
- '[a-zA-Z_]+[46]?', //ASCII and domain literal
+ '[a-zA-Z]+[46]?', //ASCII and domain literal
'[^\x00-\x7F]', //UTF-8
'[0-9]+',
'\r\n',
@@ -205,7 +255,9 @@ class EmailLexer extends AbstractLexer
*/
protected function getNonCatchablePatterns()
{
- return array('[\xA0-\xff]+');
+ return [
+ '[\xA0-\xff]+',
+ ];
}
/**
@@ -217,28 +269,38 @@ class EmailLexer extends AbstractLexer
*/
protected function getType(&$value)
{
- if ($this->isNullType($value)) {
- return self::C_NUL;
+ $encoded = $value;
+
+ if (mb_detect_encoding($value, 'auto', true) !== 'UTF-8') {
+ $encoded = utf8_encode($value);
+ }
+
+ if ($this->isValid($encoded)) {
+ return $this->charValue[$encoded];
}
- if ($this->isValid($value)) {
- return $this->charValue[$value];
+ if ($this->isNullType($encoded)) {
+ return self::C_NUL;
}
- if ($this->isUTF8Invalid($value)) {
+ if ($this->isInvalidChar($encoded)) {
$this->hasInvalidTokens = true;
return self::INVALID;
}
+
return self::GENERIC;
}
- /**
- * @param string $value
- *
- * @return bool
- */
- protected function isValid($value)
+ protected function isInvalidChar(string $value) : bool
+ {
+ if(preg_match("/[^\p{S}\p{C}\p{Cc}]+/iu", $value) ) {
+ return false;
+ }
+ return true;
+ }
+
+ protected function isValid(string $value) : bool
{
if (isset($this->charValue[$value])) {
return true;
@@ -260,11 +322,7 @@ class EmailLexer extends AbstractLexer
return false;
}
- /**
- * @param string $value
- * @return bool
- */
- protected function isUTF8Invalid($value)
+ protected function isUTF8Invalid(string $value) : bool
{
if (preg_match('/\p{Cc}+/u', $value)) {
return true;
@@ -280,4 +338,24 @@ class EmailLexer extends AbstractLexer
{
return 'iu';
}
+
+ public function getAccumulatedValues() : string
+ {
+ return $this->accumulator;
+ }
+
+ public function startRecording() : void
+ {
+ $this->hasToRecord = true;
+ }
+
+ public function stopRecording() : void
+ {
+ $this->hasToRecord = false;
+ }
+
+ public function clearRecorded() : void
+ {
+ $this->accumulator = '';
+ }
}
diff --git a/egulias/email-validator/src/EmailParser.php b/egulias/email-validator/src/EmailParser.php
index 6b7bad66..c78f74a9 100644
--- a/egulias/email-validator/src/EmailParser.php
+++ b/egulias/email-validator/src/EmailParser.php
@@ -2,27 +2,20 @@
namespace Egulias\EmailValidator;
-use Egulias\EmailValidator\Exception\ExpectingATEXT;
-use Egulias\EmailValidator\Exception\NoLocalPart;
-use Egulias\EmailValidator\Parser\DomainPart;
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\Result;
use Egulias\EmailValidator\Parser\LocalPart;
+use Egulias\EmailValidator\Parser\DomainPart;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Warning\EmailTooLong;
+use Egulias\EmailValidator\Result\Reason\NoLocalPart;
-/**
- * EmailParser
- *
- * @author Eduardo Gulias Davis <me@egulias.com>
- */
-class EmailParser
+class EmailParser extends Parser
{
const EMAIL_MAX_LENGTH = 254;
/**
- * @var array
- */
- protected $warnings = [];
-
- /**
* @var string
*/
protected $domainPart = '';
@@ -31,104 +24,65 @@ class EmailParser
* @var string
*/
protected $localPart = '';
- /**
- * @var EmailLexer
- */
- protected $lexer;
- /**
- * @var LocalPart
- */
- protected $localPartParser;
+ public function parse(string $str) : Result
+ {
+ $result = parent::parse($str);
- /**
- * @var DomainPart
- */
- protected $domainPartParser;
+ $this->addLongEmailWarning($this->localPart, $this->domainPart);
- public function __construct(EmailLexer $lexer)
- {
- $this->lexer = $lexer;
- $this->localPartParser = new LocalPart($this->lexer);
- $this->domainPartParser = new DomainPart($this->lexer);
+ return $result;
}
-
- /**
- * @param string $str
- * @return array
- */
- public function parse($str)
+
+ protected function preLeftParsing(): Result
{
- $this->lexer->setInput($str);
-
if (!$this->hasAtToken()) {
- throw new NoLocalPart();
- }
-
-
- $this->localPartParser->parse($str);
- $this->domainPartParser->parse($str);
-
- $this->setParts($str);
-
- if ($this->lexer->hasInvalidTokens()) {
- throw new ExpectingATEXT();
+ return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]);
}
+ return new ValidEmail();
+ }
- return array('local' => $this->localPart, 'domain' => $this->domainPart);
+ protected function parseLeftFromAt(): Result
+ {
+ return $this->processLocalPart();
}
- /**
- * @return Warning\Warning[]
- */
- public function getWarnings()
+ protected function parseRightFromAt(): Result
{
- $localPartWarnings = $this->localPartParser->getWarnings();
- $domainPartWarnings = $this->domainPartParser->getWarnings();
- $this->warnings = array_merge($localPartWarnings, $domainPartWarnings);
+ return $this->processDomainPart();
+ }
- $this->addLongEmailWarning($this->localPart, $this->domainPart);
+ private function processLocalPart() : Result
+ {
+ $localPartParser = new LocalPart($this->lexer);
+ $localPartResult = $localPartParser->parse();
+ $this->localPart = $localPartParser->localPart();
+ $this->warnings = array_merge($localPartParser->getWarnings(), $this->warnings);
- return $this->warnings;
+ return $localPartResult;
}
- /**
- * @return string
- */
- public function getParsedDomainPart()
+ private function processDomainPart() : Result
{
- return $this->domainPart;
+ $domainPartParser = new DomainPart($this->lexer);
+ $domainPartResult = $domainPartParser->parse();
+ $this->domainPart = $domainPartParser->domainPart();
+ $this->warnings = array_merge($domainPartParser->getWarnings(), $this->warnings);
+
+ return $domainPartResult;
}
- /**
- * @param string $email
- */
- protected function setParts($email)
+ public function getDomainPart() : string
{
- $parts = explode('@', $email);
- $this->domainPart = $this->domainPartParser->getDomainPart();
- $this->localPart = $parts[0];
+ return $this->domainPart;
}
- /**
- * @return bool
- */
- protected function hasAtToken()
+ public function getLocalPart() : string
{
- $this->lexer->moveNext();
- $this->lexer->moveNext();
- if ($this->lexer->token['type'] === EmailLexer::S_AT) {
- return false;
- }
-
- return true;
+ return $this->localPart;
}
- /**
- * @param string $localPart
- * @param string $parsedDomainPart
- */
- protected function addLongEmailWarning($localPart, $parsedDomainPart)
+ private function addLongEmailWarning(string $localPart, string $parsedDomainPart) : void
{
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {
$this->warnings[EmailTooLong::CODE] = new EmailTooLong();
diff --git a/egulias/email-validator/src/EmailValidator.php b/egulias/email-validator/src/EmailValidator.php
index a30f21dc..5a2e5c82 100644
--- a/egulias/email-validator/src/EmailValidator.php
+++ b/egulias/email-validator/src/EmailValidator.php
@@ -2,7 +2,7 @@
namespace Egulias\EmailValidator;
-use Egulias\EmailValidator\Exception\InvalidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Validation\EmailValidation;
class EmailValidator
@@ -15,12 +15,12 @@ class EmailValidator
/**
* @var Warning\Warning[]
*/
- protected $warnings = [];
+ private $warnings = [];
/**
- * @var InvalidEmail|null
+ * @var ?InvalidEmail
*/
- protected $error;
+ private $error;
public function __construct()
{
@@ -32,7 +32,7 @@ class EmailValidator
* @param EmailValidation $emailValidation
* @return bool
*/
- public function isValid($email, EmailValidation $emailValidation)
+ public function isValid(string $email, EmailValidation $emailValidation)
{
$isValid = $emailValidation->isValid($email, $this->lexer);
$this->warnings = $emailValidation->getWarnings();
diff --git a/egulias/email-validator/src/Exception/AtextAfterCFWS.php b/egulias/email-validator/src/Exception/AtextAfterCFWS.php
deleted file mode 100644
index 97f41a2c..00000000
--- a/egulias/email-validator/src/Exception/AtextAfterCFWS.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class AtextAfterCFWS extends InvalidEmail
-{
- const CODE = 133;
- const REASON = "ATEXT found after CFWS";
-}
diff --git a/egulias/email-validator/src/Exception/CRLFAtTheEnd.php b/egulias/email-validator/src/Exception/CRLFAtTheEnd.php
deleted file mode 100644
index ec23bc71..00000000
--- a/egulias/email-validator/src/Exception/CRLFAtTheEnd.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class CRLFAtTheEnd extends InvalidEmail
-{
- const CODE = 149;
- const REASON = "CRLF at the end";
-}
diff --git a/egulias/email-validator/src/Exception/CRLFX2.php b/egulias/email-validator/src/Exception/CRLFX2.php
deleted file mode 100644
index 6bd377ee..00000000
--- a/egulias/email-validator/src/Exception/CRLFX2.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class CRLFX2 extends InvalidEmail
-{
- const CODE = 148;
- const REASON = "Folding whitespace CR LF found twice";
-}
diff --git a/egulias/email-validator/src/Exception/CRNoLF.php b/egulias/email-validator/src/Exception/CRNoLF.php
deleted file mode 100644
index 9c9f7394..00000000
--- a/egulias/email-validator/src/Exception/CRNoLF.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class CRNoLF extends InvalidEmail
-{
- const CODE = 150;
- const REASON = "Missing LF after CR";
-}
diff --git a/egulias/email-validator/src/Exception/CharNotAllowed.php b/egulias/email-validator/src/Exception/CharNotAllowed.php
deleted file mode 100644
index ea20ce59..00000000
--- a/egulias/email-validator/src/Exception/CharNotAllowed.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class CharNotAllowed extends InvalidEmail
-{
- const CODE = 201;
- const REASON = "Non allowed character in domain";
-}
diff --git a/egulias/email-validator/src/Exception/CommaInDomain.php b/egulias/email-validator/src/Exception/CommaInDomain.php
deleted file mode 100644
index e9245d96..00000000
--- a/egulias/email-validator/src/Exception/CommaInDomain.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class CommaInDomain extends InvalidEmail
-{
- const CODE = 200;
- const REASON = "Comma ',' is not allowed in domain part";
-}
diff --git a/egulias/email-validator/src/Exception/ConsecutiveAt.php b/egulias/email-validator/src/Exception/ConsecutiveAt.php
deleted file mode 100644
index 165ff57a..00000000
--- a/egulias/email-validator/src/Exception/ConsecutiveAt.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class ConsecutiveAt extends InvalidEmail
-{
- const CODE = 128;
- const REASON = "Consecutive AT";
-}
diff --git a/egulias/email-validator/src/Exception/ConsecutiveDot.php b/egulias/email-validator/src/Exception/ConsecutiveDot.php
deleted file mode 100644
index 949af3b5..00000000
--- a/egulias/email-validator/src/Exception/ConsecutiveDot.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class ConsecutiveDot extends InvalidEmail
-{
- const CODE = 132;
- const REASON = "Consecutive DOT";
-}
diff --git a/egulias/email-validator/src/Exception/DomainAcceptsNoMail.php b/egulias/email-validator/src/Exception/DomainAcceptsNoMail.php
deleted file mode 100644
index 40a99705..00000000
--- a/egulias/email-validator/src/Exception/DomainAcceptsNoMail.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class DomainAcceptsNoMail extends InvalidEmail
-{
- const CODE = 154;
- const REASON = 'Domain accepts no mail (Null MX, RFC7505)';
-} \ No newline at end of file
diff --git a/egulias/email-validator/src/Exception/DomainHyphened.php b/egulias/email-validator/src/Exception/DomainHyphened.php
deleted file mode 100644
index 6f586860..00000000
--- a/egulias/email-validator/src/Exception/DomainHyphened.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class DomainHyphened extends InvalidEmail
-{
- const CODE = 144;
- const REASON = "Hyphen found in domain";
-}
diff --git a/egulias/email-validator/src/Exception/DotAtEnd.php b/egulias/email-validator/src/Exception/DotAtEnd.php
deleted file mode 100644
index 05ade77d..00000000
--- a/egulias/email-validator/src/Exception/DotAtEnd.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class DotAtEnd extends InvalidEmail
-{
- const CODE = 142;
- const REASON = "Dot at the end";
-}
diff --git a/egulias/email-validator/src/Exception/DotAtStart.php b/egulias/email-validator/src/Exception/DotAtStart.php
deleted file mode 100644
index 7772df7f..00000000
--- a/egulias/email-validator/src/Exception/DotAtStart.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class DotAtStart extends InvalidEmail
-{
- const CODE = 141;
- const REASON = "Found DOT at start";
-}
diff --git a/egulias/email-validator/src/Exception/ExpectingAT.php b/egulias/email-validator/src/Exception/ExpectingAT.php
deleted file mode 100644
index 36d633c1..00000000
--- a/egulias/email-validator/src/Exception/ExpectingAT.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class ExpectingAT extends InvalidEmail
-{
- const CODE = 202;
- const REASON = "Expecting AT '@' ";
-}
diff --git a/egulias/email-validator/src/Exception/ExpectingATEXT.php b/egulias/email-validator/src/Exception/ExpectingATEXT.php
deleted file mode 100644
index 095d9db7..00000000
--- a/egulias/email-validator/src/Exception/ExpectingATEXT.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class ExpectingATEXT extends InvalidEmail
-{
- const CODE = 137;
- const REASON = "Expecting ATEXT";
-}
diff --git a/egulias/email-validator/src/Exception/ExpectingCTEXT.php b/egulias/email-validator/src/Exception/ExpectingCTEXT.php
deleted file mode 100644
index 63b870a4..00000000
--- a/egulias/email-validator/src/Exception/ExpectingCTEXT.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class ExpectingCTEXT extends InvalidEmail
-{
- const CODE = 139;
- const REASON = "Expecting CTEXT";
-}
diff --git a/egulias/email-validator/src/Exception/ExpectingDTEXT.php b/egulias/email-validator/src/Exception/ExpectingDTEXT.php
deleted file mode 100644
index 6a5bb9bf..00000000
--- a/egulias/email-validator/src/Exception/ExpectingDTEXT.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class ExpectingDTEXT extends InvalidEmail
-{
- const CODE = 129;
- const REASON = "Expected DTEXT";
-}
diff --git a/egulias/email-validator/src/Exception/ExpectingDomainLiteralClose.php b/egulias/email-validator/src/Exception/ExpectingDomainLiteralClose.php
deleted file mode 100644
index 81aad427..00000000
--- a/egulias/email-validator/src/Exception/ExpectingDomainLiteralClose.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class ExpectingDomainLiteralClose extends InvalidEmail
-{
- const CODE = 137;
- const REASON = "Closing bracket ']' for domain literal not found";
-}
diff --git a/egulias/email-validator/src/Exception/ExpectingQPair.php b/egulias/email-validator/src/Exception/ExpectingQPair.php
deleted file mode 100644
index a738eeb6..00000000
--- a/egulias/email-validator/src/Exception/ExpectingQPair.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class ExpectingQPair extends InvalidEmail
-{
- const CODE = 136;
- const REASON = "Expecting QPAIR";
-}
diff --git a/egulias/email-validator/src/Exception/InvalidEmail.php b/egulias/email-validator/src/Exception/InvalidEmail.php
deleted file mode 100644
index 1c0218e9..00000000
--- a/egulias/email-validator/src/Exception/InvalidEmail.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-abstract class InvalidEmail extends \InvalidArgumentException
-{
- const REASON = "Invalid email";
- const CODE = 0;
-
- public function __construct()
- {
- parent::__construct(static::REASON, static::CODE);
- }
-}
diff --git a/egulias/email-validator/src/Exception/LocalOrReservedDomain.php b/egulias/email-validator/src/Exception/LocalOrReservedDomain.php
deleted file mode 100644
index 695b05a4..00000000
--- a/egulias/email-validator/src/Exception/LocalOrReservedDomain.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class LocalOrReservedDomain extends InvalidEmail
-{
- const CODE = 153;
- const REASON = 'Local, mDNS or reserved domain (RFC2606, RFC6762)';
-} \ No newline at end of file
diff --git a/egulias/email-validator/src/Exception/NoDNSRecord.php b/egulias/email-validator/src/Exception/NoDNSRecord.php
deleted file mode 100644
index 0aa5fa78..00000000
--- a/egulias/email-validator/src/Exception/NoDNSRecord.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class NoDNSRecord extends InvalidEmail
-{
- const CODE = 5;
- const REASON = 'No MX or A DSN record was found for this email';
-}
diff --git a/egulias/email-validator/src/Exception/NoDomainPart.php b/egulias/email-validator/src/Exception/NoDomainPart.php
deleted file mode 100644
index 05a2604c..00000000
--- a/egulias/email-validator/src/Exception/NoDomainPart.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class NoDomainPart extends InvalidEmail
-{
- const CODE = 131;
- const REASON = "No Domain part";
-}
diff --git a/egulias/email-validator/src/Exception/NoLocalPart.php b/egulias/email-validator/src/Exception/NoLocalPart.php
deleted file mode 100644
index 07c14b84..00000000
--- a/egulias/email-validator/src/Exception/NoLocalPart.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class NoLocalPart extends InvalidEmail
-{
- const CODE = 130;
- const REASON = "No local part";
-}
diff --git a/egulias/email-validator/src/Exception/UnclosedComment.php b/egulias/email-validator/src/Exception/UnclosedComment.php
deleted file mode 100644
index 86b2b096..00000000
--- a/egulias/email-validator/src/Exception/UnclosedComment.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class UnclosedComment extends InvalidEmail
-{
- const CODE = 146;
- const REASON = "No closing comment token found";
-}
diff --git a/egulias/email-validator/src/Exception/UnclosedQuotedString.php b/egulias/email-validator/src/Exception/UnclosedQuotedString.php
deleted file mode 100644
index 730a39dd..00000000
--- a/egulias/email-validator/src/Exception/UnclosedQuotedString.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class UnclosedQuotedString extends InvalidEmail
-{
- const CODE = 145;
- const REASON = "Unclosed quoted string";
-}
diff --git a/egulias/email-validator/src/Exception/UnopenedComment.php b/egulias/email-validator/src/Exception/UnopenedComment.php
deleted file mode 100644
index cff12d92..00000000
--- a/egulias/email-validator/src/Exception/UnopenedComment.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Exception;
-
-class UnopenedComment extends InvalidEmail
-{
- const CODE = 152;
- const REASON = "No opening comment token found";
-}
diff --git a/egulias/email-validator/src/MessageIDParser.php b/egulias/email-validator/src/MessageIDParser.php
new file mode 100644
index 00000000..9b029e14
--- /dev/null
+++ b/egulias/email-validator/src/MessageIDParser.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace Egulias\EmailValidator;
+
+use Egulias\EmailValidator\Parser;
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Parser\IDLeftPart;
+use Egulias\EmailValidator\Parser\IDRightPart;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Warning\EmailTooLong;
+use Egulias\EmailValidator\Result\Reason\NoLocalPart;
+
+class MessageIDParser extends Parser
+{
+
+ const EMAILID_MAX_LENGTH = 254;
+
+ /**
+ * @var string
+ */
+ protected $idLeft = '';
+
+ /**
+ * @var string
+ */
+ protected $idRight = '';
+
+ public function parse(string $str) : Result
+ {
+ $result = parent::parse($str);
+
+ $this->addLongEmailWarning($this->idLeft, $this->idRight);
+
+ return $result;
+ }
+
+ protected function preLeftParsing(): Result
+ {
+ if (!$this->hasAtToken()) {
+ return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]);
+ }
+ return new ValidEmail();
+ }
+
+ protected function parseLeftFromAt(): Result
+ {
+ return $this->processIDLeft();
+ }
+
+ protected function parseRightFromAt(): Result
+ {
+ return $this->processIDRight();
+ }
+
+ private function processIDLeft() : Result
+ {
+ $localPartParser = new IDLeftPart($this->lexer);
+ $localPartResult = $localPartParser->parse();
+ $this->idLeft = $localPartParser->localPart();
+ $this->warnings = array_merge($localPartParser->getWarnings(), $this->warnings);
+
+ return $localPartResult;
+ }
+
+ private function processIDRight() : Result
+ {
+ $domainPartParser = new IDRightPart($this->lexer);
+ $domainPartResult = $domainPartParser->parse();
+ $this->idRight = $domainPartParser->domainPart();
+ $this->warnings = array_merge($domainPartParser->getWarnings(), $this->warnings);
+
+ return $domainPartResult;
+ }
+
+ public function getLeftPart() : string
+ {
+ return $this->idLeft;
+ }
+
+ public function getRightPart() : string
+ {
+ return $this->idRight;
+ }
+
+ private function addLongEmailWarning(string $localPart, string $parsedDomainPart) : void
+ {
+ if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAILID_MAX_LENGTH) {
+ $this->warnings[EmailTooLong::CODE] = new EmailTooLong();
+ }
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser.php b/egulias/email-validator/src/Parser.php
new file mode 100644
index 00000000..b1905f9a
--- /dev/null
+++ b/egulias/email-validator/src/Parser.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Egulias\EmailValidator;
+
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
+
+abstract class Parser
+{
+ /**
+ * @var Warning\Warning[]
+ */
+ protected $warnings = [];
+
+ /**
+ * @var EmailLexer
+ */
+ protected $lexer;
+
+ /**
+ * id-left "@" id-right
+ */
+ abstract protected function parseRightFromAt() : Result;
+ abstract protected function parseLeftFromAt() : Result;
+ abstract protected function preLeftParsing() : Result;
+
+
+ public function __construct(EmailLexer $lexer)
+ {
+ $this->lexer = $lexer;
+ }
+
+ public function parse(string $str) : Result
+ {
+ $this->lexer->setInput($str);
+
+ if ($this->lexer->hasInvalidTokens()) {
+ return new InvalidEmail(new ExpectingATEXT("Invalid tokens found"), $this->lexer->token["value"]);
+ }
+
+ $preParsingResult = $this->preLeftParsing();
+ if ($preParsingResult->isInvalid()) {
+ return $preParsingResult;
+ }
+
+ $localPartResult = $this->parseLeftFromAt();
+
+ if ($localPartResult->isInvalid()) {
+ return $localPartResult;
+ }
+
+ $domainPartResult = $this->parseRightFromAt();
+
+ if ($domainPartResult->isInvalid()) {
+ return $domainPartResult;
+ }
+
+ return new ValidEmail();
+ }
+
+ /**
+ * @return Warning\Warning[]
+ */
+ public function getWarnings() : array
+ {
+ return $this->warnings;
+ }
+
+ protected function hasAtToken() : bool
+ {
+ $this->lexer->moveNext();
+ $this->lexer->moveNext();
+
+ return $this->lexer->token['type'] !== EmailLexer::S_AT;
+ }
+}
diff --git a/egulias/email-validator/src/Parser/Comment.php b/egulias/email-validator/src/Parser/Comment.php
new file mode 100644
index 00000000..ffa61281
--- /dev/null
+++ b/egulias/email-validator/src/Parser/Comment.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Egulias\EmailValidator\Parser;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Warning\QuotedPart;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Parser\CommentStrategy\CommentStrategy;
+use Egulias\EmailValidator\Result\Reason\UnclosedComment;
+use Egulias\EmailValidator\Result\Reason\UnOpenedComment;
+use Egulias\EmailValidator\Warning\Comment as WarningComment;
+
+class Comment extends PartParser
+{
+ /**
+ * @var int
+ */
+ private $openedParenthesis = 0;
+
+ /**
+ * @var CommentStrategy
+ */
+ private $commentStrategy;
+
+ public function __construct(EmailLexer $lexer, CommentStrategy $commentStrategy)
+ {
+ $this->lexer = $lexer;
+ $this->commentStrategy = $commentStrategy;
+ }
+
+ public function parse() : Result
+ {
+ if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
+ $this->openedParenthesis++;
+ if($this->noClosingParenthesis()) {
+ return new InvalidEmail(new UnclosedComment(), $this->lexer->token['value']);
+ }
+ }
+
+ if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
+ return new InvalidEmail(new UnOpenedComment(), $this->lexer->token['value']);
+ }
+
+ $this->warnings[WarningComment::CODE] = new WarningComment();
+
+ $moreTokens = true;
+ while ($this->commentStrategy->exitCondition($this->lexer, $this->openedParenthesis) && $moreTokens){
+
+ if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
+ $this->openedParenthesis++;
+ }
+ $this->warnEscaping();
+ if($this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
+ $this->openedParenthesis--;
+ }
+ $moreTokens = $this->lexer->moveNext();
+ }
+
+ if($this->openedParenthesis >= 1) {
+ return new InvalidEmail(new UnclosedComment(), $this->lexer->token['value']);
+ } else if ($this->openedParenthesis < 0) {
+ return new InvalidEmail(new UnOpenedComment(), $this->lexer->token['value']);
+ }
+
+ $finalValidations = $this->commentStrategy->endOfLoopValidations($this->lexer);
+
+ $this->warnings = array_merge($this->warnings, $this->commentStrategy->getWarnings());
+
+ return $finalValidations;
+ }
+
+
+ /**
+ * @return bool
+ */
+ private function warnEscaping() : bool
+ {
+ //Backslash found
+ if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
+ return false;
+ }
+
+ if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
+ return false;
+ }
+
+ $this->warnings[QuotedPart::CODE] =
+ new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
+ return true;
+
+ }
+
+ private function noClosingParenthesis() : bool
+ {
+ try {
+ $this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
+ return false;
+ } catch (\RuntimeException $e) {
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/CommentStrategy/CommentStrategy.php b/egulias/email-validator/src/Parser/CommentStrategy/CommentStrategy.php
new file mode 100644
index 00000000..c388efd6
--- /dev/null
+++ b/egulias/email-validator/src/Parser/CommentStrategy/CommentStrategy.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Egulias\EmailValidator\Parser\CommentStrategy;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\Result;
+
+interface CommentStrategy
+{
+ /**
+ * Return "true" to continue, "false" to exit
+ */
+ public function exitCondition(EmailLexer $lexer, int $openedParenthesis) : bool;
+
+ public function endOfLoopValidations(EmailLexer $lexer) : Result;
+
+ public function getWarnings() : array;
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/CommentStrategy/DomainComment.php b/egulias/email-validator/src/Parser/CommentStrategy/DomainComment.php
new file mode 100644
index 00000000..b34ce29a
--- /dev/null
+++ b/egulias/email-validator/src/Parser/CommentStrategy/DomainComment.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Egulias\EmailValidator\Parser\CommentStrategy;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
+
+class DomainComment implements CommentStrategy
+{
+ public function exitCondition(EmailLexer $lexer, int $openedParenthesis) : bool
+ {
+ if (($openedParenthesis === 0 && $lexer->isNextToken(EmailLexer::S_DOT))){ // || !$internalLexer->moveNext()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function endOfLoopValidations(EmailLexer $lexer) : Result
+ {
+ //test for end of string
+ if (!$lexer->isNextToken(EmailLexer::S_DOT)) {
+ return new InvalidEmail(new ExpectingATEXT('DOT not found near CLOSEPARENTHESIS'), $lexer->token['value']);
+ }
+ //add warning
+ //Address is valid within the message but cannot be used unmodified for the envelope
+ return new ValidEmail();
+ }
+
+ public function getWarnings(): array
+ {
+ return [];
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/CommentStrategy/LocalComment.php b/egulias/email-validator/src/Parser/CommentStrategy/LocalComment.php
new file mode 100644
index 00000000..73bc7b2b
--- /dev/null
+++ b/egulias/email-validator/src/Parser/CommentStrategy/LocalComment.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Egulias\EmailValidator\Parser\CommentStrategy;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Warning\CFWSNearAt;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
+
+class LocalComment implements CommentStrategy
+{
+ /**
+ * @var array
+ */
+ private $warnings = [];
+
+ public function exitCondition(EmailLexer $lexer, int $openedParenthesis) : bool
+ {
+ return !$lexer->isNextToken(EmailLexer::S_AT);
+ }
+
+ public function endOfLoopValidations(EmailLexer $lexer) : Result
+ {
+ if (!$lexer->isNextToken(EmailLexer::S_AT)) {
+ return new InvalidEmail(new ExpectingATEXT('ATEX is not expected after closing comments'), $lexer->token['value']);
+ }
+ $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
+ return new ValidEmail();
+ }
+
+ public function getWarnings(): array
+ {
+ return $this->warnings;
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/DomainLiteral.php b/egulias/email-validator/src/Parser/DomainLiteral.php
new file mode 100644
index 00000000..54a6fab9
--- /dev/null
+++ b/egulias/email-validator/src/Parser/DomainLiteral.php
@@ -0,0 +1,212 @@
+<?php
+namespace Egulias\EmailValidator\Parser;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Warning\CFWSWithFWS;
+use Egulias\EmailValidator\Warning\IPV6BadChar;
+use Egulias\EmailValidator\Result\Reason\CRNoLF;
+use Egulias\EmailValidator\Warning\IPV6ColonEnd;
+use Egulias\EmailValidator\Warning\IPV6MaxGroups;
+use Egulias\EmailValidator\Warning\ObsoleteDTEXT;
+use Egulias\EmailValidator\Warning\AddressLiteral;
+use Egulias\EmailValidator\Warning\IPV6ColonStart;
+use Egulias\EmailValidator\Warning\IPV6Deprecated;
+use Egulias\EmailValidator\Warning\IPV6GroupCount;
+use Egulias\EmailValidator\Warning\IPV6DoubleColon;
+use Egulias\EmailValidator\Result\Reason\ExpectingDTEXT;
+use Egulias\EmailValidator\Result\Reason\UnusualElements;
+use Egulias\EmailValidator\Warning\DomainLiteral as WarningDomainLiteral;
+
+class DomainLiteral extends PartParser
+{
+ public function parse() : Result
+ {
+ $this->addTagWarnings();
+
+ $IPv6TAG = false;
+ $addressLiteral = '';
+
+ do {
+ if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
+ return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token['value']);
+ }
+
+ $this->addObsoleteWarnings();
+
+ if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENBRACKET, EmailLexer::S_OPENBRACKET))) {
+ return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token['value']);
+ }
+
+ if ($this->lexer->isNextTokenAny(
+ array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
+ )) {
+ $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
+ $this->parseFWS();
+ }
+
+ if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
+ return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']);
+ }
+
+ if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
+ return new InvalidEmail(new UnusualElements($this->lexer->token['value']), $this->lexer->token['value']);
+ }
+ if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
+ $IPv6TAG = true;
+ }
+
+ if ($this->lexer->token['type'] === EmailLexer::S_CLOSEBRACKET) {
+ break;
+ }
+
+ $addressLiteral .= $this->lexer->token['value'];
+
+ } while ($this->lexer->moveNext());
+
+
+ //Encapsulate
+ $addressLiteral = str_replace('[', '', $addressLiteral);
+ $isAddressLiteralIPv4 = $this->checkIPV4Tag($addressLiteral);
+
+ if (!$isAddressLiteralIPv4) {
+ return new ValidEmail();
+ } else {
+ $addressLiteral = $this->convertIPv4ToIPv6($addressLiteral);
+ }
+
+ if (!$IPv6TAG) {
+ $this->warnings[WarningDomainLiteral::CODE] = new WarningDomainLiteral();
+ return new ValidEmail();
+ }
+
+ $this->warnings[AddressLiteral::CODE] = new AddressLiteral();
+
+ $this->checkIPV6Tag($addressLiteral);
+
+ return new ValidEmail();
+ }
+
+ /**
+ * @param string $addressLiteral
+ * @param int $maxGroups
+ */
+ public function checkIPV6Tag($addressLiteral, $maxGroups = 8) : void
+ {
+ $prev = $this->lexer->getPrevious();
+ if ($prev['type'] === EmailLexer::S_COLON) {
+ $this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd();
+ }
+
+ $IPv6 = substr($addressLiteral, 5);
+ //Daniel Marschall's new IPv6 testing strategy
+ $matchesIP = explode(':', $IPv6);
+ $groupCount = count($matchesIP);
+ $colons = strpos($IPv6, '::');
+
+ if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
+ $this->warnings[IPV6BadChar::CODE] = new IPV6BadChar();
+ }
+
+ if ($colons === false) {
+ // We need exactly the right number of groups
+ if ($groupCount !== $maxGroups) {
+ $this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount();
+ }
+ return;
+ }
+
+ if ($colons !== strrpos($IPv6, '::')) {
+ $this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon();
+ return;
+ }
+
+ if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
+ // RFC 4291 allows :: at the start or end of an address
+ //with 7 other groups in addition
+ ++$maxGroups;
+ }
+
+ if ($groupCount > $maxGroups) {
+ $this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups();
+ } elseif ($groupCount === $maxGroups) {
+ $this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated();
+ }
+ }
+
+ public function convertIPv4ToIPv6(string $addressLiteralIPv4) : string
+ {
+ $matchesIP = array();
+ $IPv4Match = preg_match(
+ '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
+ $addressLiteralIPv4,
+ $matchesIP);
+
+ // Extract IPv4 part from the end of the address-literal (if there is one)
+ if ($IPv4Match > 0) {
+ $index = (int) strrpos($addressLiteralIPv4, $matchesIP[0]);
+ //There's a match but it is at the start
+ if ($index > 0) {
+ // Convert IPv4 part to IPv6 format for further testing
+ return substr($addressLiteralIPv4, 0, $index) . '0:0';
+ }
+ }
+
+ return $addressLiteralIPv4;
+ }
+
+ /**
+ * @param string $addressLiteral
+ *
+ * @return bool
+ */
+ protected function checkIPV4Tag($addressLiteral) : bool
+ {
+ $matchesIP = array();
+ $IPv4Match = preg_match(
+ '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
+ $addressLiteral,
+ $matchesIP);
+
+ // Extract IPv4 part from the end of the address-literal (if there is one)
+
+ if ($IPv4Match > 0) {
+ $index = strrpos($addressLiteral, $matchesIP[0]);
+ //There's a match but it is at the start
+ if ($index === 0) {
+ $this->warnings[AddressLiteral::CODE] = new AddressLiteral();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private function addObsoleteWarnings() : void
+ {
+ if ($this->lexer->token['type'] === EmailLexer::INVALID ||
+ $this->lexer->token['type'] === EmailLexer::C_DEL ||
+ $this->lexer->token['type'] === EmailLexer::S_LF ||
+ $this->lexer->token['type'] === EmailLexer::S_BACKSLASH
+ ) {
+ $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
+ }
+ }
+
+ private function addTagWarnings() : void
+ {
+ if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
+ $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
+ }
+ if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
+ $lexer = clone $this->lexer;
+ $lexer->moveNext();
+ if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
+ $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/DomainPart.php b/egulias/email-validator/src/Parser/DomainPart.php
index 4dadba8a..4ca54f2d 100644
--- a/egulias/email-validator/src/Parser/DomainPart.php
+++ b/egulias/email-validator/src/Parser/DomainPart.php
@@ -3,38 +3,28 @@
namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
-use Egulias\EmailValidator\Exception\CharNotAllowed;
-use Egulias\EmailValidator\Exception\CommaInDomain;
-use Egulias\EmailValidator\Exception\ConsecutiveAt;
-use Egulias\EmailValidator\Exception\CRLFAtTheEnd;
-use Egulias\EmailValidator\Exception\CRNoLF;
-use Egulias\EmailValidator\Exception\DomainHyphened;
-use Egulias\EmailValidator\Exception\DotAtEnd;
-use Egulias\EmailValidator\Exception\DotAtStart;
-use Egulias\EmailValidator\Exception\ExpectingATEXT;
-use Egulias\EmailValidator\Exception\ExpectingDomainLiteralClose;
-use Egulias\EmailValidator\Exception\ExpectingDTEXT;
-use Egulias\EmailValidator\Exception\NoDomainPart;
-use Egulias\EmailValidator\Exception\UnopenedComment;
-use Egulias\EmailValidator\Warning\AddressLiteral;
-use Egulias\EmailValidator\Warning\CFWSWithFWS;
-use Egulias\EmailValidator\Warning\DeprecatedComment;
-use Egulias\EmailValidator\Warning\DomainLiteral;
-use Egulias\EmailValidator\Warning\DomainTooLong;
-use Egulias\EmailValidator\Warning\IPV6BadChar;
-use Egulias\EmailValidator\Warning\IPV6ColonEnd;
-use Egulias\EmailValidator\Warning\IPV6ColonStart;
-use Egulias\EmailValidator\Warning\IPV6Deprecated;
-use Egulias\EmailValidator\Warning\IPV6DoubleColon;
-use Egulias\EmailValidator\Warning\IPV6GroupCount;
-use Egulias\EmailValidator\Warning\IPV6MaxGroups;
-use Egulias\EmailValidator\Warning\LabelTooLong;
-use Egulias\EmailValidator\Warning\ObsoleteDTEXT;
use Egulias\EmailValidator\Warning\TLD;
-
-class DomainPart extends Parser
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\DotAtEnd;
+use Egulias\EmailValidator\Result\Reason\DotAtStart;
+use Egulias\EmailValidator\Warning\DeprecatedComment;
+use Egulias\EmailValidator\Result\Reason\CRLFAtTheEnd;
+use Egulias\EmailValidator\Result\Reason\LabelTooLong;
+use Egulias\EmailValidator\Result\Reason\NoDomainPart;
+use Egulias\EmailValidator\Result\Reason\ConsecutiveAt;
+use Egulias\EmailValidator\Result\Reason\DomainTooLong;
+use Egulias\EmailValidator\Result\Reason\CharNotAllowed;
+use Egulias\EmailValidator\Result\Reason\DomainHyphened;
+use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
+use Egulias\EmailValidator\Parser\CommentStrategy\DomainComment;
+use Egulias\EmailValidator\Result\Reason\ExpectingDomainLiteralClose;
+use Egulias\EmailValidator\Parser\DomainLiteral as DomainLiteralParser;
+
+class DomainPart extends PartParser
{
- const DOMAIN_MAX_LENGTH = 254;
+ const DOMAIN_MAX_LENGTH = 253;
const LABEL_MAX_LENGTH = 63;
/**
@@ -42,402 +32,281 @@ class DomainPart extends Parser
*/
protected $domainPart = '';
- public function parse($domainPart)
+ /**
+ * @var string
+ */
+ protected $label = '';
+
+ public function parse() : Result
{
+ $this->lexer->clearRecorded();
+ $this->lexer->startRecording();
+
$this->lexer->moveNext();
- $this->performDomainStartChecks();
+ $domainChecks = $this->performDomainStartChecks();
+ if ($domainChecks->isInvalid()) {
+ return $domainChecks;
+ }
+
+ if ($this->lexer->token['type'] === EmailLexer::S_AT) {
+ return new InvalidEmail(new ConsecutiveAt(), $this->lexer->token['value']);
+ }
- $domain = $this->doParseDomainPart();
+ $result = $this->doParseDomainPart();
+ if ($result->isInvalid()) {
+ return $result;
+ }
- $prev = $this->lexer->getPrevious();
- $length = strlen($domain);
+ $end = $this->checkEndOfDomain();
+ if ($end->isInvalid()) {
+ return $end;
+ }
+
+ $this->lexer->stopRecording();
+ $this->domainPart = $this->lexer->getAccumulatedValues();
+
+ $length = strlen($this->domainPart);
+ if ($length > self::DOMAIN_MAX_LENGTH) {
+ return new InvalidEmail(new DomainTooLong(), $this->lexer->token['value']);
+ }
+
+ return new ValidEmail();
+ }
+ private function checkEndOfDomain() : Result
+ {
+ $prev = $this->lexer->getPrevious();
if ($prev['type'] === EmailLexer::S_DOT) {
- throw new DotAtEnd();
+ return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
}
if ($prev['type'] === EmailLexer::S_HYPHEN) {
- throw new DomainHyphened();
+ return new InvalidEmail(new DomainHyphened('Hypen found at the end of the domain'), $prev['value']);
}
- if ($length > self::DOMAIN_MAX_LENGTH) {
- $this->warnings[DomainTooLong::CODE] = new DomainTooLong();
- }
- if ($prev['type'] === EmailLexer::S_CR) {
- throw new CRLFAtTheEnd();
+
+ if ($this->lexer->token['type'] === EmailLexer::S_SP) {
+ return new InvalidEmail(new CRLFAtTheEnd(), $prev['value']);
}
- $this->domainPart = $domain;
+ return new ValidEmail();
+
}
- private function performDomainStartChecks()
+ private function performDomainStartChecks() : Result
{
- $this->checkInvalidTokensAfterAT();
- $this->checkEmptyDomain();
+ $invalidTokens = $this->checkInvalidTokensAfterAT();
+ if ($invalidTokens->isInvalid()) {
+ return $invalidTokens;
+ }
+
+ $missingDomain = $this->checkEmptyDomain();
+ if ($missingDomain->isInvalid()) {
+ return $missingDomain;
+ }
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->warnings[DeprecatedComment::CODE] = new DeprecatedComment();
- $this->parseDomainComments();
}
+ return new ValidEmail();
}
- private function checkEmptyDomain()
+ private function checkEmptyDomain() : Result
{
$thereIsNoDomain = $this->lexer->token['type'] === EmailLexer::S_EMPTY ||
($this->lexer->token['type'] === EmailLexer::S_SP &&
!$this->lexer->isNextToken(EmailLexer::GENERIC));
if ($thereIsNoDomain) {
- throw new NoDomainPart();
+ return new InvalidEmail(new NoDomainPart(), $this->lexer->token['value']);
}
+
+ return new ValidEmail();
}
- private function checkInvalidTokensAfterAT()
+ private function checkInvalidTokensAfterAT() : Result
{
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
- throw new DotAtStart();
+ return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
}
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
- throw new DomainHyphened();
+ return new InvalidEmail(new DomainHyphened('After AT'), $this->lexer->token['value']);
}
+ return new ValidEmail();
}
- /**
- * @return string
- */
- public function getDomainPart()
+ protected function parseComments(): Result
{
- return $this->domainPart;
- }
+ $commentParser = new Comment($this->lexer, new DomainComment());
+ $result = $commentParser->parse();
+ $this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
- /**
- * @param string $addressLiteral
- * @param int $maxGroups
- */
- public function checkIPV6Tag($addressLiteral, $maxGroups = 8)
- {
- $prev = $this->lexer->getPrevious();
- if ($prev['type'] === EmailLexer::S_COLON) {
- $this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd();
- }
-
- $IPv6 = substr($addressLiteral, 5);
- //Daniel Marschall's new IPv6 testing strategy
- $matchesIP = explode(':', $IPv6);
- $groupCount = count($matchesIP);
- $colons = strpos($IPv6, '::');
-
- if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
- $this->warnings[IPV6BadChar::CODE] = new IPV6BadChar();
- }
-
- if ($colons === false) {
- // We need exactly the right number of groups
- if ($groupCount !== $maxGroups) {
- $this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount();
- }
- return;
- }
-
- if ($colons !== strrpos($IPv6, '::')) {
- $this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon();
- return;
- }
-
- if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
- // RFC 4291 allows :: at the start or end of an address
- //with 7 other groups in addition
- ++$maxGroups;
- }
-
- if ($groupCount > $maxGroups) {
- $this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups();
- } elseif ($groupCount === $maxGroups) {
- $this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated();
- }
+ return $result;
}
- /**
- * @return string
- */
- protected function doParseDomainPart()
+ protected function doParseDomainPart() : Result
{
+ $tldMissing = true;
+ $hasComments = false;
$domain = '';
- $label = '';
- $openedParenthesis = 0;
do {
$prev = $this->lexer->getPrevious();
- $this->checkNotAllowedChars($this->lexer->token);
+ $notAllowedChars = $this->checkNotAllowedChars($this->lexer->token);
+ if ($notAllowedChars->isInvalid()) {
+ return $notAllowedChars;
+ }
+
+ if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS ||
+ $this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
+ $hasComments = true;
+ $commentsResult = $this->parseComments();
- if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
- $this->parseComments();
- $openedParenthesis += $this->getOpenedParenthesis();
- $this->lexer->moveNext();
- $tmpPrev = $this->lexer->getPrevious();
- if ($tmpPrev['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
- $openedParenthesis--;
+ //Invalid comment parsing
+ if($commentsResult->isInvalid()) {
+ return $commentsResult;
}
}
- if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
- if ($openedParenthesis === 0) {
- throw new UnopenedComment();
- } else {
- $openedParenthesis--;
- }
+
+ $dotsResult = $this->checkConsecutiveDots();
+ if ($dotsResult->isInvalid()) {
+ return $dotsResult;
}
- $this->checkConsecutiveDots();
- $this->checkDomainPartExceptions($prev);
+ if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET) {
+ $literalResult = $this->parseDomainLiteral();
- if ($this->hasBrackets()) {
- $this->parseDomainLiteral();
+ $this->addTLDWarnings($tldMissing);
+ return $literalResult;
}
- if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
- $this->checkLabelLength($label);
- $label = '';
- } else {
- $label .= $this->lexer->token['value'];
- }
+ $labelCheck = $this->checkLabelLength();
+ if ($labelCheck->isInvalid()) {
+ return $labelCheck;
+ }
- if ($this->isFWS()) {
- $this->parseFWS();
+ $FwsResult = $this->parseFWS();
+ if($FwsResult->isInvalid()) {
+ return $FwsResult;
}
$domain .= $this->lexer->token['value'];
- $this->lexer->moveNext();
- if ($this->lexer->token['type'] === EmailLexer::S_SP) {
- throw new CharNotAllowed();
+
+ if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
+ $tldMissing = false;
}
+
+ $exceptionsResult = $this->checkDomainPartExceptions($prev, $hasComments);
+ if ($exceptionsResult->isInvalid()) {
+ return $exceptionsResult;
+ }
+ $this->lexer->moveNext();
+
} while (null !== $this->lexer->token['type']);
- $this->checkLabelLength($label);
+ $labelCheck = $this->checkLabelLength(true);
+ if ($labelCheck->isInvalid()) {
+ return $labelCheck;
+ }
+ $this->addTLDWarnings($tldMissing);
- return $domain;
+ $this->domainPart = $domain;
+ return new ValidEmail();
}
- private function checkNotAllowedChars(array $token)
+ private function checkNotAllowedChars(array $token) : Result
{
$notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true];
if (isset($notAllowed[$token['type']])) {
- throw new CharNotAllowed();
+ return new InvalidEmail(new CharNotAllowed(), $token['value']);
}
+ return new ValidEmail();
}
/**
- * @return string|false
+ * @return Result
*/
- protected function parseDomainLiteral()
+ protected function parseDomainLiteral() : Result
{
- if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
- $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
- }
- if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
- $lexer = clone $this->lexer;
- $lexer->moveNext();
- if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
- $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
- }
+ try {
+ $this->lexer->find(EmailLexer::S_CLOSEBRACKET);
+ } catch (\RuntimeException $e) {
+ return new InvalidEmail(new ExpectingDomainLiteralClose(), $this->lexer->token['value']);
}
- return $this->doParseDomainLiteral();
+ $domainLiteralParser = new DomainLiteralParser($this->lexer);
+ $result = $domainLiteralParser->parse();
+ $this->warnings = array_merge($this->warnings, $domainLiteralParser->getWarnings());
+ return $result;
}
- /**
- * @return string|false
- */
- protected function doParseDomainLiteral()
+ protected function checkDomainPartExceptions(array $prev, bool $hasComments) : Result
{
- $IPv6TAG = false;
- $addressLiteral = '';
- do {
- if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
- throw new ExpectingDTEXT();
- }
-
- if ($this->lexer->token['type'] === EmailLexer::INVALID ||
- $this->lexer->token['type'] === EmailLexer::C_DEL ||
- $this->lexer->token['type'] === EmailLexer::S_LF
- ) {
- $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
- }
-
- if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) {
- throw new ExpectingDTEXT();
- }
-
- if ($this->lexer->isNextTokenAny(
- array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
- )) {
- $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
- $this->parseFWS();
- }
-
- if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
- throw new CRNoLF();
- }
-
- if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
- $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
- $addressLiteral .= $this->lexer->token['value'];
- $this->lexer->moveNext();
- $this->validateQuotedPair();
- }
- if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
- $IPv6TAG = true;
- }
- if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) {
- break;
- }
-
- $addressLiteral .= $this->lexer->token['value'];
-
- } while ($this->lexer->moveNext());
-
- $addressLiteral = str_replace('[', '', $addressLiteral);
- $addressLiteral = $this->checkIPV4Tag($addressLiteral);
-
- if (false === $addressLiteral) {
- return $addressLiteral;
+ if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET && $prev['type'] !== EmailLexer::S_AT) {
+ return new InvalidEmail(new ExpectingATEXT('OPENBRACKET not after AT'), $this->lexer->token['value']);
}
- if (!$IPv6TAG) {
- $this->warnings[DomainLiteral::CODE] = new DomainLiteral();
- return $addressLiteral;
+ if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
+ return new InvalidEmail(new DomainHyphened('Hypen found near DOT'), $this->lexer->token['value']);
}
- $this->warnings[AddressLiteral::CODE] = new AddressLiteral();
-
- $this->checkIPV6Tag($addressLiteral);
-
- return $addressLiteral;
- }
-
- /**
- * @param string $addressLiteral
- *
- * @return string|false
- */
- protected function checkIPV4Tag($addressLiteral)
- {
- $matchesIP = array();
-
- // Extract IPv4 part from the end of the address-literal (if there is one)
- if (preg_match(
- '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
- $addressLiteral,
- $matchesIP
- ) > 0
- ) {
- $index = strrpos($addressLiteral, $matchesIP[0]);
- if ($index === 0) {
- $this->warnings[AddressLiteral::CODE] = new AddressLiteral();
- return false;
- }
- // Convert IPv4 part to IPv6 format for further testing
- $addressLiteral = substr($addressLiteral, 0, (int) $index) . '0:0';
+ if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
+ && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
+ return new InvalidEmail(new ExpectingATEXT('Escaping following "ATOM"'), $this->lexer->token['value']);
}
- return $addressLiteral;
+ return $this->validateTokens($hasComments);
}
- protected function checkDomainPartExceptions(array $prev)
+ protected function validateTokens(bool $hasComments) : Result
{
- $invalidDomainTokens = array(
- EmailLexer::S_DQUOTE => true,
- EmailLexer::S_SQUOTE => true,
- EmailLexer::S_BACKTICK => true,
- EmailLexer::S_SEMICOLON => true,
- EmailLexer::S_GREATERTHAN => true,
- EmailLexer::S_LOWERTHAN => true,
+ $validDomainTokens = array(
+ EmailLexer::GENERIC => true,
+ EmailLexer::S_HYPHEN => true,
+ EmailLexer::S_DOT => true,
);
- if (isset($invalidDomainTokens[$this->lexer->token['type']])) {
- throw new ExpectingATEXT();
- }
-
- if ($this->lexer->token['type'] === EmailLexer::S_COMMA) {
- throw new CommaInDomain();
+ if ($hasComments) {
+ $validDomainTokens[EmailLexer::S_OPENPARENTHESIS] = true;
+ $validDomainTokens[EmailLexer::S_CLOSEPARENTHESIS] = true;
}
- if ($this->lexer->token['type'] === EmailLexer::S_AT) {
- throw new ConsecutiveAt();
- }
-
- if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) {
- throw new ExpectingATEXT();
+ if (!isset($validDomainTokens[$this->lexer->token['type']])) {
+ return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
}
- if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
- throw new DomainHyphened();
- }
-
- if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
- && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
- throw new ExpectingATEXT();
- }
+ return new ValidEmail();
}
- /**
- * @return bool
- */
- protected function hasBrackets()
+ private function checkLabelLength(bool $isEndOfDomain = false) : Result
{
- if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) {
- return false;
- }
-
- try {
- $this->lexer->find(EmailLexer::S_CLOSEBRACKET);
- } catch (\RuntimeException $e) {
- throw new ExpectingDomainLiteralClose();
+ if ($this->lexer->token['type'] === EmailLexer::S_DOT || $isEndOfDomain) {
+ if ($this->isLabelTooLong($this->label)) {
+ return new InvalidEmail(new LabelTooLong(), $this->lexer->token['value']);
+ }
+ $this->label = '';
}
-
- return true;
+ $this->label .= $this->lexer->token['value'];
+ return new ValidEmail();
}
- /**
- * @param string $label
- */
- protected function checkLabelLength($label)
- {
- if ($this->isLabelTooLong($label)) {
- $this->warnings[LabelTooLong::CODE] = new LabelTooLong();
- }
- }
- /**
- * @param string $label
- * @return bool
- */
- private function isLabelTooLong($label)
+ private function isLabelTooLong(string $label) : bool
{
if (preg_match('/[^\x00-\x7F]/', $label)) {
- idn_to_ascii($label, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46, $idnaInfo);
-
+ idn_to_ascii(utf8_decode($label), IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46, $idnaInfo);
return (bool) ($idnaInfo['errors'] & IDNA_ERROR_LABEL_TOO_LONG);
}
-
return strlen($label) > self::LABEL_MAX_LENGTH;
}
- protected function parseDomainComments()
+ private function addTLDWarnings(bool $isTLDMissing) : void
{
- $this->isUnclosedComment();
- while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
- $this->warnEscaping();
- $this->lexer->moveNext();
- }
-
- $this->lexer->moveNext();
- if ($this->lexer->isNextToken(EmailLexer::S_DOT)) {
- throw new ExpectingATEXT();
+ if ($isTLDMissing) {
+ $this->warnings[TLD::CODE] = new TLD();
}
}
- protected function addTLDWarnings()
+ public function domainPart() : string
{
- if ($this->warnings[DomainLiteral::CODE]) {
- $this->warnings[TLD::CODE] = new TLD();
- }
+ return $this->domainPart;
}
-}
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/DoubleQuote.php b/egulias/email-validator/src/Parser/DoubleQuote.php
new file mode 100644
index 00000000..19c098e8
--- /dev/null
+++ b/egulias/email-validator/src/Parser/DoubleQuote.php
@@ -0,0 +1,87 @@
+<?php
+namespace Egulias\EmailValidator\Parser;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Parser\Parser;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Warning\CFWSWithFWS;
+use Egulias\EmailValidator\Warning\QuotedString;
+use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
+use Egulias\EmailValidator\Result\Reason\UnclosedQuotedString;
+use Egulias\EmailValidator\Result\Result;
+
+class DoubleQuote extends PartParser
+{
+ public function parse() : Result
+ {
+
+ $validQuotedString = $this->checkDQUOTE();
+ if($validQuotedString->isInvalid()) return $validQuotedString;
+
+ $special = array(
+ EmailLexer::S_CR => true,
+ EmailLexer::S_HTAB => true,
+ EmailLexer::S_LF => true
+ );
+
+ $invalid = array(
+ EmailLexer::C_NUL => true,
+ EmailLexer::S_HTAB => true,
+ EmailLexer::S_CR => true,
+ EmailLexer::S_LF => true
+ );
+ $setSpecialsWarning = true;
+
+ $this->lexer->moveNext();
+
+ while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && null !== $this->lexer->token['type']) {
+ if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
+ $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
+ $setSpecialsWarning = false;
+ }
+ if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) {
+ $this->lexer->moveNext();
+ }
+
+ $this->lexer->moveNext();
+
+ if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
+ return new InvalidEmail(new ExpectingATEXT("Expecting ATEXT between DQUOTE"), $this->lexer->token['value']);
+ }
+ }
+
+ $prev = $this->lexer->getPrevious();
+
+ if ($prev['type'] === EmailLexer::S_BACKSLASH) {
+ $validQuotedString = $this->checkDQUOTE();
+ if($validQuotedString->isInvalid()) return $validQuotedString;
+ }
+
+ if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
+ return new InvalidEmail(new ExpectingATEXT("Expecting ATEXT between DQUOTE"), $this->lexer->token['value']);
+ }
+
+ return new ValidEmail();
+ }
+
+ protected function checkDQUOTE() : Result
+ {
+ $previous = $this->lexer->getPrevious();
+
+ if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
+ $description = 'https://tools.ietf.org/html/rfc5322#section-3.2.4 - quoted string should be a unit';
+ return new InvalidEmail(new ExpectingATEXT($description), $this->lexer->token['value']);
+ }
+
+ try {
+ $this->lexer->find(EmailLexer::S_DQUOTE);
+ } catch (\Exception $e) {
+ return new InvalidEmail(new UnclosedQuotedString(), $this->lexer->token['value']);
+ }
+ $this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']);
+
+ return new ValidEmail();
+ }
+
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/FoldingWhiteSpace.php b/egulias/email-validator/src/Parser/FoldingWhiteSpace.php
new file mode 100644
index 00000000..d32231e7
--- /dev/null
+++ b/egulias/email-validator/src/Parser/FoldingWhiteSpace.php
@@ -0,0 +1,82 @@
+<?php
+namespace Egulias\EmailValidator\Parser;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Warning\CFWSNearAt;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Warning\CFWSWithFWS;
+use Egulias\EmailValidator\Result\Reason\CRNoLF;
+use Egulias\EmailValidator\Result\Reason\AtextAfterCFWS;
+use Egulias\EmailValidator\Result\Reason\CRLFAtTheEnd;
+use Egulias\EmailValidator\Result\Reason\CRLFX2;
+use Egulias\EmailValidator\Result\Reason\ExpectingCTEXT;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+
+class FoldingWhiteSpace extends PartParser
+{
+ public function parse() : Result
+ {
+ if (!$this->isFWS()) {
+ return new ValidEmail();
+ }
+
+ $previous = $this->lexer->getPrevious();
+
+ $resultCRLF = $this->checkCRLFInFWS();
+ if ($resultCRLF->isInvalid()) {
+ return $resultCRLF;
+ }
+
+ if ($this->lexer->token['type'] === EmailLexer::S_CR) {
+ return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']);
+ }
+
+ if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
+ return new InvalidEmail(new AtextAfterCFWS(), $this->lexer->token['value']);
+ }
+
+ if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
+ return new InvalidEmail(new ExpectingCTEXT(), $this->lexer->token['value']);
+ }
+
+ if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
+ $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
+ } else {
+ $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
+ }
+
+ return new ValidEmail();
+ }
+
+ protected function checkCRLFInFWS() : Result
+ {
+ if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
+ return new ValidEmail();
+ }
+
+ if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
+ return new InvalidEmail(new CRLFX2(), $this->lexer->token['value']);
+ }
+
+ //this has no coverage. Condition is repeated from above one
+ if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
+ return new InvalidEmail(new CRLFAtTheEnd(), $this->lexer->token['value']);
+ }
+
+ return new ValidEmail();
+ }
+
+ protected function isFWS() : bool
+ {
+ if ($this->escaped()) {
+ return false;
+ }
+
+ return $this->lexer->token['type'] === EmailLexer::S_SP ||
+ $this->lexer->token['type'] === EmailLexer::S_HTAB ||
+ $this->lexer->token['type'] === EmailLexer::S_CR ||
+ $this->lexer->token['type'] === EmailLexer::S_LF ||
+ $this->lexer->token['type'] === EmailLexer::CRLF;
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/IDLeftPart.php b/egulias/email-validator/src/Parser/IDLeftPart.php
new file mode 100644
index 00000000..abb4982b
--- /dev/null
+++ b/egulias/email-validator/src/Parser/IDLeftPart.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Parser;
+
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Parser\LocalPart;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\CommentsInIDRight;
+
+class IDLeftPart extends LocalPart
+{
+ protected function parseComments(): Result
+ {
+ return new InvalidEmail(new CommentsInIDRight(), $this->lexer->token['value']);
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/IDRightPart.php b/egulias/email-validator/src/Parser/IDRightPart.php
new file mode 100644
index 00000000..bcf80dd0
--- /dev/null
+++ b/egulias/email-validator/src/Parser/IDRightPart.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Egulias\EmailValidator\Parser;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
+
+class IDRightPart extends DomainPart
+{
+ protected function validateTokens(bool $hasComments) : Result
+ {
+ $invalidDomainTokens = array(
+ EmailLexer::S_DQUOTE => true,
+ EmailLexer::S_SQUOTE => true,
+ EmailLexer::S_BACKTICK => true,
+ EmailLexer::S_SEMICOLON => true,
+ EmailLexer::S_GREATERTHAN => true,
+ EmailLexer::S_LOWERTHAN => true,
+ );
+
+ if (isset($invalidDomainTokens[$this->lexer->token['type']])) {
+ return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
+ }
+ return new ValidEmail();
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/LocalPart.php b/egulias/email-validator/src/Parser/LocalPart.php
index 3c21f34a..d505c618 100644
--- a/egulias/email-validator/src/Parser/LocalPart.php
+++ b/egulias/email-validator/src/Parser/LocalPart.php
@@ -2,144 +2,163 @@
namespace Egulias\EmailValidator\Parser;
-use Egulias\EmailValidator\Exception\DotAtEnd;
-use Egulias\EmailValidator\Exception\DotAtStart;
use Egulias\EmailValidator\EmailLexer;
-use Egulias\EmailValidator\Exception\ExpectingAT;
-use Egulias\EmailValidator\Exception\ExpectingATEXT;
-use Egulias\EmailValidator\Exception\UnclosedQuotedString;
-use Egulias\EmailValidator\Exception\UnopenedComment;
-use Egulias\EmailValidator\Warning\CFWSWithFWS;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Warning\LocalTooLong;
+use Egulias\EmailValidator\Result\Reason\DotAtEnd;
+use Egulias\EmailValidator\Result\Reason\DotAtStart;
+use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
+use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
+use Egulias\EmailValidator\Parser\CommentStrategy\LocalComment;
-class LocalPart extends Parser
+class LocalPart extends PartParser
{
- public function parse($localPart)
+ /**
+ * @var string
+ */
+ private $localPart = '';
+
+
+ public function parse() : Result
{
- $parseDQuote = true;
- $closingQuote = false;
- $openedParenthesis = 0;
- $totalLength = 0;
+ $this->lexer->startRecording();
while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) {
- if ($this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type']) {
- throw new DotAtStart();
+ if ($this->hasDotAtStart()) {
+ return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
}
- $closingQuote = $this->checkDQUOTE($closingQuote);
- if ($closingQuote && $parseDQuote) {
- $parseDQuote = $this->parseDoubleQuote();
- }
+ if ($this->lexer->token['type'] === EmailLexer::S_DQUOTE) {
+ $dquoteParsingResult = $this->parseDoubleQuote();
- if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
- $this->parseComments();
- $openedParenthesis += $this->getOpenedParenthesis();
+ //Invalid double quote parsing
+ if($dquoteParsingResult->isInvalid()) {
+ return $dquoteParsingResult;
+ }
}
- if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
- if ($openedParenthesis === 0) {
- throw new UnopenedComment();
- }
+ if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS ||
+ $this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
+ $commentsResult = $this->parseComments();
- $openedParenthesis--;
+ //Invalid comment parsing
+ if($commentsResult->isInvalid()) {
+ return $commentsResult;
+ }
}
- $this->checkConsecutiveDots();
+ if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
+ return new InvalidEmail(new ConsecutiveDot(), $this->lexer->token['value']);
+ }
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
$this->lexer->isNextToken(EmailLexer::S_AT)
) {
- throw new DotAtEnd();
+ return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
}
- $this->warnEscaping();
- $this->isInvalidToken($this->lexer->token, $closingQuote);
+ $resultEscaping = $this->validateEscaping();
+ if ($resultEscaping->isInvalid()) {
+ return $resultEscaping;
+ }
+
+ $resultToken = $this->validateTokens(false);
+ if ($resultToken->isInvalid()) {
+ return $resultToken;
+ }
- if ($this->isFWS()) {
- $this->parseFWS();
+ $resultFWS = $this->parseLocalFWS();
+ if($resultFWS->isInvalid()) {
+ return $resultFWS;
}
- $totalLength += strlen($this->lexer->token['value']);
$this->lexer->moveNext();
}
- if ($totalLength > LocalTooLong::LOCAL_PART_LENGTH) {
+ $this->lexer->stopRecording();
+ $this->localPart = rtrim($this->lexer->getAccumulatedValues(), '@');
+ if (strlen($this->localPart) > LocalTooLong::LOCAL_PART_LENGTH) {
$this->warnings[LocalTooLong::CODE] = new LocalTooLong();
}
+
+ return new ValidEmail();
}
- /**
- * @return bool
- */
- protected function parseDoubleQuote()
+ protected function validateTokens(bool $hasComments) : Result
{
- $parseAgain = true;
- $special = array(
- EmailLexer::S_CR => true,
- EmailLexer::S_HTAB => true,
- EmailLexer::S_LF => true
+ $invalidTokens = array(
+ EmailLexer::S_COMMA => EmailLexer::S_COMMA,
+ EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
+ EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
+ EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
+ EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
+ EmailLexer::S_COLON => EmailLexer::S_COLON,
+ EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
+ EmailLexer::INVALID => EmailLexer::INVALID
);
-
- $invalid = array(
- EmailLexer::C_NUL => true,
- EmailLexer::S_HTAB => true,
- EmailLexer::S_CR => true,
- EmailLexer::S_LF => true
- );
- $setSpecialsWarning = true;
-
- $this->lexer->moveNext();
-
- while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && null !== $this->lexer->token['type']) {
- $parseAgain = false;
- if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
- $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
- $setSpecialsWarning = false;
- }
- if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) {
- $this->lexer->moveNext();
- }
-
- $this->lexer->moveNext();
-
- if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
- throw new ExpectingATEXT();
- }
+ if (isset($invalidTokens[$this->lexer->token['type']])) {
+ return new InvalidEmail(new ExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
}
+ return new ValidEmail();
+ }
- $prev = $this->lexer->getPrevious();
+ public function localPart() : string
+ {
+ return $this->localPart;
+ }
- if ($prev['type'] === EmailLexer::S_BACKSLASH) {
- if (!$this->checkDQUOTE(false)) {
- throw new UnclosedQuotedString();
- }
+ private function parseLocalFWS() : Result
+ {
+ $foldingWS = new FoldingWhiteSpace($this->lexer);
+ $resultFWS = $foldingWS->parse();
+ if ($resultFWS->isValid()) {
+ $this->warnings = array_merge($this->warnings, $foldingWS->getWarnings());
}
+ return $resultFWS;
+ }
- if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
- throw new ExpectingAT();
- }
+ private function hasDotAtStart() : bool
+ {
+ return $this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type'];
+ }
+
+ private function parseDoubleQuote() : Result
+ {
+ $dquoteParser = new DoubleQuote($this->lexer);
+ $parseAgain = $dquoteParser->parse();
+ $this->warnings = array_merge($this->warnings, $dquoteParser->getWarnings());
return $parseAgain;
}
- /**
- * @param bool $closingQuote
- */
- protected function isInvalidToken(array $token, $closingQuote)
+ protected function parseComments(): Result
{
- $forbidden = array(
- EmailLexer::S_COMMA,
- EmailLexer::S_CLOSEBRACKET,
- EmailLexer::S_OPENBRACKET,
- EmailLexer::S_GREATERTHAN,
- EmailLexer::S_LOWERTHAN,
- EmailLexer::S_COLON,
- EmailLexer::S_SEMICOLON,
- EmailLexer::INVALID
- );
+ $commentParser = new Comment($this->lexer, new LocalComment());
+ $result = $commentParser->parse();
+ $this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
+ if($result->isInvalid()) {
+ return $result;
+ }
+ return $result;
+ }
- if (in_array($token['type'], $forbidden) && !$closingQuote) {
- throw new ExpectingATEXT();
+ private function validateEscaping() : Result
+ {
+ //Backslash found
+ if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
+ return new ValidEmail();
}
+
+ if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
+ return new InvalidEmail(new ExpectingATEXT('Found ATOM after escaping'), $this->lexer->token['value']);
+ }
+
+ if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
+ return new ValidEmail();
+ }
+
+ return new ValidEmail();
}
-}
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Parser/Parser.php b/egulias/email-validator/src/Parser/Parser.php
deleted file mode 100644
index ccdc9388..00000000
--- a/egulias/email-validator/src/Parser/Parser.php
+++ /dev/null
@@ -1,249 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Parser;
-
-use Egulias\EmailValidator\EmailLexer;
-use Egulias\EmailValidator\Exception\AtextAfterCFWS;
-use Egulias\EmailValidator\Exception\ConsecutiveDot;
-use Egulias\EmailValidator\Exception\CRLFAtTheEnd;
-use Egulias\EmailValidator\Exception\CRLFX2;
-use Egulias\EmailValidator\Exception\CRNoLF;
-use Egulias\EmailValidator\Exception\ExpectingQPair;
-use Egulias\EmailValidator\Exception\ExpectingATEXT;
-use Egulias\EmailValidator\Exception\ExpectingCTEXT;
-use Egulias\EmailValidator\Exception\UnclosedComment;
-use Egulias\EmailValidator\Exception\UnclosedQuotedString;
-use Egulias\EmailValidator\Warning\CFWSNearAt;
-use Egulias\EmailValidator\Warning\CFWSWithFWS;
-use Egulias\EmailValidator\Warning\Comment;
-use Egulias\EmailValidator\Warning\QuotedPart;
-use Egulias\EmailValidator\Warning\QuotedString;
-
-abstract class Parser
-{
- /**
- * @var array
- */
- protected $warnings = [];
-
- /**
- * @var EmailLexer
- */
- protected $lexer;
-
- /**
- * @var int
- */
- protected $openedParenthesis = 0;
-
- public function __construct(EmailLexer $lexer)
- {
- $this->lexer = $lexer;
- }
-
- /**
- * @return \Egulias\EmailValidator\Warning\Warning[]
- */
- public function getWarnings()
- {
- return $this->warnings;
- }
-
- /**
- * @param string $str
- */
- abstract public function parse($str);
-
- /** @return int */
- public function getOpenedParenthesis()
- {
- return $this->openedParenthesis;
- }
-
- /**
- * validateQuotedPair
- */
- protected function validateQuotedPair()
- {
- if (!($this->lexer->token['type'] === EmailLexer::INVALID
- || $this->lexer->token['type'] === EmailLexer::C_DEL)) {
- throw new ExpectingQPair();
- }
-
- $this->warnings[QuotedPart::CODE] =
- new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
- }
-
- protected function parseComments()
- {
- $this->openedParenthesis = 1;
- $this->isUnclosedComment();
- $this->warnings[Comment::CODE] = new Comment();
- while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
- if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
- $this->openedParenthesis++;
- }
- $this->warnEscaping();
- $this->lexer->moveNext();
- }
-
- $this->lexer->moveNext();
- if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) {
- throw new ExpectingATEXT();
- }
-
- if ($this->lexer->isNextToken(EmailLexer::S_AT)) {
- $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
- }
- }
-
- /**
- * @return bool
- */
- protected function isUnclosedComment()
- {
- try {
- $this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
- return true;
- } catch (\RuntimeException $e) {
- throw new UnclosedComment();
- }
- }
-
- protected function parseFWS()
- {
- $previous = $this->lexer->getPrevious();
-
- $this->checkCRLFInFWS();
-
- if ($this->lexer->token['type'] === EmailLexer::S_CR) {
- throw new CRNoLF();
- }
-
- if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
- throw new AtextAfterCFWS();
- }
-
- if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
- throw new ExpectingCTEXT();
- }
-
- if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
- $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
- } else {
- $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
- }
- }
-
- protected function checkConsecutiveDots()
- {
- if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
- throw new ConsecutiveDot();
- }
- }
-
- /**
- * @return bool
- */
- protected function isFWS()
- {
- if ($this->escaped()) {
- return false;
- }
-
- if ($this->lexer->token['type'] === EmailLexer::S_SP ||
- $this->lexer->token['type'] === EmailLexer::S_HTAB ||
- $this->lexer->token['type'] === EmailLexer::S_CR ||
- $this->lexer->token['type'] === EmailLexer::S_LF ||
- $this->lexer->token['type'] === EmailLexer::CRLF
- ) {
- return true;
- }
-
- return false;
- }
-
- /**
- * @return bool
- */
- protected function escaped()
- {
- $previous = $this->lexer->getPrevious();
-
- if ($previous && $previous['type'] === EmailLexer::S_BACKSLASH
- &&
- $this->lexer->token['type'] !== EmailLexer::GENERIC
- ) {
- return true;
- }
-
- return false;
- }
-
- /**
- * @return bool
- */
- protected function warnEscaping()
- {
- if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
- return false;
- }
-
- if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
- throw new ExpectingATEXT();
- }
-
- if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
- return false;
- }
-
- $this->warnings[QuotedPart::CODE] =
- new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
- return true;
-
- }
-
- /**
- * @param bool $hasClosingQuote
- *
- * @return bool
- */
- protected function checkDQUOTE($hasClosingQuote)
- {
- if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) {
- return $hasClosingQuote;
- }
- if ($hasClosingQuote) {
- return $hasClosingQuote;
- }
- $previous = $this->lexer->getPrevious();
- if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
- throw new ExpectingATEXT();
- }
-
- try {
- $this->lexer->find(EmailLexer::S_DQUOTE);
- $hasClosingQuote = true;
- } catch (\Exception $e) {
- throw new UnclosedQuotedString();
- }
- $this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']);
-
- return $hasClosingQuote;
- }
-
- protected function checkCRLFInFWS()
- {
- if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
- return;
- }
-
- if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
- throw new CRLFX2();
- }
-
- if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
- throw new CRLFAtTheEnd();
- }
- }
-}
diff --git a/egulias/email-validator/src/Parser/PartParser.php b/egulias/email-validator/src/Parser/PartParser.php
new file mode 100644
index 00000000..fd65fc56
--- /dev/null
+++ b/egulias/email-validator/src/Parser/PartParser.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Egulias\EmailValidator\Parser;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
+use Egulias\EmailValidator\Result\Result;
+use Egulias\EmailValidator\Result\ValidEmail;
+
+abstract class PartParser
+{
+ /**
+ * @var array
+ */
+ protected $warnings = [];
+
+ /**
+ * @var EmailLexer
+ */
+ protected $lexer;
+
+ public function __construct(EmailLexer $lexer)
+ {
+ $this->lexer = $lexer;
+ }
+
+ abstract public function parse() : Result;
+
+ /**
+ * @return \Egulias\EmailValidator\Warning\Warning[]
+ */
+ public function getWarnings()
+ {
+ return $this->warnings;
+ }
+
+ protected function parseFWS() : Result
+ {
+ $foldingWS = new FoldingWhiteSpace($this->lexer);
+ $resultFWS = $foldingWS->parse();
+ $this->warnings = array_merge($this->warnings, $foldingWS->getWarnings());
+ return $resultFWS;
+ }
+
+ protected function checkConsecutiveDots() : Result
+ {
+ if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
+ return new InvalidEmail(new ConsecutiveDot(), $this->lexer->token['value']);
+ }
+
+ return new ValidEmail();
+ }
+
+ protected function escaped() : bool
+ {
+ $previous = $this->lexer->getPrevious();
+
+ return $previous && $previous['type'] === EmailLexer::S_BACKSLASH
+ &&
+ $this->lexer->token['type'] !== EmailLexer::GENERIC;
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/InvalidEmail.php b/egulias/email-validator/src/Result/InvalidEmail.php
new file mode 100644
index 00000000..3d85e154
--- /dev/null
+++ b/egulias/email-validator/src/Result/InvalidEmail.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Egulias\EmailValidator\Result;
+
+use Egulias\EmailValidator\Result\Reason\Reason;
+
+class InvalidEmail implements Result
+{
+ private $token;
+ /**
+ * @var Reason
+ */
+ protected $reason;
+
+ public function __construct(Reason $reason, string $token)
+ {
+ $this->token = $token;
+ $this->reason = $reason;
+ }
+
+ public function isValid(): bool
+ {
+ return false;
+ }
+
+ public function isInvalid(): bool
+ {
+ return true;
+ }
+
+ public function description(): string
+ {
+ return $this->reason->description() . " in char " . $this->token;
+ }
+
+ public function code(): int
+ {
+ return $this->reason->code();
+ }
+
+ public function reason() : Reason
+ {
+ return $this->reason;
+ }
+
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/MultipleErrors.php b/egulias/email-validator/src/Result/MultipleErrors.php
new file mode 100644
index 00000000..24bf5ff7
--- /dev/null
+++ b/egulias/email-validator/src/Result/MultipleErrors.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Egulias\EmailValidator\Result;
+
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\Reason;
+
+/**
+ * @psalm-suppress PropertyNotSetInConstructor
+ */
+class MultipleErrors extends InvalidEmail
+{
+ /**
+ * @var Reason[]
+ */
+ private $reasons = [];
+
+ public function __construct()
+ {
+ }
+
+ public function addReason(Reason $reason) : void
+ {
+ $this->reasons[$reason->code()] = $reason;
+ }
+
+ /**
+ * @return Reason[]
+ */
+ public function getReasons() : array
+ {
+ return $this->reasons;
+ }
+
+ public function reason() : Reason
+ {
+ return $this->reasons[0];
+ }
+
+ public function description() : string
+ {
+ $description = '';
+ foreach($this->reasons as $reason) {
+ $description .= $reason->description() . PHP_EOL;
+ }
+
+ return $description;
+ }
+
+ public function code() : int
+ {
+ return 0;
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/AtextAfterCFWS.php b/egulias/email-validator/src/Result/Reason/AtextAfterCFWS.php
new file mode 100644
index 00000000..76015a2d
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/AtextAfterCFWS.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class AtextAfterCFWS implements Reason
+{
+ public function code() : int
+ {
+ return 133;
+ }
+
+ public function description() : string
+ {
+ return 'ATEXT found after CFWS';
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/CRLFAtTheEnd.php b/egulias/email-validator/src/Result/Reason/CRLFAtTheEnd.php
new file mode 100644
index 00000000..a0b66e71
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/CRLFAtTheEnd.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class CRLFAtTheEnd implements Reason
+{
+ const CODE = 149;
+ const REASON = "CRLF at the end";
+
+ public function code() : int
+ {
+ return 149;
+ }
+
+ public function description() : string
+ {
+ return 'CRLF at the end';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/CRLFX2.php b/egulias/email-validator/src/Result/Reason/CRLFX2.php
new file mode 100644
index 00000000..61235649
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/CRLFX2.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class CRLFX2 implements Reason
+{
+ public function code() : int
+ {
+ return 148;
+ }
+
+ public function description() : string
+ {
+ return 'CR LF tokens found twice';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/CRNoLF.php b/egulias/email-validator/src/Result/Reason/CRNoLF.php
new file mode 100644
index 00000000..e315ff0c
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/CRNoLF.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class CRNoLF implements Reason
+{
+ public function code() : int
+ {
+ return 150;
+ }
+
+ public function description() : string
+ {
+ return 'Missing LF after CR';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/CharNotAllowed.php b/egulias/email-validator/src/Result/Reason/CharNotAllowed.php
new file mode 100644
index 00000000..45999b36
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/CharNotAllowed.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class CharNotAllowed implements Reason
+{
+ public function code() : int
+ {
+ return 1;
+ }
+
+ public function description() : string
+ {
+ return "Character not allowed";
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/CommaInDomain.php b/egulias/email-validator/src/Result/Reason/CommaInDomain.php
new file mode 100644
index 00000000..93d3b561
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/CommaInDomain.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class CommaInDomain implements Reason
+{
+ public function code() : int
+ {
+ return 200;
+ }
+
+ public function description() : string
+ {
+ return "Comma ',' is not allowed in domain part";
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/CommentsInIDRight.php b/egulias/email-validator/src/Result/Reason/CommentsInIDRight.php
new file mode 100644
index 00000000..43a7cf7b
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/CommentsInIDRight.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class CommentsInIDRight implements Reason
+{
+ public function code() : int
+ {
+ return 400;
+ }
+
+ public function description() : string
+ {
+ return 'Comments are not allowed in IDRight for message-id';
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/ConsecutiveAt.php b/egulias/email-validator/src/Result/Reason/ConsecutiveAt.php
new file mode 100644
index 00000000..5128dc9e
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/ConsecutiveAt.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class ConsecutiveAt implements Reason
+{
+ public function code() : int
+ {
+ return 128;
+ }
+
+ public function description() : string
+ {
+ return '@ found after another @';
+ }
+
+}
diff --git a/egulias/email-validator/src/Result/Reason/ConsecutiveDot.php b/egulias/email-validator/src/Result/Reason/ConsecutiveDot.php
new file mode 100644
index 00000000..e7b65657
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/ConsecutiveDot.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class ConsecutiveDot implements Reason
+{
+ public function code() : int
+ {
+ return 132;
+ }
+
+ public function description() : string
+ {
+ return 'Concecutive DOT found';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/DetailedReason.php b/egulias/email-validator/src/Result/Reason/DetailedReason.php
new file mode 100644
index 00000000..17519873
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/DetailedReason.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+abstract class DetailedReason implements Reason
+{
+ protected $detailedDescription;
+
+ public function __construct(string $details)
+ {
+ $this->detailedDescription = $details;
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/DomainAcceptsNoMail.php b/egulias/email-validator/src/Result/Reason/DomainAcceptsNoMail.php
new file mode 100644
index 00000000..55f44bba
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/DomainAcceptsNoMail.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class DomainAcceptsNoMail implements Reason
+{
+ public function code() : int
+ {
+ return 154;
+ }
+
+ public function description() : string
+ {
+ return 'Domain accepts no mail (Null MX, RFC7505)';
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/DomainHyphened.php b/egulias/email-validator/src/Result/Reason/DomainHyphened.php
new file mode 100644
index 00000000..2944eb8e
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/DomainHyphened.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class DomainHyphened extends DetailedReason
+{
+ public function code() : int
+ {
+ return 144;
+ }
+
+ public function description() : string
+ {
+ return 'S_HYPHEN found in domain';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/DomainTooLong.php b/egulias/email-validator/src/Result/Reason/DomainTooLong.php
new file mode 100644
index 00000000..fa172132
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/DomainTooLong.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class DomainTooLong implements Reason
+{
+ public function code() : int
+ {
+ return 244;
+ }
+
+ public function description() : string
+ {
+ return 'Domain is longer than 253 characters';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/DotAtEnd.php b/egulias/email-validator/src/Result/Reason/DotAtEnd.php
new file mode 100644
index 00000000..6dfe6055
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/DotAtEnd.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class DotAtEnd implements Reason
+{
+ public function code() : int
+ {
+ return 142;
+ }
+
+ public function description() : string
+ {
+ return 'Dot at the end';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/DotAtStart.php b/egulias/email-validator/src/Result/Reason/DotAtStart.php
new file mode 100644
index 00000000..b564f1b5
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/DotAtStart.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class DotAtStart implements Reason
+{
+ public function code() : int
+ {
+ return 141;
+ }
+
+ public function description() : string
+ {
+ return "Starts with a DOT";
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/ExceptionFound.php b/egulias/email-validator/src/Result/Reason/ExceptionFound.php
new file mode 100644
index 00000000..8b1135d0
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/ExceptionFound.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class ExceptionFound implements Reason
+{
+ /**
+ * @var \Exception
+ */
+ private $exception;
+
+ public function __construct(\Exception $exception)
+ {
+ $this->exception = $exception;
+
+ }
+ public function code() : int
+ {
+ return 999;
+ }
+
+ public function description() : string
+ {
+ return $this->exception->getMessage();
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/ExpectingATEXT.php b/egulias/email-validator/src/Result/Reason/ExpectingATEXT.php
new file mode 100644
index 00000000..07ea8d23
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/ExpectingATEXT.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class ExpectingATEXT extends DetailedReason
+{
+ public function code() : int
+ {
+ return 137;
+ }
+
+ public function description() : string
+ {
+ return "Expecting ATEXT (Printable US-ASCII). Extended: " . $this->detailedDescription;
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/ExpectingCTEXT.php b/egulias/email-validator/src/Result/Reason/ExpectingCTEXT.php
new file mode 100644
index 00000000..64f5f7c3
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/ExpectingCTEXT.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class ExpectingCTEXT implements Reason
+{
+ public function code() : int
+ {
+ return 139;
+ }
+
+ public function description() : string
+ {
+ return 'Expecting CTEXT';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/ExpectingDTEXT.php b/egulias/email-validator/src/Result/Reason/ExpectingDTEXT.php
new file mode 100644
index 00000000..e47c251b
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/ExpectingDTEXT.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class ExpectingDTEXT implements Reason
+{
+ public function code() : int
+ {
+ return 129;
+ }
+
+ public function description() : string
+ {
+ return 'Expecting DTEXT';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/ExpectingDomainLiteralClose.php b/egulias/email-validator/src/Result/Reason/ExpectingDomainLiteralClose.php
new file mode 100644
index 00000000..7deffcaf
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/ExpectingDomainLiteralClose.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class ExpectingDomainLiteralClose implements Reason
+{
+ public function code() : int
+ {
+ return 137;
+ }
+
+ public function description() : string
+ {
+ return "Closing bracket ']' for domain literal not found";
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/LabelTooLong.php b/egulias/email-validator/src/Result/Reason/LabelTooLong.php
new file mode 100644
index 00000000..e181ef92
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/LabelTooLong.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class LabelTooLong implements Reason
+{
+ public function code() : int
+ {
+ return 245;
+ }
+
+ public function description() : string
+ {
+ return 'Domain "label" is longer than 63 characters';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/LocalOrReservedDomain.php b/egulias/email-validator/src/Result/Reason/LocalOrReservedDomain.php
new file mode 100644
index 00000000..bc7c5d5a
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/LocalOrReservedDomain.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class LocalOrReservedDomain implements Reason
+{
+ public function code() : int
+ {
+ return 153;
+ }
+
+ public function description() : string
+ {
+ return 'Local, mDNS or reserved domain (RFC2606, RFC6762)';
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/NoDNSRecord.php b/egulias/email-validator/src/Result/Reason/NoDNSRecord.php
new file mode 100644
index 00000000..e217d02c
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/NoDNSRecord.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class NoDNSRecord implements Reason
+{
+ public function code() : int
+ {
+ return 5;
+ }
+
+ public function description() : string
+ {
+ return 'No MX or A DSN record was found for this email';
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/NoDomainPart.php b/egulias/email-validator/src/Result/Reason/NoDomainPart.php
new file mode 100644
index 00000000..bbbb04be
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/NoDomainPart.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class NoDomainPart implements Reason
+{
+ public function code() : int
+ {
+ return 131;
+ }
+
+ public function description() : string
+ {
+ return 'No domain part found';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/NoLocalPart.php b/egulias/email-validator/src/Result/Reason/NoLocalPart.php
new file mode 100644
index 00000000..984c0619
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/NoLocalPart.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class NoLocalPart implements Reason
+{
+ public function code() : int
+ {
+ return 130;
+ }
+
+ public function description() : string
+ {
+ return "No local part";
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/RFCWarnings.php b/egulias/email-validator/src/Result/Reason/RFCWarnings.php
new file mode 100644
index 00000000..e6ff29a3
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/RFCWarnings.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class RFCWarnings implements Reason
+{
+ public function code() : int
+ {
+ return 997;
+ }
+
+ public function description() : string
+ {
+ return 'Warnings found after validating';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/Reason.php b/egulias/email-validator/src/Result/Reason/Reason.php
new file mode 100644
index 00000000..e6810b93
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/Reason.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+interface Reason
+{
+ /**
+ * Code for user land to act upon;
+ */
+ public function code() : int;
+
+ /**
+ * Short description of the result, human readable.
+ */
+ public function description() : string;
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/SpoofEmail.php b/egulias/email-validator/src/Result/Reason/SpoofEmail.php
new file mode 100644
index 00000000..da669cc8
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/SpoofEmail.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class SpoofEmail implements Reason
+{
+ public function code() : int
+ {
+ return 298;
+ }
+
+ public function description() : string
+ {
+ return 'The email contains mixed UTF8 chars that makes it suspicious';
+ }
+
+}
diff --git a/egulias/email-validator/src/Result/Reason/UnOpenedComment.php b/egulias/email-validator/src/Result/Reason/UnOpenedComment.php
new file mode 100644
index 00000000..cc7915ca
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/UnOpenedComment.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class UnOpenedComment implements Reason
+{
+ public function code() : int
+ {
+ return 152;
+ }
+
+ public function description(): string
+ {
+ return 'Missing openning comment parentheses - https://tools.ietf.org/html/rfc5322#section-3.2.2';
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Reason/UnableToGetDNSRecord.php b/egulias/email-validator/src/Result/Reason/UnableToGetDNSRecord.php
new file mode 100644
index 00000000..f178b1a0
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/UnableToGetDNSRecord.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+/**
+ * Used on SERVFAIL, TIMEOUT or other runtime and network errors
+ */
+class UnableToGetDNSRecord extends NoDNSRecord
+{
+ public function code() : int
+ {
+ return 3;
+ }
+
+ public function description() : string
+ {
+ return 'Unable to get DNS records for the host';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/UnclosedComment.php b/egulias/email-validator/src/Result/Reason/UnclosedComment.php
new file mode 100644
index 00000000..4ac41a0b
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/UnclosedComment.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class UnclosedComment implements Reason
+{
+ public function code() : int
+ {
+ return 146;
+ }
+
+ public function description(): string
+ {
+ return 'No closing comment token found';
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/UnclosedQuotedString.php b/egulias/email-validator/src/Result/Reason/UnclosedQuotedString.php
new file mode 100644
index 00000000..f42c4225
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/UnclosedQuotedString.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class UnclosedQuotedString implements Reason
+{
+ public function code() : int
+ {
+ return 145;
+ }
+
+ public function description() : string
+ {
+ return "Unclosed quoted string";
+ }
+}
diff --git a/egulias/email-validator/src/Result/Reason/UnusualElements.php b/egulias/email-validator/src/Result/Reason/UnusualElements.php
new file mode 100644
index 00000000..03873dc0
--- /dev/null
+++ b/egulias/email-validator/src/Result/Reason/UnusualElements.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Egulias\EmailValidator\Result\Reason;
+
+class UnusualElements implements Reason
+{
+ /**
+ * @var string $element
+ */
+ private $element = '';
+
+ public function __construct(string $element)
+ {
+ $this->element = $element;
+ }
+
+ public function code() : int
+ {
+ return 201;
+ }
+
+ public function description() : string
+ {
+ return 'Unusual element found, wourld render invalid in majority of cases. Element found: ' . $this->element;
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/Result.php b/egulias/email-validator/src/Result/Result.php
new file mode 100644
index 00000000..1e16bcca
--- /dev/null
+++ b/egulias/email-validator/src/Result/Result.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Egulias\EmailValidator\Result;
+
+interface Result
+{
+ /**
+ * Is validation result valid?
+ */
+ public function isValid() : bool;
+
+ /**
+ * Is validation result invalid?
+ * Usually the inverse of isValid()
+ */
+ public function isInvalid() : bool;
+
+ /**
+ * Short description of the result, human readable.
+ */
+ public function description() : string;
+
+ /**
+ * Code for user land to act upon.
+ */
+ public function code() : int;
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/SpoofEmail.php b/egulias/email-validator/src/Result/SpoofEmail.php
new file mode 100644
index 00000000..9f010de1
--- /dev/null
+++ b/egulias/email-validator/src/Result/SpoofEmail.php
@@ -0,0 +1,14 @@
+<?php
+namespace Egulias\EmailValidator\Result;
+
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\SpoofEmail as ReasonSpoofEmail;
+
+class SpoofEmail extends InvalidEmail
+{
+ public function __construct()
+ {
+ $this->reason = new ReasonSpoofEmail();
+ parent::__construct($this->reason, '');
+ }
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Result/ValidEmail.php b/egulias/email-validator/src/Result/ValidEmail.php
new file mode 100644
index 00000000..4f3693a9
--- /dev/null
+++ b/egulias/email-validator/src/Result/ValidEmail.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Egulias\EmailValidator\Result;
+
+class ValidEmail implements Result
+{
+ public function isValid(): bool
+ {
+ return true;
+ }
+
+ public function isInvalid(): bool
+ {
+ return false;
+ }
+
+ public function description(): string
+ {
+ return "Valid email";
+ }
+
+ public function code(): int
+ {
+ return 0;
+ }
+
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Validation/DNSCheckValidation.php b/egulias/email-validator/src/Validation/DNSCheckValidation.php
index 491082a5..a9357278 100644
--- a/egulias/email-validator/src/Validation/DNSCheckValidation.php
+++ b/egulias/email-validator/src/Validation/DNSCheckValidation.php
@@ -3,15 +3,21 @@
namespace Egulias\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
-use Egulias\EmailValidator\Exception\InvalidEmail;
-use Egulias\EmailValidator\Exception\LocalOrReservedDomain;
-use Egulias\EmailValidator\Exception\DomainAcceptsNoMail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\DomainAcceptsNoMail;
+use Egulias\EmailValidator\Result\Reason\LocalOrReservedDomain;
+use Egulias\EmailValidator\Result\Reason\NoDNSRecord as ReasonNoDNSRecord;
+use Egulias\EmailValidator\Result\Reason\UnableToGetDNSRecord;
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
-use Egulias\EmailValidator\Exception\NoDNSRecord;
class DNSCheckValidation implements EmailValidation
{
/**
+ * @var int
+ */
+ protected const DNS_RECORD_TYPES_TO_CHECK = DNS_MX + DNS_A + DNS_AAAA;
+
+ /**
* @var array
*/
private $warnings = [];
@@ -34,7 +40,7 @@ class DNSCheckValidation implements EmailValidation
}
}
- public function isValid($email, EmailLexer $emailLexer)
+ public function isValid(string $email, EmailLexer $emailLexer) : bool
{
// use the input to check DNS if we cannot extract something similar to a domain
$host = $email;
@@ -73,19 +79,19 @@ class DNSCheckValidation implements EmailValidation
// Exclude reserved top level DNS names
if ($isLocalDomain || $isReservedTopLevel) {
- $this->error = new LocalOrReservedDomain();
+ $this->error = new InvalidEmail(new LocalOrReservedDomain(), $host);
return false;
}
return $this->checkDns($host);
}
- public function getError()
+ public function getError() : ?InvalidEmail
{
return $this->error;
}
- public function getWarnings()
+ public function getWarnings() : array
{
return $this->warnings;
}
@@ -112,31 +118,43 @@ class DNSCheckValidation implements EmailValidation
*
* @return bool True on success.
*/
- private function validateDnsRecords($host)
+ private function validateDnsRecords($host) : bool
{
- // Get all MX, A and AAAA DNS records for host
- // Using @ as workaround to fix https://bugs.php.net/bug.php?id=73149
- $dnsRecords = @dns_get_record($host, DNS_MX + DNS_A + DNS_AAAA);
+ // A workaround to fix https://bugs.php.net/bug.php?id=73149
+ /** @psalm-suppress InvalidArgument */
+ set_error_handler(
+ static function (int $errorLevel, string $errorMessage): ?bool {
+ throw new \RuntimeException("Unable to get DNS record for the host: $errorMessage");
+ }
+ );
+
+ try {
+ // Get all MX, A and AAAA DNS records for host
+ $dnsRecords = dns_get_record($host, static::DNS_RECORD_TYPES_TO_CHECK);
+ } catch (\RuntimeException $exception) {
+ $this->error = new InvalidEmail(new UnableToGetDNSRecord(), '');
+ return false;
+ } finally {
+ restore_error_handler();
+ }
// No MX, A or AAAA DNS records
- if (empty($dnsRecords)) {
- $this->error = new NoDNSRecord();
+ if ($dnsRecords === [] || $dnsRecords === false) {
+ $this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
return false;
}
// For each DNS record
foreach ($dnsRecords as $dnsRecord) {
if (!$this->validateMXRecord($dnsRecord)) {
+ // No MX records (fallback to A or AAAA records)
+ if (empty($this->mxRecords)) {
+ $this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
+ }
return false;
}
}
-
- // No MX records (fallback to A or AAAA records)
- if (empty($this->mxRecords)) {
- $this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
- }
-
return true;
}
@@ -147,7 +165,7 @@ class DNSCheckValidation implements EmailValidation
*
* @return bool True if valid.
*/
- private function validateMxRecord($dnsRecord)
+ private function validateMxRecord($dnsRecord) : bool
{
if ($dnsRecord['type'] !== 'MX') {
return true;
@@ -155,7 +173,7 @@ class DNSCheckValidation implements EmailValidation
// "Null MX" record indicates the domain accepts no mail (https://tools.ietf.org/html/rfc7505)
if (empty($dnsRecord['target']) || $dnsRecord['target'] === '.') {
- $this->error = new DomainAcceptsNoMail();
+ $this->error = new InvalidEmail(new DomainAcceptsNoMail(), "");
return false;
}
@@ -163,4 +181,4 @@ class DNSCheckValidation implements EmailValidation
return true;
}
-}
+} \ No newline at end of file
diff --git a/egulias/email-validator/src/Validation/EmailValidation.php b/egulias/email-validator/src/Validation/EmailValidation.php
index d5a015be..1bcc0a70 100644
--- a/egulias/email-validator/src/Validation/EmailValidation.php
+++ b/egulias/email-validator/src/Validation/EmailValidation.php
@@ -3,7 +3,7 @@
namespace Egulias\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
-use Egulias\EmailValidator\Exception\InvalidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Warning\Warning;
interface EmailValidation
@@ -16,19 +16,19 @@ interface EmailValidation
*
* @return bool
*/
- public function isValid($email, EmailLexer $emailLexer);
+ public function isValid(string $email, EmailLexer $emailLexer) : bool;
/**
* Returns the validation error.
*
* @return InvalidEmail|null
*/
- public function getError();
+ public function getError() : ?InvalidEmail;
/**
* Returns the validation warnings.
*
* @return Warning[]
*/
- public function getWarnings();
+ public function getWarnings() : array;
}
diff --git a/egulias/email-validator/src/Validation/Error/RFCWarnings.php b/egulias/email-validator/src/Validation/Error/RFCWarnings.php
deleted file mode 100644
index 7f2256d6..00000000
--- a/egulias/email-validator/src/Validation/Error/RFCWarnings.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Validation\Error;
-
-use Egulias\EmailValidator\Exception\InvalidEmail;
-
-class RFCWarnings extends InvalidEmail
-{
- const CODE = 997;
- const REASON = 'Warnings were found.';
-}
diff --git a/egulias/email-validator/src/Validation/Error/SpoofEmail.php b/egulias/email-validator/src/Validation/Error/SpoofEmail.php
deleted file mode 100644
index 8c92cb5a..00000000
--- a/egulias/email-validator/src/Validation/Error/SpoofEmail.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Validation\Error;
-
-use Egulias\EmailValidator\Exception\InvalidEmail;
-
-class SpoofEmail extends InvalidEmail
-{
- const CODE = 998;
- const REASON = "The email contains mixed UTF8 chars that makes it suspicious";
-}
diff --git a/egulias/email-validator/src/Validation/SpoofCheckValidation.php b/egulias/email-validator/src/Validation/Extra/SpoofCheckValidation.php
index e10bfabd..4972dbce 100644
--- a/egulias/email-validator/src/Validation/SpoofCheckValidation.php
+++ b/egulias/email-validator/src/Validation/Extra/SpoofCheckValidation.php
@@ -1,11 +1,12 @@
<?php
-namespace Egulias\EmailValidator\Validation;
+namespace Egulias\EmailValidator\Validation\Extra;
-use Egulias\EmailValidator\EmailLexer;
-use Egulias\EmailValidator\Exception\InvalidEmail;
-use Egulias\EmailValidator\Validation\Error\SpoofEmail;
use \Spoofchecker;
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\SpoofEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Validation\EmailValidation;
class SpoofCheckValidation implements EmailValidation
{
@@ -24,7 +25,7 @@ class SpoofCheckValidation implements EmailValidation
/**
* @psalm-suppress InvalidArgument
*/
- public function isValid($email, EmailLexer $emailLexer)
+ public function isValid(string $email, EmailLexer $emailLexer) : bool
{
$checker = new Spoofchecker();
$checker->setChecks(Spoofchecker::SINGLE_SCRIPT);
@@ -37,14 +38,14 @@ class SpoofCheckValidation implements EmailValidation
}
/**
- * @return InvalidEmail|null
+ * @return InvalidEmail
*/
- public function getError()
+ public function getError() : ?InvalidEmail
{
return $this->error;
}
- public function getWarnings()
+ public function getWarnings() : array
{
return [];
}
diff --git a/egulias/email-validator/src/Validation/MessageIDValidation.php b/egulias/email-validator/src/Validation/MessageIDValidation.php
new file mode 100644
index 00000000..0e020433
--- /dev/null
+++ b/egulias/email-validator/src/Validation/MessageIDValidation.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Egulias\EmailValidator\Validation;
+
+use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\MessageIDParser;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\ExceptionFound;
+
+class MessageIDValidation implements EmailValidation
+{
+
+ /**
+ * @var array
+ */
+ private $warnings = [];
+
+ /**
+ * @var ?InvalidEmail
+ */
+ private $error;
+
+ public function isValid(string $email, EmailLexer $emailLexer): bool
+ {
+ $parser = new MessageIDParser($emailLexer);
+ try {
+ $result = $parser->parse($email);
+ $this->warnings = $parser->getWarnings();
+ if ($result->isInvalid()) {
+ /** @psalm-suppress PropertyTypeCoercion */
+ $this->error = $result;
+ return false;
+ }
+ } catch (\Exception $invalid) {
+ $this->error = new InvalidEmail(new ExceptionFound($invalid), '');
+ return false;
+ }
+
+ return true;
+ }
+
+ public function getWarnings(): array
+ {
+ return $this->warnings;
+ }
+
+ public function getError(): ?InvalidEmail
+ {
+ return $this->error;
+ }
+}
diff --git a/egulias/email-validator/src/Validation/MultipleErrors.php b/egulias/email-validator/src/Validation/MultipleErrors.php
deleted file mode 100644
index 3be59732..00000000
--- a/egulias/email-validator/src/Validation/MultipleErrors.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Validation;
-
-use Egulias\EmailValidator\Exception\InvalidEmail;
-
-class MultipleErrors extends InvalidEmail
-{
- const CODE = 999;
- const REASON = "Accumulated errors for multiple validations";
- /**
- * @var InvalidEmail[]
- */
- private $errors = [];
-
- /**
- * @param InvalidEmail[] $errors
- */
- public function __construct(array $errors)
- {
- $this->errors = $errors;
- parent::__construct();
- }
-
- /**
- * @return InvalidEmail[]
- */
- public function getErrors()
- {
- return $this->errors;
- }
-}
diff --git a/egulias/email-validator/src/Validation/MultipleValidationWithAnd.php b/egulias/email-validator/src/Validation/MultipleValidationWithAnd.php
index feb22402..6debf22f 100644
--- a/egulias/email-validator/src/Validation/MultipleValidationWithAnd.php
+++ b/egulias/email-validator/src/Validation/MultipleValidationWithAnd.php
@@ -3,13 +3,15 @@
namespace Egulias\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
+use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Validation\Exception\EmptyValidationList;
+use Egulias\EmailValidator\Result\MultipleErrors;
class MultipleValidationWithAnd implements EmailValidation
{
/**
- * If one of validations gets failure skips all succeeding validation.
- * This means MultipleErrors will only contain a single error which first found.
+ * If one of validations fails, the remaining validations will be skept.
+ * This means MultipleErrors will only contain a single error, the first found.
*/
const STOP_ON_ERROR = 0;
@@ -56,60 +58,51 @@ class MultipleValidationWithAnd implements EmailValidation
/**
* {@inheritdoc}
*/
- public function isValid($email, EmailLexer $emailLexer)
+ public function isValid(string $email, EmailLexer $emailLexer) : bool
{
$result = true;
- $errors = [];
foreach ($this->validations as $validation) {
$emailLexer->reset();
$validationResult = $validation->isValid($email, $emailLexer);
$result = $result && $validationResult;
$this->warnings = array_merge($this->warnings, $validation->getWarnings());
- $errors = $this->addNewError($validation->getError(), $errors);
+ if (!$validationResult) {
+ $this->processError($validation);
+ }
if ($this->shouldStop($result)) {
break;
}
}
- if (!empty($errors)) {
- $this->error = new MultipleErrors($errors);
- }
-
return $result;
}
- /**
- * @param \Egulias\EmailValidator\Exception\InvalidEmail|null $possibleError
- * @param \Egulias\EmailValidator\Exception\InvalidEmail[] $errors
- *
- * @return \Egulias\EmailValidator\Exception\InvalidEmail[]
- */
- private function addNewError($possibleError, array $errors)
+ private function initErrorStorage() : void
{
- if (null !== $possibleError) {
- $errors[] = $possibleError;
+ if (null === $this->error) {
+ $this->error = new MultipleErrors();
}
+ }
- return $errors;
+ private function processError(EmailValidation $validation) : void
+ {
+ if (null !== $validation->getError()) {
+ $this->initErrorStorage();
+ /** @psalm-suppress PossiblyNullReference */
+ $this->error->addReason($validation->getError()->reason());
+ }
}
- /**
- * @param bool $result
- *
- * @return bool
- */
- private function shouldStop($result)
+ private function shouldStop(bool $result) : bool
{
return !$result && $this->mode === self::STOP_ON_ERROR;
}
/**
* Returns the validation errors.
- *
- * @return MultipleErrors|null
*/
- public function getError()
+ public function getError() : ?InvalidEmail
{
return $this->error;
}
@@ -117,7 +110,7 @@ class MultipleValidationWithAnd implements EmailValidation
/**
* {@inheritdoc}
*/
- public function getWarnings()
+ public function getWarnings() : array
{
return $this->warnings;
}
diff --git a/egulias/email-validator/src/Validation/NoRFCWarningsValidation.php b/egulias/email-validator/src/Validation/NoRFCWarningsValidation.php
index 6b31e544..06885ed7 100644
--- a/egulias/email-validator/src/Validation/NoRFCWarningsValidation.php
+++ b/egulias/email-validator/src/Validation/NoRFCWarningsValidation.php
@@ -3,8 +3,8 @@
namespace Egulias\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
-use Egulias\EmailValidator\Exception\InvalidEmail;
-use Egulias\EmailValidator\Validation\Error\RFCWarnings;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\RFCWarnings;
class NoRFCWarningsValidation extends RFCValidation
{
@@ -16,7 +16,7 @@ class NoRFCWarningsValidation extends RFCValidation
/**
* {@inheritdoc}
*/
- public function isValid($email, EmailLexer $emailLexer)
+ public function isValid(string $email, EmailLexer $emailLexer) : bool
{
if (!parent::isValid($email, $emailLexer)) {
return false;
@@ -26,7 +26,7 @@ class NoRFCWarningsValidation extends RFCValidation
return true;
}
- $this->error = new RFCWarnings();
+ $this->error = new InvalidEmail(new RFCWarnings(), '');
return false;
}
@@ -34,7 +34,7 @@ class NoRFCWarningsValidation extends RFCValidation
/**
* {@inheritdoc}
*/
- public function getError()
+ public function getError() : ?InvalidEmail
{
return $this->error ?: parent::getError();
}
diff --git a/egulias/email-validator/src/Validation/RFCValidation.php b/egulias/email-validator/src/Validation/RFCValidation.php
index 8781e0b6..e2c27bac 100644
--- a/egulias/email-validator/src/Validation/RFCValidation.php
+++ b/egulias/email-validator/src/Validation/RFCValidation.php
@@ -4,7 +4,8 @@ namespace Egulias\EmailValidator\Validation;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\EmailParser;
-use Egulias\EmailValidator\Exception\InvalidEmail;
+use Egulias\EmailValidator\Result\InvalidEmail;
+use Egulias\EmailValidator\Result\Reason\ExceptionFound;
class RFCValidation implements EmailValidation
{
@@ -19,30 +20,35 @@ class RFCValidation implements EmailValidation
private $warnings = [];
/**
- * @var InvalidEmail|null
+ * @var ?InvalidEmail
*/
private $error;
- public function isValid($email, EmailLexer $emailLexer)
+ public function isValid(string $email, EmailLexer $emailLexer) : bool
{
$this->parser = new EmailParser($emailLexer);
try {
- $this->parser->parse((string)$email);
- } catch (InvalidEmail $invalid) {
- $this->error = $invalid;
+ $result = $this->parser->parse($email);
+ $this->warnings = $this->parser->getWarnings();
+ if ($result->isInvalid()) {
+ /** @psalm-suppress PropertyTypeCoercion */
+ $this->error = $result;
+ return false;
+ }
+ } catch (\Exception $invalid) {
+ $this->error = new InvalidEmail(new ExceptionFound($invalid), '');
return false;
}
- $this->warnings = $this->parser->getWarnings();
return true;
}
- public function getError()
+ public function getError() : ?InvalidEmail
{
return $this->error;
}
- public function getWarnings()
+ public function getWarnings() : array
{
return $this->warnings;
}
diff --git a/egulias/email-validator/src/Warning/DomainTooLong.php b/egulias/email-validator/src/Warning/DomainTooLong.php
deleted file mode 100644
index 61ff17a7..00000000
--- a/egulias/email-validator/src/Warning/DomainTooLong.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Warning;
-
-class DomainTooLong extends Warning
-{
- const CODE = 255;
-
- public function __construct()
- {
- $this->message = 'Domain is too long, exceeds 255 chars';
- $this->rfcNumber = 5322;
- }
-}
diff --git a/egulias/email-validator/src/Warning/LabelTooLong.php b/egulias/email-validator/src/Warning/LabelTooLong.php
deleted file mode 100644
index daf07f40..00000000
--- a/egulias/email-validator/src/Warning/LabelTooLong.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Egulias\EmailValidator\Warning;
-
-class LabelTooLong extends Warning
-{
- const CODE = 63;
-
- public function __construct()
- {
- $this->message = 'Label too long';
- $this->rfcNumber = 5322;
- }
-}
diff --git a/egulias/email-validator/src/Warning/Warning.php b/egulias/email-validator/src/Warning/Warning.php
index a2ee7b0d..bce7e7a5 100644
--- a/egulias/email-validator/src/Warning/Warning.php
+++ b/egulias/email-validator/src/Warning/Warning.php
@@ -29,7 +29,7 @@ abstract class Warning
*/
public function code()
{
- return static::CODE;
+ return self::CODE;
}
/**