diff --git a/CHANGELOG.md b/CHANGELOG.md index 50791ae..510590d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[v0.2.1](https://github.com/The-FireHub-Project/Foundation/compare/v0.2.1...v0.2.0)] – 2026-11-03 + +### Added +* Introduce `Origin`, `InvalidOriginException`, and default port resolution for URI schemes by @dieselxxx + ## [[v0.2.0](https://github.com/The-FireHub-Project/Foundation/compare/v0.2.0...v0.1.0)] – 2026-11-02 ### Added diff --git a/README.md b/README.md index 0b6323c..29ff1a5 100644 --- a/README.md +++ b/README.md @@ -12,84 +12,124 @@ /> version GitHub license -[![PHPStan](https://github.com/The-FireHub-Project/Foundation/actions/workflows/PHPStan.yml/badge.svg?branch=develop)](https://github.com/The-FireHub-Project/Foundation/actions/workflows/PHPStan.yml) -[![PHPUnit](https://github.com/The-FireHub-Project/Foundation/actions/workflows/PHPUnit.yml/badge.svg?branch=develop)](https://github.com/The-FireHub-Project/Foundation/actions/workflows/PHPUnit.yml) -[![Codecov](https://codecov.io/gh/The-FireHub-Project/Foundation/branch/develop/graph/badge.svg?token=XW2YEONF51)](https://app.codecov.io/gh/The-FireHub-Project/Foundation/tree/develop) -

- + + GitHub created at + GitHub last commit (branch) - + GitHub activity (branch) - + + GitHub contributors + + GitHub language count + + GitHub top language + GitHub commit difference between two branches + + GitHub code size

-The essential PHP core for building domain-driven, clean architecture applications. -Standard provides the base classes, kernel, domain layer, and minimal runtime utilities needed to start any FireHub project. +Provides the high-level foundation layer of the FireHub ecosystem. This repository contains developer-focused APIs and reusable building blocks that simplify common programming tasks while maintaining consistency, type safety, and architectural principles across FireHub applications. + +The Foundation layer is built on top of the Runtime layer and transforms low-level execution primitives into expressive object-oriented APIs. It provides commonly used components such as string manipulation, date and time handling, mathematical utilities, and other essential application-level abstractions. + +This repository represents the developer-facing layer between the FireHub Runtime and application code, enabling clean, readable, and maintainable PHP development. --- -## FireHub Icon Development Branch +## FireHub Icon Repository Purpose + +FireHub Foundation is the high-level developer API layer of the FireHub ecosystem. + +Built on top of FireHub Runtime, it provides expressive, object-oriented abstractions for common programming tasks. The purpose of this layer is to expose simple, consistent, and powerful APIs while hiding low-level implementation details. -⚠️ **This is the `develop` branch** +Foundation contains reusable building blocks such as: -- Unstable -- APIs may change without notice -- Not intended for production use +- String manipulation APIs +- Date and time handling +- Mathematical utilities +- Common application-level abstractions +- Developer-oriented helper objects -## FireHub Icon Branch Purpose +The Foundation layer follows the FireHub architectural principles by separating responsibilities between definition, execution, and usage: -The `develop` branch is the **primary integration branch** for all ongoing development. +## FireHub Icon Security -It serves as the staging area where: -- Feature branches are merged -- Bug fixes are integrated -- Experimental work is stabilized -- Code is prepared for upcoming releases +For security reporting and guidelines, please refer to our [SECURITY.md](https://github.com/The-FireHub-Project/.github/blob/master/.github/SECURITY.md) file. -All **release branches** are created **from `develop`**. +## FireHub Icon Community -## Stability Guarantee +![GitHub watchers](https://img.shields.io/github/watchers/The-FireHub-Project/Foundation?style=social) +![GitHub Repo stars](https://img.shields.io/github/stars/The-FireHub-Project/Foundation?style=social) +![GitHub forks](https://img.shields.io/github/forks/The-FireHub-Project/Foundation?style=social) -❌ No backward compatibility guarantee -❌ APIs may change without notice -❌ Behavior may be incomplete or inconsistent -❌ Breaking changes are expected +- Be sure to review the [Code of Conduct](https://github.com/The-FireHub-Project/.github/blob/master/.github/CODE_OF_CONDUCT.md). +- Read a [Contributing](https://github.com/The-FireHub-Project/.github/blob/master/.github/CONTRIBUTING.md) file to see how you can contribute to this project. +- Read our [Security policy](https://github.com/The-FireHub-Project/.github/blob/master/.github/SECURITY.md) file to see how you can report a vulnerability. +- Review the project [Governance model](https://github.com/The-FireHub-Project/.github/blob/master/.github/GOVERNANCE.md), including roles, decision-making, RFC process, and releases. -This branch is intended **only for contributors and advanced testers**. +## FireHub Icon Support -## FireHub Icon Composer Usage (Not Recommended) +

+ + GitHub in-progress issues + + GitHub total issues + + GitHub opened pull requests +

-For internal testing only: +For help with FireHub products, please refer to the following resources: -```json -{ - "require": { - "the-firehub-project/foundation": "dev-develop" - } -} -``` -⚠️ Never use dev-develop in production. +- **General questions & discussions**: [GitHub Discussions](https://github.com/orgs/The-FireHub-Project/discussions) +- **Bug reports & issues**: [Support](https://github.com/The-FireHub-Project/Foundation/issues) +- **Documentation & guides**: [FireHub Docs](https://the-firehub-project.github.io) +- **Commercial support** (Pro/Enterprise): contact your account manager ## FireHub Icon Authors and Contributors @@ -106,4 +146,4 @@ Architecture guidelines, design principles, and ecosystem documentation are avai This software is licensed under the Apache-2.0 License. -For more details, read the full license [here](./LICENSE). \ No newline at end of file +For more details, read the full license [here](./LICENSE). diff --git a/src/Uri/Component/Scheme.php b/src/Uri/Component/Scheme.php index cc530a6..a7dc23f 100644 --- a/src/Uri/Component/Scheme.php +++ b/src/Uri/Component/Scheme.php @@ -14,6 +14,7 @@ namespace FireHub\Foundation\Uri\Component; use FireHub\Core\Type\ValueObject; +use FireHub\Foundation\Uri\KnownScheme; use FireHub\Foundation\Uri\Exception\InvalidSchemeException; use FireHub\Runtime; @@ -60,6 +61,22 @@ public function __construct ( } + /** + * ### Returns the known URI scheme + * + * Resolves the URI scheme to a known scheme enumeration. + * @since 1.0.0 + * + * @uses \FireHub\Foundation\Uri\Component\Scheme::normalized() To get the normalized scheme. + * + * @return null|\FireHub\Foundation\Uri\KnownScheme Known scheme or null. + */ + public function known ():?KnownScheme { + + return KnownScheme::tryFrom($this->normalized()); + + } + /** * ### Returns the normalized scheme * @since 1.0.0 diff --git a/src/Uri/Exception/InvalidOriginException.php b/src/Uri/Exception/InvalidOriginException.php new file mode 100644 index 0000000..03c82e3 --- /dev/null +++ b/src/Uri/Exception/InvalidOriginException.php @@ -0,0 +1,33 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.3 + * @package Foundation + */ + +namespace FireHub\Foundation\Uri\Exception; + +use FireHub\Core\Exception\Runtime\InvalidArgumentException; + +/** + * ### ### Invalid Origin Exception + * + * Thrown when a URI cannot be represented as a valid tuple-based origin. + * @since 1.0.0 + */ +final class InvalidOriginException extends InvalidArgumentException { + + /** + * @inheritdoc + * + * @since 1.0.0 + */ + protected const string DEFAULT_MESSAGE = 'Invalid URI origin.'; + +} \ No newline at end of file diff --git a/src/Uri/KnownScheme.php b/src/Uri/KnownScheme.php index dd60a55..cccece6 100644 --- a/src/Uri/KnownScheme.php +++ b/src/Uri/KnownScheme.php @@ -150,4 +150,32 @@ enum KnownScheme:string { */ case GIT = 'git'; + /** + * ### Returns the default network port + * + * Returns the conventional network port associated with the URI scheme, or null if no default port is defined. + * @since 1.0.0 + * + * @return null|int<0, 65535> Default network port. + */ + public function defaultPort ():?int { + + return match ($this) { + self::HTTP, self::WS => 80, + self::HTTPS, self::WSS => 443, + self::FTP => 21, + self::SSH => 22, + self::LDAP => 389, + self::LDAPS => 636, + self::SIP => 5060, + self::SIPS => 5061, + self::IRC => 6667, + self::IRCS => 6697, + self::NNTP => 119, + self::GIT => 9418, + default => null + }; + + } + } \ No newline at end of file diff --git a/src/Uri/Origin.php b/src/Uri/Origin.php new file mode 100644 index 0000000..2732553 --- /dev/null +++ b/src/Uri/Origin.php @@ -0,0 +1,257 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.2 + * @package Foundation + */ + +namespace FireHub\Foundation\Uri; + +use FireHub\Core\Type\ValueObject; +use FireHub\Foundation\Uri; +use FireHub\Foundation\Uri\Component\ { + Host, Port, Scheme +}; +use FireHub\Foundation\Uri\Exception\InvalidOriginException; +use FireHub\Runtime; + +/** + * ### URI Origin + * + * Represents an immutable tuple-based origin. + * + * An origin is identified by its scheme, host, and effective port. + * + * Supports HTTP, HTTPS, WS, WSS, and FTP origins. + * + * Default ports are normalized during serialization and comparison. + * + * The origin does not include URI user information, path, query, or fragment. + * @since 1.0.0 + * + * @extends \FireHub\Core\Type\ValueObject + */ +final readonly class Origin extends ValueObject { + + /** + * ### Constructor + * @since 1.0.0 + * + * @uses \FireHub\Core\Type\ValueObject::guard() As a guard. + * @uses \FireHub\Foundation\Uri\Component\Host::value() To get the host value. + * @uses \FireHub\Runtime\Str\SB\Casing::toLower() To normalize the scheme. + * + * @param \FireHub\Foundation\Uri\Component\Scheme $scheme

+ * Origin scheme. + *

+ * @param \FireHub\Foundation\Uri\Component\Host $host

+ * Origin host. + *

+ * @param null|\FireHub\Foundation\Uri\Component\Port $port [optional]

+ * Explicit origin port or null to use the scheme's default port. + *

+ * + * @throws \FireHub\Foundation\Uri\Exception\InvalidOriginException If the origin components are invalid. + * @throws \FireHub\Core\Exception\FireHubException If the condition is not met. + * @throws \FireHub\Core\Type\Exception\ValueObjectException If the exception is not a FireHubException. + * + * @return void + */ + public function __construct ( + private Scheme $scheme, + private Host $host, + private ?Port $port = null + ) { + + $scheme_value = Runtime\Str\SB\Casing::toLower($scheme->value()); + + $this->guard( + fn() => match ($scheme_value) { + KnownScheme::HTTP->value, KnownScheme::HTTPS->value, KnownScheme::WS->value, KnownScheme::WSS->value, + KnownScheme::FTP->value + => true, + default => false + }, + fn() => new InvalidOriginException( + 'The URI scheme does not support a tuple-based origin.' + ) + ); + + $this->guard( + fn() => $host->value() !== '', + fn() => new InvalidOriginException( + 'An origin must contain a host.' + ) + ); + + } + + /** + * ### Creates an origin from a URI + * + * Extracts the scheme, host, and port from an existing URI. + * + * The URI path, query, fragment, and user information do not participate in origin identity. + * @since 1.0.0 + * + * @uses \FireHub\Foundation\Uri::scheme() To get the URI scheme. + * @uses \FireHub\Foundation\Uri::authority() To get the URI authority. + * @uses \FireHub\Foundation\Uri\Component\Host::value() To get the host value. + * @uses \FireHub\Foundation\Uri\Component\Authority::value() To get the authority value. + * @uses \FireHub\Foundation\Uri\Component\Authority::port() To get the authority port. + * + * @param \FireHub\Foundation\Uri $uri

+ * URI from which to derive the origin. + *

+ * + * @throws \FireHub\Foundation\Uri\Exception\InvalidOriginException If the origin components are invalid. + * @throws \FireHub\Core\Exception\FireHubException If the condition is not met. + * @throws \FireHub\Core\Type\Exception\ValueObjectException If the exception is not a FireHubException. + * + * @return self Derived origin. + */ + public static function fromUri (Uri $uri):self { + + $scheme = $uri->scheme(); + $authority = $uri->authority(); + + if ($scheme === null || $authority === null) + throw new InvalidOriginException( + 'The URI must contain a scheme and authority.' + ); + + $host = $authority->host(); + + if ($host->value() === '') + throw new InvalidOriginException( + 'The URI must contain a host.' + ); + + return new self( + $scheme, + $host, + $authority->port() + ); + + } + + /** + * ### Gets the origin scheme + * @since 1.0.0 + * + * @return \FireHub\Foundation\Uri\Component\Scheme Origin scheme. + */ + public function scheme ():Scheme { + + return $this->scheme; + + } + + /** + * ### Gets the origin host + * @since 1.0.0 + * + * @return \FireHub\Foundation\Uri\Component\Host Origin host. + */ + public function host ():Host { + + return $this->host; + + } + + /** + * ### Gets the origin port + * @since 1.0.0 + * + * @return null|\FireHub\Foundation\Uri\Component\Port Explicit origin port. + */ + public function port ():?Port { + + return $this->port; + + } + + /** + * ### Gets the effective origin port + * @since 1.0.0 + * + * @uses \FireHub\Foundation\Uri\Component\Port::value() To get the port value. + * @uses \FireHub\Foundation\Uri\Component\Scheme::known() To get the scheme known information. + * @uses \FireHub\Foundation\Uri\KnownScheme::defaultPort() To get the default port. + * + * @throws \FireHub\Foundation\Uri\Exception\InvalidOriginException If the origin scheme does not define a + * default port. + * + * @return int Effective origin port. + */ + public function effectivePort ():int { + + return $this->port?->value() + ?? $this->scheme->known()?->defaultPort() + ?? throw new InvalidOriginException( + 'The origin scheme does not define a default port.' + ); + + } + + /** + * @inheritDoc + * + * @since 1.0.0 + * + * @uses \FireHub\Foundation\Uri\Component\Scheme::normalized() To get the normalized scheme. + * @uses \FireHub\Foundation\Uri\Component\Host::normalized() To get the normalized host. + * @uses \FireHub\Foundation\Uri\Component\Port::value() To get the port value. + * @uses \FireHub\Foundation\Uri\Component\Scheme::known() To get the scheme known information. + * @uses \FireHub\Foundation\Uri\KnownScheme::defaultPort() To get the default port. + * + * @return non-empty-string Serialized origin. + */ + public function value ():string { + + $scheme = $this->scheme->normalized(); + $host = $this->host->normalized(); + + $default_port = $this->scheme->known()?->defaultPort(); + $port = $this->port?->value(); + + return $scheme.'://'.$host + .( + $port !== null && $port !== $default_port + ? ':'.$port + : '' + ); + + } + + /** + * @inheritDoc + * + * @since 1.0.0 + * + * @uses \FireHub\Foundation\Uri\Component\Scheme::normalized() To normalize the scheme. + * @uses \FireHub\Foundation\Uri\Component\Host::normalized() To normalize the host. + * @uses \FireHub\Foundation\Uri\Origin::effectivePort() To get the effective port. + * + * @throws \FireHub\Foundation\Uri\Exception\InvalidOriginException If the origin scheme does not define a + * default port. + * + * @return non-empty-string Normalized origin identity. + */ + protected function comparisonValue ():string { + + return $this->scheme->normalized() + .'://' + .$this->host->normalized() + .':' + .$this->effectivePort(); + + } + +} \ No newline at end of file