├── LICENSE ├── README.md ├── composer.json ├── hh_autoload.json └── src ├── Cache ├── AggregateCacheServiceProvider.hack ├── CacheConfig.hack ├── CacheConfigTrait.hack ├── CacheManagerProvider.hack ├── CacheProvider.hack ├── Exception │ └── CacheDriverNotFoundException.hack ├── Resolver │ └── MemcachedResolver.hack └── types.hack ├── Emitter └── EmitterProvider.hack ├── Exception ├── AbstractVndErrorException.hack ├── ExceptionHandleInterface.hack ├── ExceptionHandler.hack ├── ExceptionHandlerProvider.hack ├── ExceptionRegister.hack ├── ExceptionRegisterProvider.hack ├── NotFoundHttpException.hack ├── VndErrorExceptionInterface.hack └── VndErrorFactory.hack ├── Foundation ├── Application.hack ├── ApplicationConfig.hack ├── ApplicationConfigProvider.hack ├── ApplicationProvider.hack ├── Bootstrap │ ├── BootstrapRegister.hack │ └── BootstrapRegisterInterface.hack ├── Command │ ├── ApplicationCacheClear.hack │ ├── CliApplication.hack │ └── ContainerCacheClear.hack ├── ConfigInterface.hack ├── ConsistentServiceProvider.hack ├── GlueConfigTrait.hack └── ServiceProvider.hack ├── Http └── VndErrorResponse.hack ├── Logger └── LoggerProvider.hack ├── Middleware ├── Dispatcher.hack ├── GlueResolver.hack ├── LogExceptionMiddleware.hack ├── LogExceptionMiddlewareProvider.hack ├── SimpleCorsMiddleware.hack └── SimpleCorsMiddlewareProvider.hack ├── RequestHandler └── AsyncFallbackHandler.hack ├── Routing ├── Exception │ └── NotFoundException.hack ├── Router.hack ├── RouterProvider.hack └── types.hack └── Validation ├── RequestValidation.hack ├── ValidationException.hack ├── Validator.hack └── ValidatorFactory.hack /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2020 Yuuki Takezawa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nazg Micro Framework Core Repository 2 | 3 | [![Packagist](https://img.shields.io/badge/HHVM-%3E=4.41-orange.svg?style=flat-square)](https://packagist.org/packages/nazg/framework) 4 | [![Packagist](https://img.shields.io/packagist/l/nazg/framework.svg?style=flat-square)](https://packagist.org/packages/nazg/framework) 5 | [![Build Status](http://img.shields.io/travis/nazg-hack/framework/master.svg?style=flat-square)](https://travis-ci.org/nazg-hack/framework) 6 | 7 | Http Application / MicroFramework for HHVM/Hack 8 | 9 | ## Supported 10 | 11 | HHVM 4.41 and above. 12 | 13 | ## Usage 14 | 15 | [Skeleton](https://github.com/ytake/nazg-skeleton) 16 | 17 | ```bash 18 | $ composer install 19 | ``` 20 | 21 | ## License 22 | 23 | The Nazg Framework is licensed under The MIT License (MIT). See License File for more information. 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nazg/framework", 3 | "description": "Http Microframework for Hack", 4 | "keywords": [ 5 | "hhvm", 6 | "hack", 7 | "cache", 8 | "middleware", 9 | "dependency injection" 10 | ], 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Yuuki Takezawa", 15 | "email": "yuuki.takezawa@comnect.jp.net" 16 | } 17 | ], 18 | "require": { 19 | "hhvm": "^4.62", 20 | "hhvm/hsl": "^4.0", 21 | "hhvm/hsl-experimental": "^4.50", 22 | "hhvm/hhvm-autoload": "^3.0", 23 | "ytake/hungrr": "^0.13.2", 24 | "ytake/hhypermedia": "^0.6.1", 25 | "nazg/glue": "^1.5", 26 | "nazg/heredity": "^1.12.1", 27 | "nazg/hcache": "^0.6.1", 28 | "nazg/http-server-request-handler": "^0.6.0", 29 | "nazg/http-executor": "^0.12.1", 30 | "facebook/hack-router": "^0.19.6", 31 | "facebook/hh-clilib": "^2.5.0", 32 | "facebook/hack-http-request-response-interfaces": "^0.3", 33 | "hack-logging/hack-logging": "^0.7.1" 34 | }, 35 | "require-dev": { 36 | "facebook/fbexpect": "^2.6.1", 37 | "hhvm/hhast": "^4.0", 38 | "hhvm/hacktest": "^2.0" 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "NazgTest\\": "tests/" 43 | }, 44 | "files": [ 45 | "tests/Struct/Shapes.php" 46 | ] 47 | }, 48 | "scripts": { 49 | "tests": [ 50 | "hhvm ./vendor/bin/hacktest.hack tests/" 51 | ], 52 | "register": [ 53 | "hhvm ./vendor/bin/hh-autoload.hack" 54 | ] 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /hh_autoload.json: -------------------------------------------------------------------------------- 1 | { 2 | "roots": [ 3 | "src/" 4 | ], 5 | "devRoots": [ 6 | "tests/" 7 | ], 8 | "devFailureHandler": null, 9 | "parser": "ext-factparse" 10 | } 11 | -------------------------------------------------------------------------------- /src/Cache/AggregateCacheServiceProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Cache; 17 | 18 | use type Nazg\Foundation\ConsistentServiceProvider; 19 | use type Nazg\Glue\Scope; 20 | use type Nazg\HCache\CacheManager; 21 | 22 | final class AggregateCacheServiceProvider extends ConsistentServiceProvider { 23 | 24 | <<__Override>> 25 | public function apply(): void { 26 | $this->container 27 | ->bind(CacheManager::class) 28 | ->provider(new CacheManagerProvider()) 29 | ->in(Scope::SINGLETON); 30 | $this->container 31 | ->bind(\Nazg\HCache\CacheProvider::class) 32 | ->provider(new CacheProvider()) 33 | ->in(Scope::SINGLETON); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Cache/CacheConfig.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Cache; 17 | 18 | use type Memcached; 19 | use type Nazg\Cache\Resolver\MemcachedResolver; 20 | 21 | class CacheConfig { 22 | 23 | public function __construct( 24 | protected MemcachedConfig $memcachedConfig, 25 | protected FileSystemConfig $filesystemConfig, 26 | ) {} 27 | 28 | public function getMemcached(): ?Memcached { 29 | $resolver = new MemcachedResolver($this->memcachedConfig); 30 | return $resolver->provide(); 31 | } 32 | 33 | public function getFileSystemDir(): ?string { 34 | return Shapes::idx($this->filesystemConfig, 'cacheStoreDir'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Cache/CacheConfigTrait.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Cache; 17 | 18 | use type Nazg\Foundation\ConfigInterface; 19 | 20 | trait CacheConfigTrait { 21 | require implements ConfigInterface; 22 | 23 | protected Driver $cacheDriver = Driver::File; 24 | 25 | private FileSystemConfig $FileSystemConfig = shape( 26 | 'cacheStoreDir' => '/' 27 | ); 28 | 29 | private MemcachedServer $memcachedServer = shape( 30 | 'host' => '127.0.0.1', 'port' => 11211 31 | ); 32 | 33 | private MemcachedConfig $memcachedConfig = shape( 34 | 'servers' => vec[], 35 | ); 36 | 37 | public async function setMemcachedCacheConfigAsync( 38 | MemcachedConfig $memcachedConfig 39 | ): Awaitable { 40 | $this->memcachedConfig = $memcachedConfig; 41 | } 42 | 43 | public async function setFilesystemCacheConfigAsync( 44 | FileSystemConfig $FileSystemConfig 45 | ): Awaitable { 46 | $this->FileSystemConfig = $FileSystemConfig; 47 | } 48 | 49 | public async function setCacheDriverAsync( 50 | Driver $driver 51 | ): Awaitable { 52 | $this->cacheDriver = $driver; 53 | } 54 | 55 | public function getCacheDriver(): Driver { 56 | return $this->cacheDriver; 57 | } 58 | 59 | public function getMemcachedCacheConfig(): MemcachedConfig { 60 | return $this->memcachedConfig; 61 | } 62 | 63 | public function getFilesystemCacheConfig(): FileSystemConfig { 64 | return $this->FileSystemConfig; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Cache/CacheManagerProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Cache; 17 | 18 | use type Nazg\Glue\Container; 19 | use type Nazg\Glue\ProviderInterface; 20 | use type Nazg\HCache\CacheManager; 21 | 22 | final class CacheManagerProvider implements ProviderInterface { 23 | 24 | public function get(Container $_container): CacheManager { 25 | return new CacheManager(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Cache/CacheProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Cache; 17 | 18 | use type Nazg\Glue\Container; 19 | use type Nazg\Glue\ProviderInterface; 20 | use type Nazg\HCache\CacheManager; 21 | use type Nazg\HCache\Driver\{FileSystemCache, MemcachedCache}; 22 | use type Nazg\Foundation\ApplicationConfig; 23 | 24 | class CacheProvider implements ProviderInterface<\Nazg\HCache\CacheProvider> { 25 | 26 | protected Driver $defaultDriver = Driver::File; 27 | 28 | public function get(Container $container): \Nazg\HCache\CacheProvider { 29 | $manager = $container->get(CacheManager::class); 30 | $config = $container->get(ApplicationConfig::class); 31 | $this->defaultDriver = $config->getCacheDriver(); 32 | return $this->detectCacheProvider( 33 | $manager->createCache($this->defaultDriver), 34 | $this->cacheConfig($config) 35 | ); 36 | } 37 | 38 | protected function cacheConfig( 39 | ApplicationConfig $config 40 | ): CacheConfig { 41 | return new CacheConfig( 42 | $config->getMemcachedCacheConfig(), 43 | $config->getFilesystemCacheConfig() 44 | ); 45 | } 46 | 47 | protected function detectCacheProvider( 48 | \Nazg\HCache\CacheProvider $provider, 49 | CacheConfig $cacheConfigure 50 | ): \Nazg\HCache\CacheProvider { 51 | if($this->defaultDriver === Driver::File) { 52 | if($provider is FileSystemCache) { 53 | $dir = $cacheConfigure->getFileSystemDir(); 54 | if($dir is nonnull) { 55 | $provider->setDirectory($dir); 56 | } 57 | } 58 | } 59 | if($this->defaultDriver === Driver::Memcached) { 60 | if($provider is MemcachedCache) { 61 | $m = $cacheConfigure->getMemcached(); 62 | if($m is nonnull) { 63 | $provider->setMemcached($m); 64 | } 65 | } 66 | } 67 | return $provider; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Cache/Exception/CacheDriverNotFoundException.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Cache\Exception; 17 | 18 | final class CacheDriverNotFoundException extends \LogicException { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Cache/Resolver/MemcachedResolver.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2020 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Cache\Resolver; 17 | 18 | use type Memcached; 19 | use type Nazg\Cache\MemcachedConfig; 20 | 21 | 22 | class MemcachedResolver { 23 | 24 | public function __construct( 25 | protected MemcachedConfig $config 26 | ) {} 27 | 28 | public function provide(): Memcached { 29 | $config = $this->config; 30 | $m = new Memcached(Shapes::idx($config, 'persistentId')); 31 | $servers = Shapes::idx($config, 'servers'); 32 | if($servers is nonnull) { 33 | foreach ($servers as $value) { 34 | $m->addServer($value['host'], $value['port'], Shapes::idx($value, 'weight', 0)); 35 | } 36 | } 37 | return $m; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Cache/types.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Cache; 17 | 18 | type MemcachedServer = shape( 19 | 'host' => string, 20 | 'port' => int, 21 | ?'weight' => int, 22 | ); 23 | type FileSystemConfig = shape( 24 | 'cacheStoreDir' => string 25 | ); 26 | type MemcachedConfig = shape( 27 | 'servers' => vec, 28 | ?'persistentId' => string, 29 | ); 30 | 31 | enum Driver : string as string { 32 | Apc = 'apc'; 33 | Void = 'void'; 34 | Map = 'map'; 35 | File = 'file'; 36 | Memcached = 'memcached'; 37 | } 38 | -------------------------------------------------------------------------------- /src/Emitter/EmitterProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Emitter; 17 | 18 | use type Nazg\Glue\ProviderInterface; 19 | use namespace Nazg\HttpExecutor\Emitter; 20 | 21 | final class EmitterProvider implements ProviderInterface { 22 | 23 | public function get( 24 | \Nazg\Glue\Container $_container 25 | ): Emitter\SapiEmitter { 26 | return new Emitter\SapiEmitter(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Exception/AbstractVndErrorException.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | use type Ytake\Hhypermedia\Error\LinkRelation; 19 | use type Ytake\Hhypermedia\LinkResource; 20 | use type Facebook\Experimental\Http\Message\UriInterface; 21 | 22 | abstract class AbstractVndErrorException 23 | extends \Exception implements VndErrorExceptionInterface { 24 | 25 | private dict $attributes = dict[]; 26 | 27 | private dict> $linkRelations = dict[]; 28 | 29 | private int $logRefCode = 500; 30 | 31 | private ?UriInterface $uri = null; 32 | 33 | public function setAttributes( 34 | dict $attributes 35 | ): void { 36 | $this->attributes = $attributes; 37 | } 38 | 39 | public function getAttributes(): dict { 40 | return $this->attributes; 41 | } 42 | 43 | public function setRelations( 44 | dict> $linkRelations 45 | ): void { 46 | $this->linkRelations = $linkRelations; 47 | } 48 | 49 | public function setLogRef( 50 | int $logRefCode 51 | ): void { 52 | $this->logRefCode = $logRefCode; 53 | } 54 | 55 | public function getRelations(): dict> { 56 | return $this->linkRelations; 57 | } 58 | 59 | public function getLogRef(): int { 60 | return $this->logRefCode; 61 | } 62 | 63 | public function setPath(UriInterface $uri): void { 64 | $this->uri = $uri; 65 | } 66 | 67 | public function getPath(): ?UriInterface { 68 | return $this->uri; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Exception/ExceptionHandleInterface.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | interface ExceptionHandleInterface { 19 | 20 | public function handle(\Exception $e): void; 21 | } 22 | -------------------------------------------------------------------------------- /src/Exception/ExceptionHandler.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | use type Nazg\Http\VndErrorResponse; 19 | use type Nazg\HttpExecutor\Emitter\EmitterInterface; 20 | use type Facebook\Experimental\Http\Message\ResponseInterface; 21 | use type Facebook\Experimental\Http\Message\UriInterface; 22 | use type Ytake\Hungrr\StatusCode; 23 | use namespace HH\Lib\{Dict, C, IO}; 24 | use function get_class; 25 | use function is_array; 26 | use function json_encode; 27 | 28 | class ExceptionHandler implements ExceptionHandleInterface { 29 | 30 | public function __construct( 31 | protected IO\ReadHandle $readHandle, 32 | protected IO\WriteHandle $writeHandle, 33 | protected EmitterInterface $emitter 34 | ) {} 35 | 36 | protected async function renderAsync( 37 | dict $em, 38 | \Exception $e 39 | ): Awaitable { 40 | await $this->writeHandle->writeAsync(json_encode($em)); 41 | return new VndErrorResponse( 42 | $this->writeHandle, 43 | $this->resolveStatusCode($e->getCode()) 44 | ); 45 | } 46 | 47 | protected function respond( 48 | dict $em, 49 | \Exception $e 50 | ): void { 51 | $this->emitter->emit( 52 | $this->readHandle, 53 | \HH\Asio\join($this->renderAsync($em, $e)) 54 | ); 55 | } 56 | 57 | public function handle(\Exception $e): void { 58 | $this->respond($this->resolveError($e), $e); 59 | } 60 | 61 | protected function resolveError(\Exception $e): dict { 62 | $shape = shape(); 63 | $factory = new VndErrorFactory($e); 64 | if($e is AbstractVndErrorException) { 65 | $shape['logref'] = $e->getLogRef(); 66 | $path = $e->getPath(); 67 | if($path is UriInterface) { 68 | $shape['path'] = $path->toString(); 69 | } 70 | } 71 | return $factory->invoke($this->dictErrors($e), $shape)->toDict(); 72 | } 73 | 74 | protected function dictErrors(\Exception $e): dict { 75 | return dict[ 76 | 'exception' => get_class($e), 77 | 'file' => $e->getFile(), 78 | 'line' => $e->getLine(), 79 | 'trace' => Dict\map($e->getTrace(), ($v) ==> { 80 | if(is_array($v)) { 81 | if(C\contains_key($v, 'args')) { 82 | return Dict\filter_with_key($v, ($k, $_) ==> $k !== 'args'); 83 | } 84 | } 85 | return $v; 86 | }), 87 | ]; 88 | } 89 | 90 | protected function resolveStatusCode( 91 | mixed $exceptionCode 92 | ): StatusCode { 93 | $exceptionCode as int; 94 | try { 95 | return StatusCode::assert($exceptionCode); 96 | } catch(\UnexpectedValueException $e) { 97 | return StatusCode::INTERNAL_SERVER_ERROR; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Exception/ExceptionHandlerProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | use type Nazg\Glue\Container; 19 | use type Nazg\Glue\ProviderInterface; 20 | use type Nazg\HttpExecutor\Emitter\EmitterInterface; 21 | use namespace HH\Lib\IO; 22 | 23 | class ExceptionHandlerProvider implements ProviderInterface { 24 | 25 | public function __construct( 26 | protected IO\ReadHandle $readHandle, 27 | protected IO\WriteHandle $writeHandle, 28 | protected EmitterInterface $emitter 29 | ) {} 30 | 31 | public function get( 32 | Container $_container 33 | ): ExceptionHandleInterface { 34 | return new ExceptionHandler( 35 | $this->readHandle, 36 | $this->writeHandle, 37 | $this->emitter 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Exception/ExceptionRegister.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | use type Nazg\Foundation\Bootstrap\BootstrapRegisterInterface; 19 | 20 | use function set_exception_handler; 21 | 22 | class ExceptionRegister implements BootstrapRegisterInterface { 23 | 24 | public function __construct( 25 | protected ExceptionHandleInterface $handler 26 | ) {} 27 | 28 | public function register(): void { 29 | set_exception_handler(vec[$this->handler, 'handle']); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Exception/ExceptionRegisterProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | use type Nazg\Glue\Container; 19 | use type Nazg\Glue\ProviderInterface; 20 | 21 | class ExceptionRegisterProvider implements ProviderInterface { 22 | 23 | public function get( 24 | Container $container 25 | ): ExceptionRegister { 26 | return new ExceptionRegister( 27 | $container->get(ExceptionHandleInterface::class) 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Exception/NotFoundHttpException.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | use type Ytake\Hungrr\StatusCode; 19 | 20 | final class NotFoundHttpException extends AbstractVndErrorException { 21 | 22 | private int $logRefCode = StatusCode::NOT_FOUND; 23 | } 24 | -------------------------------------------------------------------------------- /src/Exception/VndErrorExceptionInterface.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | use type Ytake\Hhypermedia\Error\LinkRelation; 19 | use type Ytake\Hhypermedia\LinkResource; 20 | use type Facebook\Experimental\Http\Message\UriInterface; 21 | 22 | interface VndErrorExceptionInterface { 23 | 24 | /** 25 | * Link attributes follow the same semantics as defined in the HAL specification 26 | * 27 | * @see https://github.com/blongden/vnd.error#link-attributes 28 | */ 29 | public function setRelations( 30 | dict> $linkRelations 31 | ): void; 32 | 33 | /** 34 | * For expressing a identifier to refer to the specific error on the server side 35 | * for logging purposes (i.e. a request number). 36 | * 37 | * @see https://github.com/blongden/vnd.error#logref 38 | */ 39 | public function setLogRef( 40 | int $logRefCode 41 | ): void; 42 | 43 | public function getRelations(): dict>; 44 | 45 | public function getLogRef(): ?int; 46 | 47 | public function setPath(UriInterface $uri): void; 48 | 49 | public function getPath(): ?UriInterface; 50 | } 51 | -------------------------------------------------------------------------------- /src/Exception/VndErrorFactory.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Exception; 17 | 18 | use type Ytake\Hhypermedia\Serializer; 19 | use type Ytake\Hhypermedia\Error\ErrorLink; 20 | use type Ytake\Hhypermedia\Error\MessageResource; 21 | use type Ytake\Hhypermedia\ResourceObject; 22 | use type Ytake\Hhypermedia\Serializer\VndErrorSerializer; 23 | use type Ytake\Hhypermedia\ErrorAttributes; 24 | use type Ytake\Hhypermedia\Visitor\JsonSerializationVisitor; 25 | 26 | final class VndErrorFactory { 27 | 28 | public function __construct( 29 | private \Exception $throw 30 | ) {} 31 | 32 | public function invoke( 33 | dict $optionalAttributes = dict[], 34 | ErrorAttributes $errorAttributes = shape() 35 | ): Serializer { 36 | $resourceObject = new ResourceObject(); 37 | $throw = $this->throw; 38 | if($throw is AbstractVndErrorException) { 39 | foreach($throw->getRelations() as $name => $relation) { 40 | $resourceObject = $resourceObject->withLink(new ErrorLink($name, $relation)); 41 | } 42 | } 43 | return new Serializer( 44 | new VndErrorSerializer(), 45 | new MessageResource( 46 | $throw->getMessage(), 47 | $resourceObject, 48 | $errorAttributes, 49 | $optionalAttributes 50 | ), 51 | new JsonSerializationVisitor( 52 | \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES| \JSON_PRESERVE_ZERO_FRACTION 53 | ) 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Foundation/Application.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation; 17 | 18 | use type Nazg\Heredity\AsyncHeredity; 19 | use type Nazg\Heredity\AsyncMiddlewareStack; 20 | use type Nazg\RequestHandler\AsyncFallbackHandler; 21 | use type Nazg\Foundation\Bootstrap\BootstrapRegister; 22 | use type Facebook\HackRouter\BaseRouter; 23 | use type Facebook\Experimental\Http\Message\ServerRequestInterface; 24 | use type Nazg\Http\Server\AsyncRequestHandlerInterface; 25 | use type Nazg\Glue\Container; 26 | use type Nazg\HttpExecutor\AsyncRequestHandleExecutor; 27 | 28 | use namespace Nazg\HttpExecutor\Emitter; 29 | use namespace HH\Lib\IO; 30 | use namespace Nazg\Middleware; 31 | use namespace HH\Lib\Vec; 32 | 33 | <<__ConsistentConstruct>> 34 | class Application { 35 | 36 | protected vec> $middlewares = vec[ 37 | 38 | ]; 39 | 40 | protected vec> $appProviders = vec[ 41 | \Nazg\Cache\AggregateCacheServiceProvider::class, 42 | ]; 43 | 44 | protected ?AsyncRequestHandlerInterface $requestHandler; 45 | 46 | protected ?BootstrapRegister $bootstrapRegister; 47 | 48 | protected bool $attributeValidation = false; 49 | 50 | public function __construct( 51 | private Container $container, 52 | private IO\ReadHandle $readHandle, 53 | private IO\CloseableWriteHandle $writeHandle, 54 | ) {} 55 | 56 | public function build(ApplicationConfig $config): this { 57 | $provider = new ApplicationProvider( 58 | $this->container, 59 | $config, 60 | $this->readHandle, 61 | $this->writeHandle 62 | ); 63 | $provider->apply(); 64 | $this->middlewares = Vec\concat( 65 | $this->middlewares, 66 | $config->getApplicationGlobalMiddlewares() 67 | ); 68 | $this->registerDependency($config); 69 | \HH\Asio\join($this->container->lockAsync()); 70 | return $this; 71 | } 72 | 73 | protected function registerDependency(ApplicationConfig $config): void { 74 | $providers = $config->getServiceProviders(); 75 | foreach(Vec\concat($this->appProviders, $providers) as $provider) { 76 | (new $provider($this->container))->apply(); 77 | } 78 | } 79 | 80 | public async function runAsync( 81 | ServerRequestInterface $serverRequest 82 | ): Awaitable { 83 | // register bootstrap for framework application 84 | $this->bootstrap($this->container); 85 | $router = $this->container->get(BaseRouter::class); 86 | list($middleware, $attributes) = $router->routeRequest($serverRequest); 87 | if ($attributes->count()) { 88 | $serverRequest = $serverRequest->withServerParams(dict($attributes)); 89 | } 90 | await $this->executor( 91 | $this->middlewareProcessor( 92 | $middleware['middleware'], 93 | $this->container 94 | ), 95 | $this->container->get(Emitter\EmitterInterface::class), 96 | $serverRequest 97 | )->runAsync(); 98 | } 99 | 100 | protected function executor( 101 | AsyncRequestHandlerInterface $handler, 102 | Emitter\EmitterInterface $emitter, 103 | ServerRequestInterface $serverRequest 104 | ): AsyncRequestHandleExecutor { 105 | return new AsyncRequestHandleExecutor( 106 | $this->readHandle, 107 | $this->writeHandle, 108 | $handler, 109 | $emitter, 110 | $serverRequest 111 | ); 112 | } 113 | 114 | private function bootstrap(Container $container): void { 115 | $bootstrap = $this->bootstrapRegister ?: new BootstrapRegister($container); 116 | $bootstrap->register(); 117 | } 118 | 119 | public function setBootstrap(BootstrapRegister $br): void { 120 | $this->bootstrapRegister = $br; 121 | } 122 | 123 | public function setRequestHandler( 124 | AsyncRequestHandlerInterface $handler 125 | ): void { 126 | $this->requestHandler = $handler; 127 | } 128 | 129 | /** 130 | * Middleware always executed by the application 131 | * must override application class 132 | * 133 | * 134 | * <<__Override>> 135 | * protected function middleware(): vec<\Nazg\Types\TMiddlewareClass> { 136 | * return vec[]; 137 | * } 138 | * 139 | */ 140 | protected function getAppMiddleware(): vec> { 141 | return vec[]; 142 | } 143 | 144 | public function setValidateAttribute(bool $attributeValidation): void { 145 | $this->attributeValidation = $attributeValidation; 146 | } 147 | 148 | protected function middlewareProcessor( 149 | vec> $middleware, 150 | Container $container, 151 | ): AsyncRequestHandlerInterface { 152 | // sync middleware 153 | $appMiddleware = Vec\concat( 154 | $this->middlewares, 155 | $this->getAppMiddleware(), 156 | $middleware 157 | ); 158 | $stack = new AsyncMiddlewareStack( 159 | $appMiddleware, 160 | new Middleware\GlueResolver($container), 161 | ); 162 | if ($this->attributeValidation) { 163 | $dispatcher = new Middleware\Dispatcher($stack, $this->getRequestHandler()); 164 | $dispatcher->setContainer($container); 165 | return $dispatcher; 166 | } 167 | return new AsyncHeredity($stack, $this->getRequestHandler()); 168 | } 169 | 170 | public function getContainer(): Container { 171 | return $this->container; 172 | } 173 | 174 | public function getRequestHandler(): AsyncRequestHandlerInterface { 175 | return $this->requestHandler ?: new AsyncFallbackHandler(); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Foundation/ApplicationConfig.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation; 17 | 18 | use type Nazg\Http\Server\AsyncMiddlewareInterface; 19 | use type Facebook\Experimental\Http\Message\HTTPMethod; 20 | use namespace Nazg\Cache; 21 | 22 | /** 23 | * Framework Configure Class 24 | */ 25 | <<__ConsistentConstruct>> 26 | class ApplicationConfig implements ConfigInterface { 27 | 28 | use Cache\CacheConfigTrait, 29 | GlueConfigTrait; 30 | 31 | protected dict> $routes = dict[]; 32 | protected shape('logfile' => string, 'logname' => string) $logfile = shape( 33 | 'logfile' => '', 34 | 'logname' => 'nazg' 35 | ); 36 | protected vec> $providers = vec[]; 37 | protected vec> $middlewares = vec[]; 38 | 39 | public async function setRoutesAsync( 40 | dict> $routes 41 | ): Awaitable { 42 | $this->routes = $routes; 43 | } 44 | 45 | public function getRoutes(): dict> { 46 | return $this->routes; 47 | } 48 | 49 | public async function setLogConfigAsync(shape('logfile' => string, 'logname' => string) $logConfig): Awaitable { 50 | $this->logfile = $logConfig; 51 | } 52 | 53 | public function getLogConfig(): shape('logfile' => string, 'logname' => string) { 54 | return $this->logfile; 55 | } 56 | 57 | public async function setServiceProvidersAsync( 58 | vec> $providers 59 | ): Awaitable { 60 | $this->providers = $providers; 61 | } 62 | 63 | public function getServiceProviders(): vec> { 64 | return $this->providers; 65 | } 66 | 67 | public function setApplicationGlobalMiddlewares( 68 | vec> $middlewares 69 | ): void { 70 | $this->middlewares = $middlewares; 71 | } 72 | 73 | public function getApplicationGlobalMiddlewares(): vec> { 74 | return $this->middlewares; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Foundation/ApplicationConfigProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation; 17 | 18 | use type Nazg\Glue\Container; 19 | use type Nazg\Glue\ProviderInterface; 20 | 21 | final class ApplicationConfigProvider implements ProviderInterface { 22 | 23 | public function __construct( 24 | protected ApplicationConfig $config 25 | ) {} 26 | 27 | public function get( 28 | Container $_ 29 | ): ApplicationConfig { 30 | return $this->config; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Foundation/ApplicationProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation; 17 | 18 | use type Nazg\Glue\{Container, Scope}; 19 | use type Nazg\Routing\RouterProvider; 20 | use type Facebook\HackRouter\BaseRouter; 21 | use type Nazg\Emitter\EmitterProvider; 22 | use type HackLogging\Logger; 23 | use namespace Nazg\Logger; 24 | use namespace Nazg\Exception; 25 | use namespace Nazg\HttpExecutor\Emitter; 26 | use namespace HH\Lib\IO; 27 | 28 | final class ApplicationProvider extends ServiceProvider { 29 | 30 | public function __construct( 31 | protected Container $container, 32 | private ApplicationConfig $config, 33 | protected IO\ReadHandle $readHandle, 34 | protected IO\WriteHandle $writeHandle, 35 | ) { 36 | parent::__construct($container); 37 | } 38 | 39 | <<__Override>> 40 | public function apply(): void { 41 | // 42 | $this->container 43 | ->bind(Emitter\EmitterInterface::class) 44 | ->provider(new EmitterProvider()); 45 | // 46 | $this->container 47 | ->bind(Exception\ExceptionHandleInterface::class) 48 | ->provider(new Exception\ExceptionHandlerProvider( 49 | $this->readHandle, 50 | $this->writeHandle, 51 | $this->container->get(Emitter\EmitterInterface::class)) 52 | ); 53 | $this->container 54 | ->bind(Exception\ExceptionRegister::class) 55 | ->provider(new Exception\ExceptionRegisterProvider()); 56 | // 57 | $this->container 58 | ->bind(ApplicationConfig::class) 59 | ->provider(new ApplicationConfigProvider($this->config)) 60 | ->in(Scope::SINGLETON); 61 | // router 62 | $this->container 63 | ->bind(BaseRouter::class) 64 | ->provider(new RouterProvider()); 65 | $this->container 66 | ->bind(Logger::class) 67 | ->provider(new Logger\LoggerProvider()) 68 | ->in(Scope::SINGLETON); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Foundation/Bootstrap/BootstrapRegister.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation\Bootstrap; 17 | 18 | use type Nazg\Glue\Container; 19 | 20 | class BootstrapRegister implements BootstrapRegisterInterface { 21 | 22 | protected vec> 23 | $vecBootstrapper = vec[\Nazg\Exception\ExceptionRegister::class]; 24 | 25 | public function __construct( 26 | protected Container $container 27 | ) {} 28 | 29 | public function register(): void { 30 | foreach ($this->vecBootstrapper as $i) { 31 | if ($this->container->has($i)) { 32 | $this->container->get($i) 33 | |> $$->register(); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Foundation/Bootstrap/BootstrapRegisterInterface.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation\Bootstrap; 17 | 18 | interface BootstrapRegisterInterface { 19 | 20 | public function register(): void; 21 | } 22 | -------------------------------------------------------------------------------- /src/Foundation/Command/ApplicationCacheClear.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation\Command; 17 | 18 | use type Nazg\HCache\CacheProvider; 19 | use namespace Facebook\CLILib\CLIOptions; 20 | 21 | final class ApplicationCacheClear extends CliApplication { 22 | 23 | <<__Override>> 24 | public async function mainAsync(): Awaitable { 25 | $app = $this->getApplication(); 26 | $cache = $app->getContainer()->get(CacheProvider::class); 27 | $stdout = $this->getStdout(); 28 | if($cache->flushAll()) { 29 | await $stdout->writeAsync("Cache Clear"); 30 | return 0; 31 | } 32 | await $stdout->writeAsync("[ERROR] Failed to clear cache."); 33 | return 1; 34 | } 35 | 36 | <<__Override>> 37 | protected function getSupportedOptions(): vec { 38 | return vec[]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Foundation/Command/CliApplication.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation\Command; 17 | 18 | use type Nazg\Foundation\Application; 19 | use type Facebook\CLILib\CLIBase; 20 | 21 | abstract class CliApplication extends CLIBase { 22 | 23 | private static ?Application $app; 24 | 25 | final public static function setApplication( 26 | Application $app 27 | ): void { 28 | self::$app = $app; 29 | } 30 | 31 | public function getApplication(): Application { 32 | invariant(self::$app is Application, 'Could not resolve Application instance'); 33 | return self::$app; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Foundation/Command/ContainerCacheClear.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation\Command; 17 | 18 | use type Nazg\Foundation\ApplicationConfig; 19 | use namespace Facebook\CLILib\CLIOptions; 20 | use function apc_delete; 21 | 22 | final class ContainerCacheClear extends CliApplication { 23 | 24 | <<__Override>> 25 | public async function mainAsync(): Awaitable { 26 | $app = $this->getApplication(); 27 | $config = $app->getContainer()->get(ApplicationConfig::class); 28 | apc_delete($config->getContainerCacheKeyname()); 29 | $stdout = $this->getStdout(); 30 | await $stdout->writeAsync("Deleted Container Cache"); 31 | return 0; 32 | } 33 | 34 | <<__Override>> 35 | protected function getSupportedOptions(): vec { 36 | return vec[]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Foundation/ConfigInterface.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation; 17 | 18 | interface ConfigInterface { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Foundation/ConsistentServiceProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation; 17 | 18 | <<__ConsistentConstruct>> 19 | abstract class ConsistentServiceProvider extends ServiceProvider { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/Foundation/GlueConfigTrait.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation; 17 | 18 | trait GlueConfigTrait { 19 | require implements ConfigInterface; 20 | 21 | protected bool $enableContainerCache = false; 22 | protected string $containerCacheKeyname = 'nazg.framework.glue'; 23 | 24 | public function isContainerCache(): bool { 25 | return $this->enableContainerCache; 26 | } 27 | 28 | public function setContainerCacheKeyname(string $keyname): void { 29 | $this->containerCacheKeyname = $keyname; 30 | } 31 | 32 | public function getContainerCacheKeyname(): string { 33 | return $this->containerCacheKeyname; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Foundation/ServiceProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Foundation; 17 | 18 | use type Nazg\Glue\Container; 19 | 20 | abstract class ServiceProvider { 21 | 22 | public function __construct( 23 | protected Container $container 24 | ) {} 25 | 26 | abstract public function apply(): void; 27 | } 28 | -------------------------------------------------------------------------------- /src/Http/VndErrorResponse.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Http; 17 | 18 | use type Ytake\Hungrr\Response\JsonResponse; 19 | use type Ytake\Hungrr\StatusCode; 20 | use type Ytake\Hungrr\Response\InjectContentTypeTrait; 21 | use namespace HH\Lib\IO; 22 | 23 | final class VndErrorResponse extends JsonResponse { 24 | 25 | use InjectContentTypeTrait; 26 | 27 | public function __construct( 28 | private IO\WriteHandle $body, 29 | StatusCode $status = StatusCode::INTERNAL_SERVER_ERROR, 30 | dict> $headers = dict[], 31 | protected int $encodingOptions = self::DEFAULT_JSON_FLAGS 32 | ) { 33 | parent::__construct( 34 | $body, 35 | $status, 36 | /* HH_FIXME[3004] */ 37 | $this->injectContentType('application/vnd.error+json', $headers), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Logger/LoggerProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Logger; 17 | 18 | use type Nazg\Glue\Container; 19 | use type Nazg\Glue\ProviderInterface; 20 | use type Nazg\Foundation\ApplicationConfig; 21 | use type HackLogging\Logger; 22 | use type HackLogging\Handler\FilesystemHandler; 23 | use namespace HH\Lib\File; 24 | 25 | final class LoggerProvider implements ProviderInterface { 26 | 27 | public function get( 28 | Container $container 29 | ): Logger { 30 | $config = $container->get(ApplicationConfig::class); 31 | $logConfig = $config->getLogConfig(); 32 | return new Logger($logConfig['logname'], vec[ 33 | new FilesystemHandler(File\open_write_only($logConfig['logfile'])) 34 | ]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Middleware/Dispatcher.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Middleware; 17 | 18 | use type HH\Lib\IO\CloseableWriteHandle; 19 | use type Nazg\Heredity\AsyncHeredity; 20 | use type Nazg\Glue\Container; 21 | use type Nazg\Http\Server\AsyncMiddlewareInterface; 22 | use type Facebook\Experimental\Http\Message\ResponseInterface; 23 | use type Facebook\Experimental\Http\Message\ServerRequestInterface; 24 | use namespace Nazg\Validation; 25 | 26 | class Dispatcher extends AsyncHeredity { 27 | 28 | const string InterceptorMethod = 'process'; 29 | protected ?Container $container; 30 | 31 | <<__Override>> 32 | protected async function processorAsync( 33 | CloseableWriteHandle $writeHandle, 34 | AsyncMiddlewareInterface $middleware, 35 | ServerRequestInterface $request 36 | ): Awaitable { 37 | $this->validateInterceptor($middleware, $request); 38 | return await $middleware->processAsync($writeHandle, $request, $this); 39 | } 40 | 41 | protected function validateInterceptor( 42 | AsyncMiddlewareInterface $middleware, 43 | ServerRequestInterface $request, 44 | ): void { 45 | $container = $this->container; 46 | if ($container is nonnull) { 47 | $rm = new \ReflectionMethod($middleware, self::InterceptorMethod); 48 | $attribute = $rm->getAttributeClass(Validation\RequestValidation::class); 49 | if ($attribute is nonnull) { 50 | $container->get($attribute->validationClass) 51 | |> new Validation\ValidatorFactory($$, $request) 52 | |> $$->validator()->validate(); 53 | } 54 | } 55 | } 56 | 57 | public function setContainer(Container $container): void { 58 | $this->container = $container; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Middleware/GlueResolver.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Middleware; 17 | 18 | use namespace HH\Lib\Str; 19 | use type Nazg\Http\Server\AsyncMiddlewareInterface; 20 | use type Nazg\Heredity\Exception\MiddlewareResolvingException; 21 | use type Nazg\Heredity\Resolvable; 22 | use type Nazg\Glue\Container; 23 | 24 | class GlueResolver implements Resolvable { 25 | 26 | public function __construct( 27 | protected Container $container 28 | ) {} 29 | 30 | public function resolve( 31 | classname $middleware 32 | ): AsyncMiddlewareInterface { 33 | if ($this->container->has($middleware)) { 34 | return $this->container->get($middleware); 35 | } 36 | throw new MiddlewareResolvingException( 37 | Str\format('Identifier "%s" is not binding.', $middleware), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Middleware/LogExceptionMiddleware.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Middleware; 17 | 18 | use type HackLogging\Logger; 19 | use type HackLogging\LogLevel; 20 | use type HH\Lib\IO\CloseableWriteHandle; 21 | use type Facebook\Experimental\Http\Message\ResponseInterface; 22 | use type Facebook\Experimental\Http\Message\ServerRequestInterface; 23 | use type Nazg\Http\Server\AsyncMiddlewareInterface; 24 | use type Nazg\Http\Server\AsyncRequestHandlerInterface; 25 | 26 | class LogExceptionMiddleware implements AsyncMiddlewareInterface { 27 | 28 | public function __construct( 29 | protected Logger $log 30 | ) {} 31 | 32 | public async function processAsync( 33 | CloseableWriteHandle $writeHandle, 34 | ServerRequestInterface $request, 35 | AsyncRequestHandlerInterface $handler, 36 | ): Awaitable { 37 | try { 38 | return await $handler->handleAsync($writeHandle, $request); 39 | } catch (\Exception $e) { 40 | await $this->log->writeAsync( 41 | LogLevel::DEBUG, 42 | $e->getMessage(), 43 | dict[ 44 | 'file' => $e->getFile(), 45 | 'line' => $e->getLine(), 46 | 'trace' => $e->getTraceAsString(), 47 | ], 48 | ); 49 | throw $e; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Middleware/LogExceptionMiddlewareProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Middleware; 17 | 18 | use type HackLogging\Logger; 19 | use type Nazg\Glue\Container; 20 | use type Nazg\Glue\ProviderInterface; 21 | 22 | final class LogExceptionMiddlewareProvider implements ProviderInterface { 23 | 24 | public function get( 25 | Container $container 26 | ): LogExceptionMiddleware { 27 | return new LogExceptionMiddleware($container->get(Logger::class)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Middleware/SimpleCorsMiddleware.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2020 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Middleware; 17 | 18 | use type Facebook\HackRouter\HttpMethod; 19 | use type Facebook\Experimental\Http\Message\ResponseInterface; 20 | use type Facebook\Experimental\Http\Message\ServerRequestInterface; 21 | use type HH\Lib\IO\CloseableWriteHandle; 22 | use type Nazg\Http\Server\AsyncMiddlewareInterface; 23 | use type Nazg\Http\Server\AsyncRequestHandlerInterface; 24 | use function implode; 25 | 26 | type CorsSetting = shape( 27 | ?'origin' => string, 28 | ?'header' => string, 29 | 'methods' => Vector, 30 | ); 31 | 32 | enum AccessControl : string as string { 33 | AllowOrigin = 'Access-Control-Allow-Origin'; 34 | AllowHeaders = 'Access-Control-Allow-Headers'; 35 | AllowMethods = 'Access-Control-Allow-Methods'; 36 | } 37 | 38 | class SimpleCorsMiddleware implements AsyncMiddlewareInterface { 39 | 40 | protected string $allowOrigin = '*'; 41 | protected string 42 | $allowHeaders = 'X-Requested-With, Content-Type, Accept, Origin, Authorization'; 43 | protected Vector 44 | $allowMethods = Vector { 45 | HttpMethod::GET, 46 | HttpMethod::HEAD, 47 | HttpMethod::POST, 48 | }; 49 | 50 | public function __construct(protected CorsSetting $cors) {} 51 | 52 | public async function processAsync( 53 | CloseableWriteHandle $writeHandle, 54 | ServerRequestInterface $request, 55 | AsyncRequestHandlerInterface $handler, 56 | ): Awaitable { 57 | $response = await $handler->handleAsync($writeHandle, $request); 58 | $origin = ($this->cors['origin']) ?? $this->allowOrigin; 59 | $header = ($this->cors['header']) ?? $this->allowHeaders; 60 | $methods = 61 | ($this->cors['methods']->isEmpty()) 62 | ? $this->allowMethods 63 | : $this->cors['methods']; 64 | return 65 | $response->withHeader(AccessControl::AllowOrigin, vec[$origin]) 66 | ->withHeader(AccessControl::AllowHeaders, vec[$header]) 67 | ->withHeader( 68 | AccessControl::AllowMethods, 69 | vec[$this->implodeMethods($methods)], 70 | ); 71 | } 72 | 73 | <<__Rx>> 74 | protected function implodeMethods(Vector $methods): string { 75 | return implode(",", $methods); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Middleware/SimpleCorsMiddlewareProvider.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2020 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Middleware; 17 | 18 | use type Facebook\HackRouter\HttpMethod; 19 | use type Nazg\Glue\Container; 20 | use type Nazg\Glue\ProviderInterface; 21 | 22 | class SimpleCorsMiddlewareProvider implements ProviderInterface { 23 | 24 | public function get( 25 | Container $container 26 | ): SimpleCorsMiddleware { 27 | return new SimpleCorsMiddleware(shape( 28 | 'methods' => Vector { 29 | HttpMethod::GET, 30 | HttpMethod::HEAD, 31 | HttpMethod::POST, 32 | }, 33 | ) 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/RequestHandler/AsyncFallbackHandler.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\RequestHandler; 17 | 18 | use type HH\Lib\IO\WriteHandle; 19 | use type Facebook\Experimental\Http\Message\ResponseInterface; 20 | use type Facebook\Experimental\Http\Message\ServerRequestInterface; 21 | use type Nazg\Http\Server\AsyncRequestHandlerInterface; 22 | use type Ytake\Hungrr\StatusCode; 23 | use type Ytake\Hungrr\Response\JsonResponse; 24 | 25 | class AsyncFallbackHandler implements AsyncRequestHandlerInterface { 26 | 27 | public async function handleAsync( 28 | WriteHandle $wirteHandle, 29 | ServerRequestInterface $_ 30 | ): Awaitable { 31 | return new JsonResponse( 32 | $wirteHandle, 33 | StatusCode::NOT_FOUND 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Routing/Exception/NotFoundException.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Routing\Exception; 17 | 18 | use type Exception; 19 | 20 | final class NotFoundException extends Exception { 21 | 22 | public function __construct( 23 | string $message = 'Not Found', 24 | ?Exception $previous = null 25 | ) { 26 | parent::__construct($message, 404, $previous); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Routing/Router.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Routing; 17 | 18 | use namespace HH\Lib\Dict; 19 | use type Facebook\HackRouter\BaseRouter; 20 | use type Facebook\Experimental\Http\Message\HTTPMethod; 21 | 22 | final class Router extends BaseRouter { 23 | 24 | public function __construct( 25 | private dict> $routeMap 26 | ) {} 27 | 28 | <<__Override>> 29 | protected function getRoutes( 30 | ): ImmMap<\Facebook\HackRouter\HttpMethod, ImmMap> { 31 | return new ImmMap($this->dictRoutes()); 32 | } 33 | 34 | <<__Rx>> 35 | protected function dictRoutes(): dict<\Facebook\HackRouter\HttpMethod, ImmMap> { 36 | return Dict\map_keys($this->routeMap, ($k) ==> { 37 | return $this->convertHttpMethod($k); 38 | }); 39 | } 40 | 41 | public function findRoute(string $named): string { 42 | $collect = $this->collectRoutes(); 43 | if($collect->contains($named)) { 44 | return $collect->at($named); 45 | } 46 | throw new Exception\NotFoundException(); 47 | } 48 | 49 | <<__Memoize>> 50 | protected function collectRoutes(): ImmMap { 51 | $map = new Map($this->routeMap); 52 | $i = $map->getIterator(); 53 | $named = dict[]; 54 | while ($i->valid()) { 55 | $current = $i->current(); 56 | $keys = $current->keys(); 57 | $index = 0; 58 | foreach($current as $method => $v) { 59 | if(Shapes::keyExists($v, 'named')) { 60 | $named[Shapes::idx($v, 'named', '')] = $keys[$index]; 61 | } 62 | $index++; 63 | } 64 | $i->next(); 65 | } 66 | return new ImmMap($named); 67 | } 68 | 69 | <<__Memoize, __Rx>> 70 | private function convertHttpMethod( 71 | HTTPMethod $method, 72 | ): \Facebook\HackRouter\HttpMethod { 73 | return \Facebook\HackRouter\HttpMethod::assert( 74 | HTTPMethod::assert($method) 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Routing/RouterProvider.hack: -------------------------------------------------------------------------------- 1 | namespace Nazg\Routing; 2 | 3 | use type Nazg\Glue\Container; 4 | use type Nazg\Glue\ProviderInterface; 5 | use type Nazg\Foundation\ApplicationConfig; 6 | use type Facebook\HackRouter\BaseRouter; 7 | use type Facebook\Experimental\Http\Message\HTTPMethod; 8 | 9 | final class RouterProvider 10 | implements ProviderInterface> { 11 | 12 | public function get(Container $container): BaseRouter<\Nazg\Routing\TResponder> { 13 | return new Router($this->resolveRoutes($container)); 14 | } 15 | 16 | protected function resolveRoutes( 17 | Container $container 18 | ): dict> { 19 | $config = $container->get(ApplicationConfig::class); 20 | return $config->getRoutes(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Routing/types.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Routing; 17 | 18 | use type Nazg\Http\Server\AsyncMiddlewareInterface; 19 | 20 | type MiddlewareVector = vec>; 21 | type TResponder = shape( 22 | 'middleware' => MiddlewareVector, 23 | ?'named' => string, 24 | ); 25 | -------------------------------------------------------------------------------- /src/Validation/RequestValidation.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2019 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Validation; 17 | 18 | final class RequestValidation implements \HH\MethodAttribute { 19 | 20 | public function __construct( 21 | public classname $validationClass, 22 | ) {} 23 | } 24 | -------------------------------------------------------------------------------- /src/Validation/ValidationException.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Validation; 17 | 18 | use type Exception; 19 | use type Ytake\Hungrr\StatusCode; 20 | use type Nazg\Exception\AbstractVndErrorException; 21 | 22 | class ValidationException extends AbstractVndErrorException { 23 | 24 | private int $logRefCode = StatusCode::BAD_REQUEST; 25 | 26 | public function __construct( 27 | protected Validator $validator, 28 | int $code = StatusCode::BAD_REQUEST, 29 | protected ?Exception $previous = null, 30 | ) { 31 | parent::__construct('The given data was invalid.', $code, $previous); 32 | $this->validator = $validator; 33 | } 34 | 35 | public function errors(): vec { 36 | return $this->validator->errors(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Validation/Validator.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Validation; 17 | 18 | use type Facebook\TypeAssert\IncorrectTypeException; 19 | use type Facebook\Experimental\Http\Message\ServerRequestInterface; 20 | 21 | abstract class Validator { 22 | 23 | protected ?ServerRequestInterface $request; 24 | 25 | protected vec $messages = vec[]; 26 | 27 | protected vec $validateMethods = vec[]; 28 | 29 | protected bool $shouldThrowException = false; 30 | 31 | // disabled type assert for request parameters 32 | protected bool $skipValidateStructure = true; 33 | 34 | public function validateRequest( 35 | ServerRequestInterface $request 36 | ): void { 37 | $this->request = $request; 38 | } 39 | 40 | public function validate(): bool { 41 | if ($this->request is nonnull) { 42 | try { 43 | $this->assertStructure(); 44 | } catch(IncorrectTypeException $e) { 45 | throw new ValidationException($this); 46 | } 47 | } 48 | if ($this->errors()) { 49 | if ($this->shouldThrowException) { 50 | throw new ValidationException($this); 51 | } 52 | return false; 53 | } 54 | return true; 55 | } 56 | 57 | <<__Memoize>> 58 | public function errors(): vec { 59 | return $this->assertValidateResult(); 60 | } 61 | 62 | protected function assertStructure(): void { 63 | if (!$this->skipValidateStructure) { 64 | // here 65 | } 66 | } 67 | 68 | abstract protected function assertValidateResult(): vec; 69 | } 70 | -------------------------------------------------------------------------------- /src/Validation/ValidatorFactory.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | * THE SOFTWARE. 9 | * 10 | * This software consists of voluntary contributions made by many individuals 11 | * and is licensed under the MIT license. 12 | * 13 | * Copyright (c) 2017-2018 Yuuki Takezawa 14 | * 15 | */ 16 | namespace Nazg\Validation; 17 | 18 | use type Facebook\Experimental\Http\Message\ServerRequestInterface; 19 | 20 | class ValidatorFactory { 21 | 22 | public function __construct( 23 | protected Validator $validatorName, 24 | protected ServerRequestInterface $request, 25 | ) {} 26 | 27 | public function validator(): Validator { 28 | $validator = $this->validatorName; 29 | $validator->validateRequest($this->request); 30 | return $validator; 31 | } 32 | } 33 | --------------------------------------------------------------------------------