diff --git a/Makefile b/Makefile
index 629c3085..9b109260 100644
--- a/Makefile
+++ b/Makefile
@@ -87,6 +87,9 @@ test-money: phpunit
test-pager: PHPUNIT_TESTSUITE=pager
test-pager: phpunit
+test-user-agent: PHPUNIT_TESTSUITE=user-agent
+test-user-agent: phpunit
+
phpunit:
XDEBUG_MODE=$(XDEBUG_MODE) \
$(PHP) \
diff --git a/bard.json b/bard.json
index d70c02bc..bc74f7e0 100644
--- a/bard.json
+++ b/bard.json
@@ -157,6 +157,14 @@
"path": "src/SonsOfPHP/Contract/Pager",
"repository": "git@github.com:SonsOfPHP/pager-contract.git"
},
+ {
+ "path": "src/SonsOfPHP/Component/UserAgent",
+ "repository": "git@github.com:SonsOfPHP/user-agent.git"
+ },
+ {
+ "path": "src/SonsOfPHP/Contract/UserAgent",
+ "repository": "git@github.com:SonsOfPHP/user-agent-contract.git"
+ },
{
"path": "src/SonsOfPHP/Contract/Version",
"repository": "git@github.com:SonsOfPHP/version-contract.git"
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 1d748e5f..c7c3d401 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -98,6 +98,10 @@
src/SonsOfPHP/Component/Pager/Tests
+
+
+
+
src/SonsOfPHP/Component/Version/Tests
diff --git a/src/SonsOfPHP/Component/UserAgent/.gitattributes b/src/SonsOfPHP/Component/UserAgent/.gitattributes
new file mode 100644
index 00000000..84c7add0
--- /dev/null
+++ b/src/SonsOfPHP/Component/UserAgent/.gitattributes
@@ -0,0 +1,4 @@
+/Tests export-ignore
+/phpunit.xml.dist export-ignore
+/.gitattributes export-ignore
+/.gitignore export-ignore
diff --git a/src/SonsOfPHP/Component/UserAgent/.gitignore b/src/SonsOfPHP/Component/UserAgent/.gitignore
new file mode 100644
index 00000000..5414c2c6
--- /dev/null
+++ b/src/SonsOfPHP/Component/UserAgent/.gitignore
@@ -0,0 +1,3 @@
+composer.lock
+phpunit.xml
+vendor/
diff --git a/src/SonsOfPHP/Component/UserAgent/LICENSE b/src/SonsOfPHP/Component/UserAgent/LICENSE
new file mode 100644
index 00000000..d950b8dd
--- /dev/null
+++ b/src/SonsOfPHP/Component/UserAgent/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2022 Joshua Estes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/SonsOfPHP/Component/UserAgent/README.md b/src/SonsOfPHP/Component/UserAgent/README.md
new file mode 100644
index 00000000..3c9e3fac
--- /dev/null
+++ b/src/SonsOfPHP/Component/UserAgent/README.md
@@ -0,0 +1,18 @@
+Sons of PHP - User Agent Component
+==================================
+
+## Learn More
+
+* [Documentation][docs]
+* [Contributing][contributing]
+* [Report Issues][issues] and [Submit Pull Requests][pull-requests] in the
+ [Mother Repository][mother-repo]
+* Get Help & Support using [Discussions][discussions] or [Discord][discord]
+
+[discussions]: https://github.com/orgs/SonsOfPHP/discussions
+[mother-repo]: https://github.com/SonsOfPHP/sonsofphp
+[contributing]: https://docs.sonsofphp.com/contributing/
+[docs]: https://docs.sonsofphp.com/components/user-agent/
+[issues]: https://github.com/SonsOfPHP/sonsofphp/issues?q=is%3Aopen+is%3Aissue+label%3AUserAgent
+[pull-requests]: https://github.com/SonsOfPHP/sonsofphp/pulls?q=is%3Aopen+is%3Apr+label%3AUserAgent
+[discord]: https://discord.gg/sdVxNhFqND
diff --git a/src/SonsOfPHP/Component/UserAgent/UserAgentException.php b/src/SonsOfPHP/Component/UserAgent/UserAgentException.php
new file mode 100644
index 00000000..1452ec40
--- /dev/null
+++ b/src/SonsOfPHP/Component/UserAgent/UserAgentException.php
@@ -0,0 +1,12 @@
+
+ */
+class UserAgentException extends \Exception
+{
+}
diff --git a/src/SonsOfPHP/Component/UserAgent/composer.json b/src/SonsOfPHP/Component/UserAgent/composer.json
new file mode 100644
index 00000000..e6f0d8c0
--- /dev/null
+++ b/src/SonsOfPHP/Component/UserAgent/composer.json
@@ -0,0 +1,52 @@
+{
+ "name": "sonsofphp/user-agent",
+ "type": "library",
+ "description": "Provides more details about the browser the end user is currently using.",
+ "keywords": [
+ "user-agent",
+ "browser"
+ ],
+ "homepage": "https://github.com/SonsOfPHP/user-agent",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Joshua Estes",
+ "email": "joshua@sonsofphp.com"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/SonsOfPHP/sonsofphp/issues",
+ "forum": "https://github.com/orgs/SonsOfPHP/discussions",
+ "docs": "https://docs.sonsofphp.com",
+ "chat": "https://discord.gg/sdVxNhFqND"
+ },
+ "autoload": {
+ "psr-4": {
+ "SonsOfPHP\\Component\\UserAgent\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "require": {
+ "php": ">=8.1"
+ },
+ "extra": {
+ "sort-packages": true,
+ "branch-alias": {
+ "dev-main": "0.3.x-dev"
+ }
+ },
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/JoshuaEstes"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/subscription/pkg/packagist-sonsofphp-sonsofphp"
+ }
+ ]
+}
diff --git a/src/SonsOfPHP/Contract/UserAgent/.gitattributes b/src/SonsOfPHP/Contract/UserAgent/.gitattributes
new file mode 100644
index 00000000..3a01b372
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/.gitattributes
@@ -0,0 +1,2 @@
+/.gitattributes export-ignore
+/.gitignore export-ignore
diff --git a/src/SonsOfPHP/Contract/UserAgent/.gitignore b/src/SonsOfPHP/Contract/UserAgent/.gitignore
new file mode 100644
index 00000000..d8a7996a
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor/
diff --git a/src/SonsOfPHP/Contract/UserAgent/LICENSE b/src/SonsOfPHP/Contract/UserAgent/LICENSE
new file mode 100644
index 00000000..39238382
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2022 to Present Joshua Estes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/SonsOfPHP/Contract/UserAgent/README.md b/src/SonsOfPHP/Contract/UserAgent/README.md
new file mode 100644
index 00000000..aba5809c
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/README.md
@@ -0,0 +1,16 @@
+Sons of PHP - User Agent Contract
+=================================
+
+## Learn More
+
+* [Documentation][docs]
+* [Contributing][contributing]
+* [Report Issues][issues] and [Submit Pull Requests][pull-requests] in the [Mother Repository][mother-repo]
+* Get Help & Support using [Discussions][discussions]
+
+[discussions]: https://github.com/orgs/SonsOfPHP/discussions
+[mother-repo]: https://github.com/SonsOfPHP/sonsofphp
+[contributing]: https://docs.sonsofphp.com/contributing/
+[docs]: https://docs.sonsofphp.com/contracts/user-agent/
+[issues]: https://github.com/SonsOfPHP/sonsofphp/issues?q=is%3Aopen+is%3Aissue+label%3AUserAgent
+[pull-requests]: https://github.com/SonsOfPHP/sonsofphp/pulls?q=is%3Aopen+is%3Apr+label%3AUserAgent
diff --git a/src/SonsOfPHP/Contract/UserAgent/UserAgentExceptionInterface.php b/src/SonsOfPHP/Contract/UserAgent/UserAgentExceptionInterface.php
new file mode 100644
index 00000000..049293d7
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/UserAgentExceptionInterface.php
@@ -0,0 +1,10 @@
+
+ */
+interface UserAgentExceptionInterface {}
diff --git a/src/SonsOfPHP/Contract/UserAgent/UserAgentFactoryInterface.php b/src/SonsOfPHP/Contract/UserAgent/UserAgentFactoryInterface.php
new file mode 100644
index 00000000..83bebd34
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/UserAgentFactoryInterface.php
@@ -0,0 +1,15 @@
+
+ */
+interface UserAgentFactoryInterface
+{
+ public static function createFromGlobals(): UserAgentInterface;
+
+ // createFromRequest(RequestInterface $request); <- PSR-7 Request
+}
diff --git a/src/SonsOfPHP/Contract/UserAgent/UserAgentInterface.php b/src/SonsOfPHP/Contract/UserAgent/UserAgentInterface.php
new file mode 100644
index 00000000..9a63dc61
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/UserAgentInterface.php
@@ -0,0 +1,19 @@
+
+ */
+interface UserAgentInterface extends \Stringable, \JsonSerializable
+{
+ public function isBot(): bool;
+
+ public function isMobile(): bool;
+
+ public function isTablet(): bool;
+
+ public function isDesktop(): bool;
+}
diff --git a/src/SonsOfPHP/Contract/UserAgent/UserAgentProviderInterface.php b/src/SonsOfPHP/Contract/UserAgent/UserAgentProviderInterface.php
new file mode 100644
index 00000000..ba01bfd4
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/UserAgentProviderInterface.php
@@ -0,0 +1,13 @@
+
+ */
+interface UserAgentProviderInterface
+{
+ public function getForUA(string $ua): UserAgentInterface;
+}
diff --git a/src/SonsOfPHP/Contract/UserAgent/composer.json b/src/SonsOfPHP/Contract/UserAgent/composer.json
new file mode 100644
index 00000000..f6e42fb0
--- /dev/null
+++ b/src/SonsOfPHP/Contract/UserAgent/composer.json
@@ -0,0 +1,52 @@
+{
+ "name": "sonsofphp/user-agent-contract",
+ "type": "library",
+ "description": "",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "homepage": "https://github.com/SonsOfPHP/user-agent-contract",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Joshua Estes",
+ "email": "joshua@sonsofphp.com"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/SonsOfPHP/sonsofphp/issues",
+ "forum": "https://github.com/orgs/SonsOfPHP/discussions",
+ "docs": "https://docs.sonsofphp.com"
+ },
+ "autoload": {
+ "psr-4": {
+ "SonsOfPHP\\Contract\\UserAgent\\": ""
+ }
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "require": {
+ "php": ">=8.1"
+ },
+ "extra": {
+ "sort-packages": true,
+ "branch-alias": {
+ "dev-main": "0.3.x-dev"
+ }
+ },
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/JoshuaEstes"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/subscription/pkg/packagist-sonsofphp-sonsofphp"
+ }
+ ]
+}