Skip to content

Commit

Permalink
Built initial URL interface structure
Browse files Browse the repository at this point in the history
Signed-off-by: Tom Wright <tom@inflatablecookie.com>
  • Loading branch information
betterthanclay committed Oct 29, 2023
1 parent 6eb33f7 commit b785aa1
Show file tree
Hide file tree
Showing 29 changed files with 1,560 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# https://EditorConfig.org

root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
block_comment_start = /*
block_comment = *
block_comment_end = */

[*.yml]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.editorconfig export-ignore
.gitattributes export-ignore
.github/ export-ignore
.gitignore export-ignore
CHANGELOG.md export-ignore
ecs.php export-ignore
phpstan.neon export-ignore
phpunit.xml.dist export-ignore
docs/ export-ignore
tests/ export-ignore
stubs/ export-ignore
130 changes: 130 additions & 0 deletions .github/workflows/integrate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow

name: "Integrate"

on:
push:
branches:
- "develop"
pull_request: null

env:
PHP_EXTENSIONS: "intl"

jobs:
file_consistency:
name: "1️⃣ File consistency"
runs-on: "ubuntu-latest"
steps:
- name: "Set up PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "8.0"
extensions: "${{ env.PHP_EXTENSIONS }}"
ini-values: "post_max_size=256M"

- name: "Checkout code"
uses: "actions/checkout@v3"

- name: Install Effigy
run: |
composer global config --no-plugins allow-plugins.phpstan/extension-installer true
composer global require decodelabs/effigy
- name: "Install dependencies"
uses: "ramsey/composer-install@v2"
with:
dependency-versions: "highest"

- name: "Check file permissions"
run: |
composer global exec effigy check-executable-permissions
- name: "Check exported files"
run: |
composer global exec effigy check-git-exports
- name: "Find non-printable ASCII characters"
run: |
composer global exec effigy check-non-ascii
- name: "Check source code for syntax errors"
run: |
composer global exec effigy lint
static_analysis:
name: "3️⃣ Static Analysis"
needs:
- "file_consistency"
runs-on: "ubuntu-latest"
strategy:
matrix:
php-version:
- "8.0"
- "8.1"
- "8.2"
steps:
- name: "Set up PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
extensions: "${{ env.PHP_EXTENSIONS }}"
ini-values: "post_max_size=256M"

- name: "Checkout code"
uses: "actions/checkout@v3"

- name: Install Effigy
run: |
composer global config --no-plugins allow-plugins.phpstan/extension-installer true
composer global require decodelabs/effigy
- name: "Validate Composer configuration"
run: "composer validate --strict"

- name: "Install dependencies"
uses: "ramsey/composer-install@v2"
with:
dependency-versions: "highest"

- name: "Execute static analysis"
run: |
composer global exec effigy analyze -- --headless
coding_standards:
name: "4️⃣ Coding Standards"
needs:
- "file_consistency"
runs-on: "ubuntu-latest"
steps:
- name: "Set up PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "8.0"
extensions: "${{ env.PHP_EXTENSIONS }}"
ini-values: "post_max_size=256M"

- name: "Checkout code"
uses: "actions/checkout@v3"

- name: "Check EditorConfig configuration"
run: "test -f .editorconfig"

- name: "Check adherence to EditorConfig"
uses: "greut/eclint-action@v0"

- name: Install Effigy
run: |
composer global config --no-plugins allow-plugins.phpstan/extension-installer true
composer global require decodelabs/effigy
- name: "Install dependencies"
uses: "ramsey/composer-install@v2"
with:
dependency-versions: "highest"

- name: "Check coding style"
run: |
composer global exec effigy format -- --headless
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/vendor
composer.phar
composer.lock
.DS_Store
Thumbs.db
/phpunit.xml
/.idea
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## v0.1.0 (2023-10-29)
* Built initial URI interface structure
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Singularity

[![PHP from Packagist](https://img.shields.io/packagist/php-v/decodelabs/singularity?style=flat)](https://packagist.org/packages/decodelabs/singularity)
[![Latest Version](https://img.shields.io/packagist/v/decodelabs/singularity.svg?style=flat)](https://packagist.org/packages/decodelabs/singularity)
[![Total Downloads](https://img.shields.io/packagist/dt/decodelabs/singularity.svg?style=flat)](https://packagist.org/packages/decodelabs/singularity)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/decodelabs/singularity/Integrate)](https://github.com/string|int|floatdecodelabs/singularity/actions/workflows/integrate.yml)
[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-44CC11.svg?longCache=true&style=flat)](https://github.com/phpstan/phpstan)
[![License](https://img.shields.io/packagist/l/decodelabs/singularity?style=flat)](https://packagist.org/packages/decodelabs/singularity)

### Consolidated URI parsing and resolving

Singularity provides a unified interface for parsing and resolving URIs, PSR-7 URLs and file paths.

_Get news and updates on the [DecodeLabs blog](https://blog.decodelabs.com)._

---

## Installation

Install via Composer:

```bash
composer require decodelabs/singularity
```

## Usage

Parse and normalize URIs:

```php

use DecodeLabs\Singularity;

dd(
// ::uri() will parse any valid URI
Singularity::uri('mailto:info@example.com'),

// ::url() will parse any valid URL
Singularity::url('http://user:pass@www.example.com:8080/resource/page.html?param1=value1&param2=value2#section1'),
Singularity::url('ftp://ftp.example.com/files/document.pdf'),
Singularity::url('mailto:user@example.com?subject=Hello&body=Hi%20there'),
Singularity::url('tel:+1-816-555-1212'),

// ::urn() will parse any valid URN
Singularity::urn('urn:isbn:0-486-27557-4'),
Singularity::urn('urn:ietf:rfc:3986'),
Singularity::urn('urn:oid:2.16.840')
);
```

## Licensing

Singularity is licensed under the proprietary License. See [LICENSE](./LICENSE) for the full license text.
35 changes: 35 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "decodelabs/singularity",
"description": "Consolidated URI parsing and resolving",
"type": "library",
"keywords": [ ],
"license": "proprietary",
"authors": [ {
"name": "Tom Wright",
"email": "tom@inflatablecookie.com"
} ],
"require": {
"php": "^8.0",

"decodelabs/exceptional": "^0.4.4",
"decodelabs/archetype": "^0.2.14",

"psr/http-message": "^2.0",
"decodelabs/collections": "^0.8.4",
"decodelabs/glitch-support": "^0.4.4"
},
"require-dev": {
"decodelabs/glitch": "^0.18.8",
"decodelabs/phpstan-decodelabs": "^0.6.7"
},
"autoload": {
"psr-4": {
"DecodeLabs\\": "src/"
}
},
"extra": {
"branch-alias": {
"dev-develop": "0.1.x-dev"
}
}
}
13 changes: 13 additions & 0 deletions ecs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

// ecs.php

declare(strict_types=1);

use Symplify\EasyCodingStandard\Config\ECSConfig;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;

return static function (ECSConfig $ecsConfig): void {
$ecsConfig->paths([__DIR__.'/src']);
$ecsConfig->sets([SetList::CLEAN_CODE, SetList::PSR_12]);
};
4 changes: 4 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parameters:
paths:
- src
level: max
93 changes: 93 additions & 0 deletions src/Singularity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

/**
* @package Singularity
* @license http://opensource.org/licenses/MIT
*/

declare(strict_types=1);

namespace DecodeLabs;

use DecodeLabs\Singularity\Uri;
use DecodeLabs\Singularity\Url;
use DecodeLabs\Singularity\Url\Generic as GenericUrl;
use DecodeLabs\Singularity\Urn;
use DecodeLabs\Singularity\Urn\Generic as GenericUrn;

class Singularity
{
public static function uri(
string|Uri|null $uri
): ?Uri {
if ($uri === null) {
return null;
}

if ($uri instanceof Uri) {
return $uri;
}

if (!preg_match('/^([a-z][a-z0-9+.-]*):/i', $uri, $matches)) {
$scheme = 'Http';
} else {
$scheme = ucfirst(strtolower($matches[1]));
}

if ($scheme === 'Urn') {
if (!preg_match('/^urn:([a-z0-9][a-z0-9-]{1,31}):/i', $uri, $matches)) {
throw Exceptional::InvalidArgument(
'Invalid URN: ' . $uri
);
}

if (!$class = Archetype::tryResolve(Urn::class, $matches[1])) {
$class = GenericUrn::class;
}
} else {
if ($scheme === 'Https') {
$scheme = 'Http';
}

if (!$class = Archetype::tryResolve(Url::class, $scheme)) {
$class = GenericUrl::class;
}
}

return $class::fromString($uri);
}

public static function url(
string|Uri|null $uri
): ?Url {
$output = self::uri($uri);

if (
$output !== null &&
!$output instanceof Url
) {
throw Exceptional::InvalidArgument(
'URI is not a URL'
);
}

return $output;
}

public static function urn(
string|Uri|null $uri
): ?Urn {
$output = self::uri($uri);

if (
$output !== null &&
!$output instanceof Urn
) {
throw Exceptional::InvalidArgument(
'URI is not a URN'
);
}

return $output;
}
}
Loading

0 comments on commit b785aa1

Please sign in to comment.