├── .codeclimate.yml ├── LICENSE.md ├── README.md ├── build ├── split-notags.sh └── split.sh ├── composer.json ├── src ├── Backpack │ ├── AppBuilder.php │ ├── Bedrock │ │ ├── AbstractAppBuilder.php │ │ ├── AppRoutesMapper.php │ │ └── HttpAppBuilder.php │ ├── Bootstrap │ │ ├── PhpSettings.php │ │ ├── Routing.php │ │ └── Session.php │ ├── Config │ │ └── ConfigLoader.php │ ├── ConfigLoader.php │ └── Http │ │ └── Request.php ├── Bedrock │ ├── AbstractApplication.php │ ├── AbstractBootstrapper.php │ ├── Bootstrap │ │ ├── BootableResolver.php │ │ ├── Bootstrap.php │ │ ├── CallableProcess.php │ │ └── Exception │ │ │ └── InvalidBootableProcessException.php │ ├── Cli │ │ ├── Application.php │ │ └── Exception │ │ │ └── InvalidCommandException.php │ ├── Http │ │ ├── Application.php │ │ └── GroupManager.php │ ├── Kernel.php │ ├── README.md │ └── composer.json ├── Blueprint │ ├── Backpack │ │ ├── AppBuilderBase.php │ │ ├── CliAppBuilder.php │ │ └── HttpAppBuilder.php │ ├── Bedrock │ │ ├── Application.php │ │ ├── CliApplication.php │ │ ├── HttpApplication.php │ │ └── Kernel.php │ ├── Collection │ │ ├── Collection.php │ │ ├── Dictionary.php │ │ ├── ObjectAccess.php │ │ └── Structure.php │ ├── Common │ │ ├── Arrayable.php │ │ ├── Bootable.php │ │ ├── Initializable.php │ │ ├── Outputable.php │ │ ├── Renderable.php │ │ ├── ResourceLoader.php │ │ ├── ResourceProcessor.php │ │ ├── ResourceResolver.php │ │ └── Validator.php │ ├── Config │ │ ├── Config.php │ │ ├── ConfigException.php │ │ ├── ConfigFactory.php │ │ └── Stream.php │ ├── Http │ │ ├── ResponseEmitter.php │ │ ├── ResponseMiddleware.php │ │ ├── Route.php │ │ └── Stack.php │ ├── README.md │ └── composer.json ├── Collection │ ├── Collection.php │ ├── CollectionFlattener.php │ ├── DotNotationCollection.php │ ├── Exception │ │ └── ImmutableException.php │ ├── ImmutablePropertiesBag.php │ ├── PropertiesBag.php │ ├── README.md │ ├── Structure │ │ ├── AbstractImmutableStructure.php │ │ ├── AbstractStructure.php │ │ ├── DataType.php │ │ └── Exception │ │ │ ├── InvalidPropertyDefinitionException.php │ │ │ ├── InvalidPropertyTypeException.php │ │ │ ├── InvalidStructureException.php │ │ │ └── UndefinedPropertyException.php │ └── composer.json ├── Common │ ├── Chrono │ │ ├── Chrono.php │ │ └── Step.php │ ├── PhpIni.php │ ├── README.md │ ├── Traits │ │ ├── ArrayMergeRecursiveDistinct.php │ │ ├── LoadArrayFiles.php │ │ ├── Macro.php │ │ ├── MicroTime.php │ │ └── UpdateToCamelCase.php │ ├── composer.json │ └── helpers.php ├── Config │ ├── Cache │ │ └── FileCache.php │ ├── Config.php │ ├── ConfigCacheFactory.php │ ├── ConfigFactory.php │ ├── ConfigResolver.php │ ├── Exception │ │ ├── CacheInvalidKeyException.php │ │ ├── CachePathNotFoundException.php │ │ ├── CachePathNotWritableException.php │ │ ├── FileNotFoundException.php │ │ ├── FileNotReadableException.php │ │ ├── LoaderException.php │ │ ├── NoFileHandlersException.php │ │ ├── ProcessorException.php │ │ ├── ProcessorTypeException.php │ │ └── UnknownResourceException.php │ ├── FilesHandlers.php │ ├── Loader │ │ ├── DefaultLoader.php │ │ ├── PhpLoader.php │ │ └── TextLoader.php │ ├── Processor │ │ ├── ArrayProcessor.php │ │ ├── CallableProcessor.php │ │ ├── CollectionProcessor.php │ │ ├── ConfigProcessor.php │ │ ├── EnvProcessor.php │ │ ├── IniProcessor.php │ │ ├── JsonProcessor.php │ │ ├── StdClassProcessor.php │ │ ├── XmlProcessor.php │ │ └── YamlProcessor.php │ ├── README.md │ ├── Stream │ │ ├── DataStream.php │ │ ├── FileStream.php │ │ ├── JsonStream.php │ │ └── XmlStream.php │ └── composer.json ├── Di │ ├── ArrayDefinition.php │ ├── Binding │ │ ├── AbstractBinding.php │ │ ├── BindingInterface.php │ │ ├── Factory.php │ │ ├── Prototype.php │ │ └── Singleton.php │ ├── BindingResolver.php │ ├── ClassInspector.php │ ├── ClassInstantiator.php │ ├── ClassResolver.php │ ├── Container.php │ ├── Exception │ │ ├── AmbiguousResolutionException.php │ │ ├── ClassDefinitionNotFoundException.php │ │ ├── ClassNotFoundException.php │ │ ├── InfiniteLoopResolutionException.php │ │ ├── InterfaceNotFoundException.php │ │ ├── InvalidDefinitionException.php │ │ └── MethodNotFoundException.php │ ├── ExplicitResolver.php │ ├── InterfaceResolver.php │ ├── README.md │ └── composer.json └── Http │ ├── Exception │ ├── BodyParserException.php │ ├── EmptyStackException.php │ ├── JsonBodyParserException.php │ └── StackEndedWithoutResponseException.php │ ├── Middleware │ ├── CallableMiddleware.php │ └── JsonBodyParserMiddleware.php │ ├── README.md │ ├── Request │ ├── BlankRequest.php │ ├── Exception │ │ ├── HandlerNotFoundException.php │ │ ├── InvalidHandlerException.php │ │ └── UnresolvableHandlerException.php │ ├── HandlerResolver.php │ ├── PreRoute.php │ ├── Route.php │ ├── RouteArgs.php │ ├── RouteExpression.php │ └── RouteServerRequest.php │ ├── Response │ └── Emitter.php │ ├── Stack.php │ ├── StackFactory.php │ └── composer.json └── supporters.txt /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | checks: 3 | argument-count: 4 | enabled: true 5 | config: 6 | threshold: 5 7 | complex-logic: 8 | enabled: true 9 | config: 10 | threshold: 4 11 | file-lines: 12 | enabled: true 13 | config: 14 | threshold: 300 15 | method-complexity: 16 | enabled: true 17 | config: 18 | threshold: 5 19 | method-count: 20 | enabled: true 21 | config: 22 | threshold: 40 23 | method-lines: 24 | enabled: true 25 | config: 26 | threshold: 30 27 | nested-control-flow: 28 | enabled: true 29 | config: 30 | threshold: 4 31 | return-statements: 32 | enabled: true 33 | config: 34 | threshold: 5 35 | similar-code: 36 | enabled: true 37 | config: 38 | threshold: #language-specific defaults. overrides affect all languages. 39 | identical-code: 40 | enabled: true 41 | config: 42 | threshold: #language-specific defaults. overrides affect all languages. 43 | plugins: 44 | rubocop: 45 | enabled: true 46 | eslint: 47 | enabled: true 48 | exclude_patterns: 49 | - "tests/" -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) François Lajoie 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Peak Peak 2 |

3 | Build status 4 | version 5 | 6 | 7 | Total Downloads 8 | License 9 |

10 | 11 | > **Note:** This repository contains the core code of the Peak framework. If you want an example on how to build an application using Peak, visit the main [Peak repository](https://github.com/peakphp/peak). 12 | 13 | ### What is Peak? 14 | 15 | Peak is a powerful, minimalist and flexible micro-framework, middleware centric based on PSR-7, PSR-11 and PSR-15. 16 | 17 | ### Installation 18 | 19 | ``$ composer require peak/framework`` 20 | 21 | ### Quick start project 22 | 23 | Check https://github.com/peakphp/peak for a quick start project structure. 24 | 25 | ### Documentation & Site 26 | 27 | Check https://peakframework.com 28 | 29 | -------------------------------------------------------------------------------- /build/split-notags.sh: -------------------------------------------------------------------------------- 1 | git subsplit init git@github.com:peakphp/framework.git 2 | git subsplit publish --no-tags --heads="master" src/Bedrock:git@github.com:peakphp/bedrock.git 3 | git subsplit publish --no-tags --heads="master" src/Blueprint:git@github.com:peakphp/blueprint.git 4 | git subsplit publish --no-tags --heads="master" src/Collection:git@github.com:peakphp/collection.git 5 | git subsplit publish --no-tags --heads="master" src/Common:git@github.com:peakphp/common.git 6 | git subsplit publish --no-tags --heads="master" src/Config:git@github.com:peakphp/config.git 7 | git subsplit publish --no-tags --heads="master" src/Di:git@github.com:peakphp/di.git 8 | git subsplit publish --no-tags --heads="master" src/Http:git@github.com:peakphp/http.git 9 | rm -rf .subsplit/ -------------------------------------------------------------------------------- /build/split.sh: -------------------------------------------------------------------------------- 1 | git subsplit init git@github.com:peakphp/framework.git 2 | git subsplit publish --heads="master" src/Bedrock:git@github.com:peakphp/bedrock.git 3 | git subsplit publish --heads="master" src/Blueprint:git@github.com:peakphp/blueprint.git 4 | git subsplit publish --heads="master" src/Collection:git@github.com:peakphp/collection.git 5 | git subsplit publish --heads="master" src/Common:git@github.com:peakphp/common.git 6 | git subsplit publish --heads="master" src/Config:git@github.com:peakphp/config.git 7 | git subsplit publish --heads="master" src/Di:git@github.com:peakphp/di.git 8 | git subsplit publish --heads="master" src/Http:git@github.com:peakphp/http.git 9 | rm -rf .subsplit/ -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peak/framework", 3 | "description": "The Peak Framework.", 4 | "keywords": ["framework", "peak", "middleware", "psr-7", "psr-11", "psr-15"], 5 | "license": "MIT", 6 | "homepage": "https://peakframework.com", 7 | "support": { 8 | "issues": "https://github.com/peakphp/framework/issues", 9 | "source": "https://github.com/peakphp/framework", 10 | "api": "http://api.peakframework.com/" 11 | }, 12 | "authors": [ 13 | { 14 | "name": "Francois Lajoie", 15 | "email": "francois@peakframework.com" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=7.2", 20 | "psr/container": "^1.0", 21 | "psr/log": "^1.1", 22 | "psr/http-message": "^1.0", 23 | "psr/http-server-handler": "^1.0", 24 | "psr/http-server-middleware": "^1.0", 25 | "psr/http-factory": "^1.0", 26 | "psr/simple-cache": "^1.0" 27 | }, 28 | "require-dev": { 29 | "phpunit/phpunit": "^8.5", 30 | "phpstan/phpstan": "^0.12", 31 | "symfony/yaml": "^4.4" 32 | }, 33 | "suggest": { 34 | "symfony/console": "Required to use Peak\\Bedrock\\Cli\\Application (^4.4).", 35 | "symfony/process": "Required to use Peak\\Bedrock\\Cli\\Application (^4.4).", 36 | "symfony/yaml": "Required to parse yaml file with Peak\\Config (^4.4)." 37 | }, 38 | "autoload": { 39 | "files": [ 40 | "src/Common/helpers.php" 41 | ], 42 | "psr-4": { 43 | "Peak\\": "src/" 44 | } 45 | }, 46 | "minimum-stability": "dev", 47 | "prefer-stable": true 48 | } 49 | -------------------------------------------------------------------------------- /src/Backpack/AppBuilder.php: -------------------------------------------------------------------------------- 1 | env = $env; 64 | return $this; 65 | } 66 | 67 | /** 68 | * @param ContainerInterface $container 69 | * @return $this 70 | */ 71 | public function setContainer(ContainerInterface $container) 72 | { 73 | $this->container = $container; 74 | return $this; 75 | } 76 | 77 | /** 78 | * @param $props 79 | * @return $this 80 | * @throws Exception 81 | */ 82 | public function setProps($props) 83 | { 84 | if (!is_array($props) && !$props instanceof Dictionary) { 85 | throw new Exception('Props must be an array or an instance of Peak\Blueprint\Collection\Dictionary. '.gettype($props).' given ...'); 86 | } elseif (is_array($props)) { 87 | $props = new DotNotationCollection($props); 88 | } 89 | 90 | $this->props = $props; 91 | return $this; 92 | } 93 | 94 | /** 95 | * @param Kernel $kernel 96 | * @return $this 97 | */ 98 | public function setKernel(Kernel $kernel) 99 | { 100 | $this->kernel = $kernel; 101 | return $this; 102 | } 103 | 104 | /** 105 | * @param string $kernelClass 106 | * @return $this 107 | */ 108 | public function setKernelClass(string $kernelClass) 109 | { 110 | $this->kernelClass = $kernelClass; 111 | return $this; 112 | } 113 | 114 | /** 115 | * @param string $appClass 116 | * @return $this 117 | */ 118 | public function setAppClass(string $appClass) 119 | { 120 | $this->appClass = $appClass; 121 | return $this; 122 | } 123 | 124 | /** 125 | * @param Closure $fn 126 | * @return $this 127 | */ 128 | public function executeAfterBuild(Closure $fn) 129 | { 130 | $this->afterBuild = $fn; 131 | return $this; 132 | } 133 | } -------------------------------------------------------------------------------- /src/Backpack/Bedrock/AppRoutesMapper.php: -------------------------------------------------------------------------------- 1 | inspectRecursive($app->getHandlers()); 21 | } 22 | 23 | /** 24 | * @param array $handlers 25 | * @return array 26 | */ 27 | private function inspectRecursive(array $handlers): array 28 | { 29 | $routes = []; 30 | foreach ($handlers as $handler) { 31 | $subRoutes = []; 32 | if ($handler instanceof Route && !($handler instanceof PreRoute)) { 33 | $route = [ 34 | 'method' => $handler->getMethod(), 35 | 'path' => $handler->getPath(), 36 | 'stack' => [], 37 | ]; 38 | $handlers = $handler->getHandlers(); 39 | foreach ($handlers as $h) { 40 | if (is_string($h)) { 41 | $route['stack'][] = $h; 42 | } elseif (is_object($h)) { 43 | $route['stack'][] = get_class($h); 44 | } 45 | } 46 | $routes[] = $route; 47 | $subRoutes = $this->inspectRecursive($handler->getHandlers()); 48 | } elseif (is_array($handler)) { 49 | $subRoutes = $this->inspectRecursive($handler); 50 | } elseif ($handler instanceof Stack) { 51 | $subRoutes = $this->inspectRecursive($handler->getHandlers()); 52 | } 53 | 54 | if (!empty($subRoutes)) { 55 | $routes = array_merge($subRoutes, $routes); 56 | } 57 | } 58 | 59 | //reorder routes by path 60 | array_multisort (array_column($routes, 'path'), SORT_ASC, $routes); 61 | return $routes; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Backpack/Bedrock/HttpAppBuilder.php: -------------------------------------------------------------------------------- 1 | handlerResolver = $handlerResolver; 41 | return $this; 42 | } 43 | 44 | /** 45 | * @param string|null $alias 46 | * @return $this 47 | */ 48 | public function addToContainerAfterBuild(string $alias = null) 49 | { 50 | $this->addToContainerAfterBuild = true; 51 | $this->aliasContainer = $alias; 52 | return $this; 53 | } 54 | 55 | /** 56 | * @return HttpApplication 57 | * @throws Exception 58 | */ 59 | public function build(): HttpApplication 60 | { 61 | return $this->internalBuild($this->appClass ?? Application::class); 62 | } 63 | 64 | /** 65 | * @param string $applicationClass 66 | * @return \Peak\Blueprint\Bedrock\HttpApplication 67 | * @throws \Exception 68 | */ 69 | private function internalBuild(string $applicationClass): \Peak\Blueprint\Bedrock\HttpApplication 70 | { 71 | $kernel = $this->kernel; 72 | if (!isset($kernel)) { 73 | $kernelClass = $this->kernelClass ?? \Peak\Bedrock\Kernel::class; 74 | $kernel = new $kernelClass( 75 | $this->env ?? 'prod', 76 | $this->container ?? new Container() 77 | ); 78 | } elseif (isset($this->container) || isset($this->env) || isset($this->kernelClass)) { 79 | $this->triggerKernelError(); 80 | } 81 | 82 | $app = new $applicationClass( 83 | $kernel, 84 | $this->handlerResolver ?? new HandlerResolver($kernel->getContainer()), 85 | $this->props ?? null 86 | ); 87 | 88 | if ($this->addToContainerAfterBuild) { 89 | $this->addToContainer($app); 90 | } 91 | 92 | if (isset($this->afterBuild) && is_callable($this->afterBuild)) { 93 | ($this->afterBuild)($app); 94 | } 95 | 96 | return $app; 97 | } 98 | 99 | /** 100 | * Trigger an error with arguments container and env when a kernel has been set previously 101 | */ 102 | private function triggerKernelError() 103 | { 104 | $msgErrorSuffix = 'setting will be ignored because Kernel had been set previously.'; 105 | if (isset($this->container)) { 106 | trigger_error('Container '.$msgErrorSuffix); 107 | } 108 | if (isset($this->env)) { 109 | trigger_error('Env '.$msgErrorSuffix); 110 | } 111 | if (isset($this->kernelClass)) { 112 | trigger_error('Kernel class '.$msgErrorSuffix); 113 | } 114 | } 115 | 116 | /** 117 | * @param \Peak\Blueprint\Bedrock\HttpApplication $app 118 | * @throws \Exception 119 | */ 120 | private function addToContainer(\Peak\Blueprint\Bedrock\HttpApplication $app) 121 | { 122 | $container = $app->getKernel()->getContainer(); 123 | if (!$container instanceof Container) { 124 | throw new Exception('Cannot add application instance to the container'); 125 | } 126 | $container->set($app); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/Backpack/Bootstrap/PhpSettings.php: -------------------------------------------------------------------------------- 1 | app = $app; 32 | $this->phpPropName = $phpPropName; 33 | } 34 | 35 | /** 36 | * Configure php setting on the fly via ini_set 37 | * @throws Exception 38 | */ 39 | public function boot() 40 | { 41 | new PhpIni($this->app->getProp($this->phpPropName, [])); 42 | } 43 | } -------------------------------------------------------------------------------- /src/Backpack/Bootstrap/Routing.php: -------------------------------------------------------------------------------- 1 | app = $app; 41 | $this->routesPropName = $routesPropName; 42 | $this->routesPathPrefixPropName = $routesPathPrefixPropName; 43 | } 44 | 45 | /** 46 | * Look for routes to register in application properties 47 | * @throws \Exception 48 | */ 49 | public function boot() 50 | { 51 | if ($this->app->getProps() === null) { 52 | return; 53 | } 54 | 55 | $routes = []; 56 | $pathPrefix = $this->app->getProp($this->routesPathPrefixPropName, ''); 57 | $propRoutes = $this->app->getProp($this->routesPropName, []); 58 | 59 | if (!is_array($propRoutes)) { 60 | throw new \Exception('Routes definitions must be an array!'); 61 | } 62 | 63 | foreach ($this->app->getProp($this->routesPropName, []) as $route) { 64 | $this->validate($route); 65 | $routes[] = $this->app->createRoute( 66 | $route['method'] ?? null, 67 | $pathPrefix.$route['path'], 68 | $route['stack'] 69 | ); 70 | } 71 | 72 | $this->app->stack($routes); 73 | } 74 | 75 | /** 76 | * @param mixed $route 77 | * @throws \Exception 78 | */ 79 | private function validate($route) 80 | { 81 | if (!is_array($route)) { 82 | throw new \Exception('Route definition must be an array!'); 83 | } elseif (!isset($route['path'])) { 84 | throw new \Exception('Route definition must have a path!'); 85 | } elseif (!isset($route['stack'])) { 86 | throw new \Exception('Route definition must have a stack!'); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/Backpack/Bootstrap/Session.php: -------------------------------------------------------------------------------- 1 | app = $app; 39 | $this->sessionPropName = $sessionPropName; 40 | } 41 | /** 42 | * Setup and start session from app props 43 | * @throws Exception 44 | */ 45 | public function boot() 46 | { 47 | if ((php_sapi_name() === 'cli' || defined('STDIN'))) { 48 | return; 49 | } 50 | 51 | if (session_status() == PHP_SESSION_ACTIVE) { 52 | throw new Exception('Session is already started'); 53 | } 54 | 55 | // save path 56 | if ($this->app->hasProp($this->sessionPropName.'.save_path')) { 57 | session_save_path($this->app->getProp($this->sessionPropName.'.save_path')); 58 | } 59 | 60 | // save handler class 61 | if ($this->app->hasProp($this->sessionPropName.'.save_handler')) { 62 | session_set_save_handler($this->app->getProp($this->sessionPropName.'.save_handler')); 63 | } 64 | 65 | // name the session 66 | if ($this->app->hasProp($this->sessionPropName.'.name')) { 67 | session_name($this->app->getProp($this->sessionPropName.'.name')); 68 | } 69 | 70 | // session options 71 | $options = []; 72 | if ($this->app->hasProp($this->sessionPropName.'.options')) { 73 | $options = $this->app->getProp($this->sessionPropName.'.options'); 74 | } 75 | 76 | // start the session 77 | session_start($options); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Backpack/Config/ConfigLoader.php: -------------------------------------------------------------------------------- 1 | cachePath)) { 50 | trigger_error('Cache configurations will be ignored because ConfigFactory have been set.'); 51 | } 52 | $this->configFactory = $configFactory; 53 | return $this; 54 | } 55 | 56 | /** 57 | * @param string $path 58 | * @param string $uid 59 | * @param int $ttlInSec 60 | * @param CacheInterface|null $driver 61 | * @return $this 62 | */ 63 | public function setCache(string $path, string $uid, int $ttlInSec, CacheInterface $driver = null) 64 | { 65 | if (isset($this->configFactory)) { 66 | trigger_error('Cache configurations will be ignored because ConfigFactory have been set.'); 67 | } 68 | $this->cachePath = $path; 69 | $this->cacheId = $uid; 70 | $this->cacheTtl = $ttlInSec; 71 | $this->cacheDriver = $driver; 72 | return $this; 73 | } 74 | 75 | /** 76 | * @param mixed $resources 77 | * @return mixed|Config|\Peak\Config\Config 78 | * @throws \Peak\Config\Exception\CachePathNotFoundException 79 | * @throws \Peak\Config\Exception\CachePathNotWritableException 80 | * @throws \Peak\Config\Exception\UnknownResourceException 81 | * @throws \Psr\SimpleCache\InvalidArgumentException 82 | */ 83 | public function load($resources) 84 | { 85 | return $this->loadConfig($resources); 86 | } 87 | 88 | /** 89 | * @param array $resources 90 | * @param Config $config 91 | * @return Config|\Peak\Config\Config 92 | * @throws \Peak\Config\Exception\CachePathNotFoundException 93 | * @throws \Peak\Config\Exception\CachePathNotWritableException 94 | * @throws \Peak\Config\Exception\UnknownResourceException 95 | * @throws \Psr\SimpleCache\InvalidArgumentException 96 | */ 97 | public function loadWith(array $resources, Config $config) 98 | { 99 | // todo to remove 100 | return $this->loadConfig($resources, $config); 101 | } 102 | 103 | /** 104 | * @param array $resources 105 | * @param Config|null $config 106 | * @return Config 107 | * @throws \Peak\Config\Exception\CachePathNotFoundException 108 | * @throws \Peak\Config\Exception\CachePathNotWritableException 109 | * @throws \Peak\Config\Exception\UnknownResourceException 110 | * @throws \Psr\SimpleCache\InvalidArgumentException 111 | */ 112 | private function loadConfig(array $resources, \Peak\Blueprint\Config\Config $config = null) 113 | { 114 | $configFactory = $this->configFactory; 115 | 116 | if (!isset($configFactory)) { 117 | if (!isset($this->cachePath)) { 118 | $configFactory = new \Peak\Config\ConfigFactory(); 119 | } else { 120 | $configFactory = new ConfigCacheFactory( 121 | (string) $this->cacheId, 122 | (int) $this->cacheTtl, 123 | new \Peak\Config\ConfigFactory(), 124 | $this->cacheDriver ?? new FileCache($this->cachePath) 125 | ); 126 | } 127 | } 128 | 129 | if (!isset($config)) { 130 | $config = new \Peak\Config\Config(); 131 | } 132 | 133 | return $configFactory->loadResourcesWith($resources, $config); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Backpack/ConfigLoader.php: -------------------------------------------------------------------------------- 1 | getMethod() === 'GET'); 16 | } 17 | 18 | /** 19 | * @param ServerRequestInterface $request 20 | * @return bool 21 | */ 22 | public static function isPost(ServerRequestInterface $request): bool 23 | { 24 | return ($request->getMethod() === 'POST'); 25 | } 26 | 27 | /** 28 | * @param ServerRequestInterface $request 29 | * @return bool 30 | */ 31 | public static function isPut(ServerRequestInterface $request): bool 32 | { 33 | return ($request->getMethod() === 'PUT'); 34 | } 35 | 36 | /** 37 | * @param ServerRequestInterface $request 38 | * @return bool 39 | */ 40 | public static function isPatch(ServerRequestInterface $request): bool 41 | { 42 | return ($request->getMethod() === 'PATCH'); 43 | } 44 | 45 | /** 46 | * @param ServerRequestInterface $request 47 | * @return bool 48 | */ 49 | public static function isDelete(ServerRequestInterface $request): bool 50 | { 51 | return ($request->getMethod() === 'DELETE'); 52 | } 53 | } -------------------------------------------------------------------------------- /src/Bedrock/AbstractApplication.php: -------------------------------------------------------------------------------- 1 | kernel; 32 | } 33 | 34 | /** 35 | * @return ContainerInterface 36 | */ 37 | public function getContainer(): ContainerInterface 38 | { 39 | return $this->kernel->getContainer(); 40 | } 41 | 42 | /** 43 | * @param string $property 44 | * @param mixed $default 45 | * @return mixed 46 | */ 47 | public function getProp(string $property, $default = null) 48 | { 49 | if (!isset($this->props)) { 50 | return $default; 51 | } 52 | return $this->props->get($property, $default); 53 | } 54 | 55 | /** 56 | * @param string $property 57 | * @return bool 58 | */ 59 | public function hasProp(string $property): bool 60 | { 61 | if (!isset($this->props)) { 62 | return false; 63 | } 64 | return $this->props->has($property); 65 | } 66 | 67 | /** 68 | * @return Dictionary|null 69 | */ 70 | public function getProps(): ?Dictionary 71 | { 72 | return $this->props; 73 | } 74 | 75 | /** 76 | * Bootstrap bootable processes 77 | * @param array $processes 78 | * @return $this 79 | * @throws InvalidBootableProcessException 80 | */ 81 | public function bootstrap(array $processes) 82 | { 83 | $bootstrap = new Bootstrap($processes, $this->getContainer()); 84 | $bootstrap->boot(); 85 | return $this; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Bedrock/AbstractBootstrapper.php: -------------------------------------------------------------------------------- 1 | application = $application; 51 | $this->resolver = new BootableResolver($application->getContainer()); 52 | } 53 | 54 | /** 55 | * Boot 56 | * @return bool|mixed 57 | * @throws \Exception 58 | */ 59 | public function boot() 60 | { 61 | // call environment method if exists 62 | // e.g. envDev() envProd() envStating() envTesting() 63 | $envMethod = 'env'.ucfirst(strtolower($this->application->getKernel()->getEnv())); 64 | $this->call($envMethod); 65 | 66 | // run processes 67 | foreach ($this->processes as $process) { 68 | $processResolved = $this->resolver->resolve($process); 69 | $processResolved->boot(); 70 | } 71 | 72 | // call method beginning by $bootMethodsPrefix 73 | $bootMethods = get_class_methods(get_class($this)); 74 | if (!empty($bootMethods)) { 75 | $l = strlen($this->bootMethodsPrefix); 76 | foreach ($bootMethods as $bootMethod) { 77 | if (substr($bootMethod, 0, $l) === $this->bootMethodsPrefix) { 78 | $this->call($bootMethod); 79 | } 80 | } 81 | } 82 | 83 | return true; 84 | } 85 | 86 | /** 87 | * @param string $method 88 | */ 89 | private function call(string $method) 90 | { 91 | if (method_exists($this, $method)) { 92 | if ('Peak\Di\Container' === get_class($this->application->getContainer())) { 93 | $this->application->getContainer()->call([$this, $method]); 94 | } else { 95 | $this->$method(); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Bedrock/Bootstrap/BootableResolver.php: -------------------------------------------------------------------------------- 1 | container = $container; 28 | } 29 | 30 | /** 31 | * Try to return a resolved item or throw an exception 32 | * @param mixed $item 33 | * @return Bootable 34 | * @throws InvalidBootableProcessException 35 | */ 36 | public function resolve($item): Bootable 37 | { 38 | if(is_string($item) && class_exists($item)) { 39 | if (null !== $this->container) { 40 | $item = $this->container->get($item); 41 | } 42 | if (is_string($item)) { 43 | $item = new $item(); 44 | } 45 | } elseif (is_callable($item)) { 46 | $item = new CallableProcess($item); 47 | } 48 | 49 | if (!$item instanceof Bootable) { 50 | throw new InvalidBootableProcessException($item); 51 | } 52 | 53 | return $item; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Bedrock/Bootstrap/Bootstrap.php: -------------------------------------------------------------------------------- 1 | processes = $processes; 30 | $this->resolver = new BootableResolver($container); 31 | } 32 | 33 | /** 34 | * Boot 35 | * 36 | * @return bool|mixed 37 | * @throws Exception\InvalidBootableProcessException 38 | */ 39 | public function boot() 40 | { 41 | foreach ($this->processes as $process) { 42 | $processResolved = $this->resolver->resolve($process); 43 | $processResolved->boot(); 44 | } 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Bedrock/Bootstrap/CallableProcess.php: -------------------------------------------------------------------------------- 1 | callable = $callable; 23 | } 24 | 25 | /** 26 | * @return mixed|void 27 | */ 28 | public function boot() 29 | { 30 | $fn = $this->callable; 31 | $fn(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Bedrock/Bootstrap/Exception/InvalidBootableProcessException.php: -------------------------------------------------------------------------------- 1 | process = $process; 23 | } 24 | 25 | /** 26 | * @return mixed 27 | */ 28 | public function getProcess() 29 | { 30 | return $this->process; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Bedrock/Cli/Application.php: -------------------------------------------------------------------------------- 1 | kernel = $kernel; 34 | $this->props = $props; 35 | 36 | $name = 'CliApplication'; 37 | $version = '0.1'; 38 | 39 | if (isset($props)) { 40 | $name = $props->get('name', $name); 41 | $version = $props->get('version', $version); 42 | } 43 | 44 | $this->console = new \Symfony\Component\Console\Application($name, $version); 45 | } 46 | 47 | /** 48 | * @return \Symfony\Component\Console\Application 49 | */ 50 | public function console(): \Symfony\Component\Console\Application 51 | { 52 | return $this->console; 53 | } 54 | 55 | /** 56 | * @param $commands 57 | * @return $this|mixed 58 | * @throws \Exception 59 | */ 60 | public function add($commands) 61 | { 62 | if (!is_array($commands)) { 63 | $commands = [$commands]; 64 | } 65 | 66 | foreach ($commands as $command) { 67 | if (is_string($command)) { 68 | $command = $this->getContainer()->get($command); 69 | } 70 | if (!$command instanceof Command) { 71 | throw new InvalidCommandException(); 72 | } 73 | $this->console->add($command); 74 | } 75 | return $this; 76 | } 77 | 78 | /** 79 | * @param InputInterface|null $input 80 | * @param OutputInterface|null $output 81 | * @return mixed 82 | * @throws \Exception 83 | */ 84 | public function run(InputInterface $input = null, OutputInterface $output = null) 85 | { 86 | return $this->console->run($input, $output); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Bedrock/Cli/Exception/InvalidCommandException.php: -------------------------------------------------------------------------------- 1 | startedGroups++; 30 | $this->currentPathPrefix .= $path; 31 | $this->handlers[$this->currentPathPrefix] = []; 32 | } 33 | 34 | /** 35 | * @param string $path 36 | */ 37 | public function stopGroup(string $path): void 38 | { 39 | $this->startedGroups--; 40 | $this->currentPathPrefix = substr($this->currentPathPrefix, 0, strlen($this->currentPathPrefix) - strlen($path)); 41 | } 42 | 43 | /** 44 | * @param $handler 45 | */ 46 | public function holdHandler($handler): void 47 | { 48 | $this->handlers[$this->currentPathPrefix][] = $handler; 49 | } 50 | 51 | /** 52 | * @param array $handlers 53 | */ 54 | public function holdHandlers(array $handlers): void 55 | { 56 | foreach ($handlers as $handler) { 57 | $this->holdHandler($handler); 58 | } 59 | } 60 | 61 | /** 62 | * @param string $path 63 | * @return array 64 | */ 65 | public function getHandlers(string $path): array 66 | { 67 | return $this->handlers[$path]; 68 | } 69 | 70 | /** 71 | * @return array 72 | */ 73 | public function getAllHandlers(): array 74 | { 75 | return $this->handlers; 76 | } 77 | 78 | /** 79 | * @param string|null $path 80 | */ 81 | public function releaseHandlers(string $path = null): void 82 | { 83 | (isset($path)) ? $this->handlers[$path] = [] : $this->handlers = []; 84 | } 85 | 86 | /** 87 | * @return int 88 | */ 89 | public function countStartedGroups(): int 90 | { 91 | return $this->startedGroups; 92 | } 93 | 94 | public function currentlyInAGroup(): bool 95 | { 96 | return ($this->startedGroups > 0); 97 | } 98 | 99 | /** 100 | * @return string 101 | */ 102 | public function getCurrentPathPrefix(): string 103 | { 104 | return $this->currentPathPrefix; 105 | } 106 | 107 | /** 108 | * @param string $path 109 | * @return string 110 | */ 111 | public function getFullPathFor(string $path): string 112 | { 113 | return $this->currentPathPrefix.$path; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Bedrock/Kernel.php: -------------------------------------------------------------------------------- 1 | environment = $environment; 38 | $this->container = $container; 39 | $this->initialize(); 40 | } 41 | 42 | /** 43 | * @return ContainerInterface 44 | */ 45 | public function getContainer(): ContainerInterface 46 | { 47 | return $this->container; 48 | } 49 | 50 | /** 51 | * @return string 52 | */ 53 | public function getEnv(): string 54 | { 55 | return $this->environment; 56 | } 57 | 58 | /** 59 | * Initialize kernel 60 | */ 61 | public function initialize() 62 | { 63 | // nothing by default 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Bedrock/README.md: -------------------------------------------------------------------------------- 1 | # Peak\Bedrock 2 | 3 | This package help you to create: 4 | - HTTP Application compatible PSR-7, PSR-11 and PSR-15. 5 | - CLI application compatible PSR-11 (with symfony/console). 6 | 7 | ### Installation 8 | 9 | ``` 10 | $ compose require peak/bedrock 11 | ``` 12 | 13 | ### Documentation 14 | 15 | See https://peakphp.github.io/docs/bedrock -------------------------------------------------------------------------------- /src/Bedrock/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peak/bedrock", 3 | "description": "Peak Bedrock Component", 4 | "keywords": ["peak", "bedrock", "http", "cli" ,"middleware", "psr-7", "psr-15", "application"], 5 | "license": "MIT", 6 | "homepage": "https://peakframework.com", 7 | "support": { 8 | "issues": "https://github.com/peakphp/framework/issues", 9 | "source": "https://github.com/peakphp/framework" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Francois Lajoie", 14 | "email": "francois@peakframework.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.2", 19 | "peak/http": "^4.0", 20 | "peak/collection": "^4.0" 21 | }, 22 | "suggest": { 23 | "symfony/console": "Required to use Peak\\Bedrock\\Cli\\Application (^4.3).", 24 | "symfony/process": "Required to use Peak\\Bedrock\\Cli\\Application (^4.3)." 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Peak\\Bedrock\\": "" 29 | } 30 | }, 31 | "extra": { 32 | "branch-alias": { 33 | "dev-master": "4.0-dev" 34 | } 35 | }, 36 | "minimum-stability": "dev", 37 | "prefer-stable": true 38 | } -------------------------------------------------------------------------------- /src/Blueprint/Backpack/AppBuilderBase.php: -------------------------------------------------------------------------------- 1 | Peak/Blueprint 2 | 3 | Contains all framework packages interfaces. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ compose require peak/blueprint 9 | ``` 10 | 11 | ### Documentation 12 | 13 | See https://peakphp.github.io/docs/blueprint -------------------------------------------------------------------------------- /src/Blueprint/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peak/blueprint", 3 | "description": "Peak Blueprint Interfaces", 4 | "keywords": ["peak", "blueprint", "interfaces", "contract"], 5 | "license": "MIT", 6 | "homepage": "https://peakframework.com", 7 | "support": { 8 | "issues": "https://github.com/peakphp/framework/issues", 9 | "source": "https://github.com/peakphp/framework" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Francois Lajoie", 14 | "email": "francois@peakframework.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.2" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Peak\\Blueprint\\": "" 23 | } 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "4.0-dev" 28 | } 29 | }, 30 | "minimum-stability": "dev", 31 | "prefer-stable": true 32 | } -------------------------------------------------------------------------------- /src/Collection/CollectionFlattener.php: -------------------------------------------------------------------------------- 1 | collection = $coll; 39 | } 40 | 41 | /** 42 | * Change keys separators 43 | * @param string $sep 44 | * @return $this 45 | * @throws Exception 46 | */ 47 | public function separator(string $sep): CollectionFlattener 48 | { 49 | if (mb_strlen($sep) != 1 || $sep === '*') { 50 | throw new Exception(__CLASS__.': Separator must be 1 character and cannot be an asterisk (*)'); 51 | } 52 | $this->separator = $sep; 53 | return $this; 54 | } 55 | 56 | /** 57 | * Simply flat all the collection 58 | * @return array 59 | */ 60 | public function flatAll(): array 61 | { 62 | $this->search = []; 63 | return $this->flatCollection($this->collection->toArray()); 64 | } 65 | 66 | /** 67 | * @param string $key 68 | * @return array 69 | */ 70 | public function flatKey(string $key): array 71 | { 72 | $this->search = [$key]; 73 | return $this->flatCollection($this->collection->toArray()); 74 | } 75 | 76 | /** 77 | * Flat multiple keys names 78 | * @param array $keys 79 | * @return array 80 | */ 81 | public function flatKeys(array $keys): array 82 | { 83 | $this->search = $keys; 84 | return $this->flatCollection($this->collection->toArray()); 85 | } 86 | 87 | /** 88 | * Flat collection recursively to one level key,val array 89 | * @param array $data 90 | * @param string|null $prefix 91 | * @param array $flat_data 92 | * @return array 93 | */ 94 | protected function flatCollection(array $data, string $prefix = null, array $flat_data = []): array 95 | { 96 | foreach ($data as $key => $val) { 97 | if ($prefix !== null) { 98 | $key = $prefix.$this->separator.$key; 99 | } 100 | 101 | $skip_key = $this->skipKey($key); 102 | 103 | if (is_array($val)) { 104 | $flat_data = array_merge( 105 | $flat_data, 106 | $this->flatCollection($val, (string) $key) 107 | ); 108 | continue; 109 | } 110 | 111 | if ($skip_key) { 112 | continue; 113 | } 114 | 115 | $flat_data[$key] = $val; 116 | } 117 | 118 | return $flat_data; 119 | } 120 | 121 | /** 122 | * Detect if search is finishing by a wildcard (.*) 123 | * @param string $search 124 | * @return bool 125 | */ 126 | protected function hasWildCard(string $search): bool 127 | { 128 | return (substr($search, -2) === $this->separator.'*'); 129 | } 130 | 131 | /** 132 | * Detect if key must be skipped according to $search 133 | * @param string $key 134 | * @return bool 135 | */ 136 | protected function skipKey(string $key): bool 137 | { 138 | if (!empty($this->search)) { 139 | foreach ($this->search as $search) { 140 | if ($this->hasWildCard($search)) { 141 | $search = substr($search, 0,-2); 142 | if (substr($key, 0, mb_strlen($search)) === $search) { 143 | return false; 144 | } 145 | } elseif ($search === $key) { 146 | return false; 147 | } 148 | } 149 | return true; 150 | } 151 | return false; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/Collection/DotNotationCollection.php: -------------------------------------------------------------------------------- 1 | items; 32 | 33 | if (!empty($path)) { 34 | $keys = $this->explode($path); 35 | foreach ($keys as $key) { 36 | if (!is_array($array) || !array_key_exists($key, $array)) { 37 | return $default; 38 | } 39 | $array = $array[$key]; 40 | } 41 | } 42 | 43 | return $array; 44 | } 45 | 46 | /** 47 | * Add a path 48 | * @param string $path 49 | * @param mixed $value 50 | */ 51 | public function set(string $path, $value): void 52 | { 53 | if (!empty($path)) { 54 | $at = & $this->items; 55 | $keys = $this->explode($path); 56 | 57 | while (count($keys) > 0) { 58 | if (count($keys) === 1) { 59 | if (!is_array($at)) { 60 | throw new RuntimeException('Can not set value at this path ['.$path.'] because is not array.'); 61 | } 62 | $at[array_shift($keys)] = $value; 63 | } else { 64 | $key = array_shift($keys); 65 | if (!isset($at[$key])) { 66 | $at[$key] = []; 67 | } 68 | $at =& $at[$key]; 69 | } 70 | } 71 | } else { 72 | $this->items = [$value]; 73 | } 74 | } 75 | 76 | /** 77 | * Merge a path with an array 78 | * @param string $path 79 | * @param array $values 80 | */ 81 | public function add(string $path, array $values): void 82 | { 83 | $get = (array)$this->get($path); 84 | $this->set($path, $this->arrayMergeRecursiveDistinct($get, $values)); 85 | } 86 | 87 | /** 88 | * Check if we have path 89 | * @param string $path 90 | * @return bool 91 | */ 92 | public function has(string $path): bool 93 | { 94 | $keys = $this->explode($path); 95 | $array = $this->items; 96 | foreach ($keys as $key) { 97 | if (!is_array($array) || !array_key_exists($key, $array)) { 98 | return false; 99 | } 100 | $array = $array[$key]; 101 | } 102 | return true; 103 | } 104 | 105 | /** 106 | * Explode path string 107 | * @param string $path 108 | * @return mixed 109 | */ 110 | protected function explode(string $path) 111 | { 112 | return explode(self::SEPARATOR, $path); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Collection/Exception/ImmutableException.php: -------------------------------------------------------------------------------- 1 | properties = $properties; 32 | } 33 | 34 | /** 35 | * @param string $prop 36 | * @param null $default 37 | * @return mixed 38 | */ 39 | public function get(string $prop, $default = null) 40 | { 41 | return $this->properties[$prop] ?? $default; 42 | } 43 | 44 | /** 45 | * @param string $prop 46 | * @param mixed $value 47 | * @return mixed 48 | */ 49 | public function set(string $prop, $value) 50 | { 51 | $this->properties[$prop] = $value; 52 | return $this; 53 | } 54 | 55 | /** 56 | * @param string $prop 57 | * @return bool 58 | */ 59 | public function has(string $prop): bool 60 | { 61 | return array_key_exists($prop, $this->properties); 62 | } 63 | 64 | /** 65 | * @param string $property 66 | * @return mixed 67 | * @throws Exception 68 | */ 69 | public function &__get(string $property) 70 | { 71 | if (!array_key_exists($property, $this->properties)) { 72 | throw new Exception('Property '.$property.' not found'); 73 | } 74 | 75 | return $this->properties[$property]; 76 | } 77 | 78 | /** 79 | * @param string $property 80 | * @param mixed $val 81 | */ 82 | public function __set(string $property, $val) 83 | { 84 | $this->properties[$property] = $val; 85 | } 86 | 87 | /** 88 | * @param string $property 89 | * @return bool 90 | */ 91 | public function __isset(string $property): bool 92 | { 93 | return array_key_exists($property, $this->properties); 94 | } 95 | 96 | /** 97 | * @param string $property 98 | */ 99 | public function __unset(string $property) 100 | { 101 | unset($this->properties[$property]); 102 | } 103 | 104 | /** 105 | * Assign a value to the specified offset 106 | * 107 | * @param string $offset 108 | * @param mixed $value 109 | */ 110 | public function offsetSet($offset, $value) 111 | { 112 | $this->properties[$offset] = $value; 113 | } 114 | 115 | /** 116 | * Whether an item exists 117 | * 118 | * @param string $offset 119 | * @return bool 120 | */ 121 | public function offsetExists($offset): bool 122 | { 123 | return isset($this->properties[$offset]); 124 | } 125 | 126 | /** 127 | * Item to delete 128 | * 129 | * @param string $offset 130 | */ 131 | public function offsetUnset($offset) 132 | { 133 | unset($this->properties[$offset]); 134 | } 135 | 136 | /** 137 | * Offset to retrieve 138 | * 139 | * @param string $offset 140 | * @return mixed 141 | */ 142 | public function offsetGet($offset) 143 | { 144 | return isset($this->properties[$offset]) ? $this->properties[$offset] : null; 145 | } 146 | 147 | /** 148 | * @return int 149 | */ 150 | public function count() 151 | { 152 | return count($this->properties); 153 | } 154 | 155 | /** 156 | * @return ArrayIterator 157 | */ 158 | public function getIterator() 159 | { 160 | return new ArrayIterator($this->properties); 161 | } 162 | 163 | /** 164 | * @return array 165 | */ 166 | public function toArray() 167 | { 168 | return $this->properties; 169 | } 170 | 171 | /** 172 | * @return string 173 | */ 174 | public function serialize() 175 | { 176 | return serialize($this->properties); 177 | } 178 | 179 | /** 180 | * @param string $data 181 | */ 182 | public function unserialize($data) 183 | { 184 | $this->properties = unserialize($data); 185 | } 186 | 187 | /** 188 | * @return array|mixed 189 | */ 190 | public function jsonSerialize() 191 | { 192 | return $this->properties; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/Collection/README.md: -------------------------------------------------------------------------------- 1 | # Peak Peak/Collection 2 | 3 | This package adds additional functionality on top of PHP arrays and typed structure. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ composer require peak/collection 9 | ``` 10 | 11 | ### Documentation 12 | 13 | See https://peakphp.github.io/docs/collection -------------------------------------------------------------------------------- /src/Collection/Structure/AbstractImmutableStructure.php: -------------------------------------------------------------------------------- 1 | types = $types; 38 | $this->default = $default; 39 | } 40 | 41 | /** 42 | * @return array 43 | */ 44 | public function getTypes(): array 45 | { 46 | return $this->types; 47 | } 48 | 49 | /** 50 | * @return mixed 51 | */ 52 | public function getDefault() 53 | { 54 | return $this->default; 55 | } 56 | 57 | /** 58 | * @param mixed $defaultValue 59 | * @return $this 60 | */ 61 | public function default($defaultValue) 62 | { 63 | $this->default = $defaultValue; 64 | return $this; 65 | } 66 | 67 | /** 68 | * @return $this 69 | */ 70 | public function null() 71 | { 72 | $this->types[] = self::NIL; 73 | return $this; 74 | } 75 | 76 | /** 77 | * @return DataType 78 | */ 79 | public function string(): DataType 80 | { 81 | $this->types[] = self::STRING; 82 | return $this; 83 | } 84 | 85 | /** 86 | * @return DataType 87 | */ 88 | public function integer(): DataType 89 | { 90 | $this->types[] = self::INT; 91 | return $this; 92 | } 93 | 94 | /** 95 | * @return DataType 96 | */ 97 | public function float(): DataType 98 | { 99 | $this->types[] = self::FLOAT; 100 | return $this; 101 | } 102 | 103 | /** 104 | * @return DataType 105 | */ 106 | public function boolean(): DataType 107 | { 108 | $this->types[] = self::BOOL; 109 | return $this; 110 | } 111 | 112 | /** 113 | * @return DataType 114 | */ 115 | public function array(): DataType 116 | { 117 | $this->types[] = self::ARRAY; 118 | return $this; 119 | } 120 | 121 | /** 122 | * @param string $className 123 | * @return DataType 124 | */ 125 | public function object(string $className = 'object'): DataType 126 | { 127 | $this->types[] = $className; 128 | return $this; 129 | } 130 | 131 | /** 132 | * @return DataType 133 | */ 134 | public function resource(): DataType 135 | { 136 | $this->types[] = self::RESOURCE; 137 | return $this; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Collection/Structure/Exception/InvalidPropertyDefinitionException.php: -------------------------------------------------------------------------------- 1 | class = get_class($class); 27 | $this->propertyName = $propertyName; 28 | parent::__construct(getClassShortName($this->class).' - Structure definition for [' . $propertyName . '] must be an instance of DataType'); 29 | } 30 | 31 | /** 32 | * @return object 33 | */ 34 | public function getClass(): object 35 | { 36 | return $this->class; 37 | } 38 | 39 | /** 40 | * @return string 41 | */ 42 | public function getPropertyName(): string 43 | { 44 | return $this->propertyName; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Collection/Structure/Exception/InvalidPropertyTypeException.php: -------------------------------------------------------------------------------- 1 | class = get_class($class); 42 | $this->propertyName = $propertyName; 43 | $this->typesExpected = $typesExpected; 44 | $this->typeReceived = $typeReceived; 45 | $typesExpectedString = implode(' OR ', $typesExpected); 46 | parent::__construct( 47 | getClassShortName($this->class).' - Property ['.$propertyName.'] expect a type of ('.$typesExpectedString.') ... '. $typeReceived . ' given' 48 | ); 49 | } 50 | 51 | /** 52 | * @return string 53 | */ 54 | public function getClass(): string 55 | { 56 | return $this->class; 57 | } 58 | 59 | /** 60 | * @return string 61 | */ 62 | public function getPropertyName(): string 63 | { 64 | return $this->propertyName; 65 | } 66 | 67 | /** 68 | * @return array 69 | */ 70 | public function getTypesExpected(): array 71 | { 72 | return $this->typesExpected; 73 | } 74 | 75 | /** 76 | * @return string 77 | */ 78 | public function getTypeReceived(): string 79 | { 80 | return $this->typeReceived; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Collection/Structure/Exception/InvalidStructureException.php: -------------------------------------------------------------------------------- 1 | class = get_class($class); 27 | $this->propertyName = $propertyName; 28 | parent::__construct(getClassShortName($this->class).' - Property [' . $propertyName . '] is undefined in the structure'); 29 | } 30 | 31 | /** 32 | * @return string 33 | */ 34 | public function getPropertyName(): string 35 | { 36 | return $this->propertyName; 37 | } 38 | 39 | /** 40 | * @return string 41 | */ 42 | public function getClass(): string 43 | { 44 | return $this->class; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Collection/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peak/collection", 3 | "description": "Peak Collection Component", 4 | "keywords": ["peak", "collection", "bag", "utilities"], 5 | "license": "MIT", 6 | "homepage": "https://peakframework.com", 7 | "support": { 8 | "issues": "https://github.com/peakphp/framework/issues", 9 | "source": "https://github.com/peakphp/framework" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Francois Lajoie", 14 | "email": "francois@peakframework.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.2", 19 | "peak/blueprint": "^4.0", 20 | "peak/common": "^4.0" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Peak\\Collection\\": "" 25 | } 26 | }, 27 | "extra": { 28 | "branch-alias": { 29 | "dev-master": "4.0-dev" 30 | } 31 | }, 32 | "minimum-stability": "dev", 33 | "prefer-stable": true 34 | } -------------------------------------------------------------------------------- /src/Common/Chrono/Chrono.php: -------------------------------------------------------------------------------- 1 | 32 | */ 33 | private $steps = []; 34 | 35 | /** 36 | * Chrono constructor. 37 | * @param string $name 38 | */ 39 | public function __construct(string $name = '') 40 | { 41 | $this->name = $name; 42 | $this->start = $this->getMicroTime(); 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getName(): string 49 | { 50 | return $this->name; 51 | } 52 | 53 | /** 54 | * @return array 55 | */ 56 | public function getSteps(): array 57 | { 58 | return $this->steps; 59 | } 60 | 61 | /** 62 | * @return bool 63 | */ 64 | public function stop(): bool 65 | { 66 | if (isset($this->end)) { 67 | return false; 68 | } 69 | $this->end = $this->getMicroTime(); 70 | return true; 71 | } 72 | 73 | /** 74 | * @param string $id 75 | * @param string $description 76 | */ 77 | public function markStep(string $id, string $description = '') 78 | { 79 | $time = $this->start; 80 | if (!empty($this->steps)) { 81 | $lastStep = $this->steps[count($this->steps) - 1]; 82 | $time = $lastStep->getStart() + $lastStep->getDuration(); 83 | } 84 | $this->steps[] = new Step($time, $id, $description); 85 | } 86 | 87 | /** 88 | * @return bool 89 | */ 90 | public function isStopped(): bool 91 | { 92 | return (isset($this->end)); 93 | } 94 | 95 | /** 96 | * @param int $decimalPrecision 97 | * @return float 98 | */ 99 | public function getSec(int $decimalPrecision = 2): float 100 | { 101 | return $this->formatSec($this->getElapsed(), $decimalPrecision); 102 | } 103 | 104 | /** 105 | * @param int $decimalPrecision 106 | * @return float 107 | */ 108 | public function getMs(int $decimalPrecision = 2): float 109 | { 110 | return $this->formatMs($this->getElapsed(), $decimalPrecision); 111 | } 112 | 113 | /** 114 | * @return float 115 | */ 116 | private function getElapsed(): float 117 | { 118 | $end = $this->end ?? $this->getMicroTime(); 119 | return $end - $this->start; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Common/Chrono/Step.php: -------------------------------------------------------------------------------- 1 | start = $start; 43 | $this->duration = $this->getMicroTime() - $start; 44 | $this->id = $id; 45 | $this->description = $description; 46 | } 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function getId(): string 52 | { 53 | return $this->id; 54 | } 55 | 56 | /** 57 | * @return string 58 | */ 59 | public function getDescription(): string 60 | { 61 | return $this->description; 62 | } 63 | 64 | /** 65 | * @return float 66 | */ 67 | public function getStart(): float 68 | { 69 | return $this->start; 70 | } 71 | 72 | /** 73 | * @return float 74 | */ 75 | public function getDuration(): float 76 | { 77 | return $this->duration; 78 | } 79 | 80 | /** 81 | * @param int $decimalPrecision 82 | * @return float 83 | */ 84 | public function getSec(int $decimalPrecision = 2): float 85 | { 86 | return $this->formatSec($this->duration, $decimalPrecision); 87 | } 88 | 89 | /** 90 | * @param int $decimalPrecision 91 | * @return float 92 | */ 93 | public function getMs(int $decimalPrecision = 2): float 94 | { 95 | return $this->formatMs($this->duration, $decimalPrecision); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Common/PhpIni.php: -------------------------------------------------------------------------------- 1 | $val) { 26 | if (!is_array($val)) { 27 | $this->set($setting, $val, $strict); 28 | } else { 29 | foreach ($val as $k => $v) { 30 | $this->set($setting.'.'.$k, $v, $strict); 31 | } 32 | } 33 | } 34 | } 35 | 36 | /** 37 | * @param string $option 38 | * @param mixed $value 39 | * @param bool $strict 40 | * @throws \Exception 41 | */ 42 | private function set(string $option, $value, bool $strict = false) 43 | { 44 | $result = @ini_set($option, (string)$value); 45 | if ($strict && $result === false) { 46 | throw new \Exception('Fail to set php option '.$option.' to "'.print_r($value, true).'"'); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Common/README.md: -------------------------------------------------------------------------------- 1 | # Peak/Common 2 | 3 | This component contains a bunch of useful and generic tools. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ composer require peak/common 9 | ``` 10 | 11 | ### Documentation 12 | 13 | See https://peakphp.github.io/docs/blueprint -------------------------------------------------------------------------------- /src/Common/Traits/ArrayMergeRecursiveDistinct.php: -------------------------------------------------------------------------------- 1 | $value) { 25 | if (!$mergeNumericKeys && is_numeric($key)) { 26 | $merged[] = $value; 27 | continue; 28 | } 29 | if (is_array($value) && array_key_exists($key, $merged) && is_array($merged[$key])) { 30 | $merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value); 31 | } else { 32 | $merged[$key] = $value; 33 | } 34 | } 35 | 36 | return $merged; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Common/Traits/LoadArrayFiles.php: -------------------------------------------------------------------------------- 1 | array_files_content[] = $content; 42 | } 43 | 44 | return $this->array_files_content; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Common/Traits/Macro.php: -------------------------------------------------------------------------------- 1 | macros[$name] = Closure::bind($macroCallable, $this, get_class()); 26 | return $this; 27 | } 28 | 29 | /** 30 | * @param string $name 31 | * @return bool 32 | */ 33 | public function hasMacro(string $name) 34 | { 35 | return isset($this->macros[$name]); 36 | } 37 | 38 | /** 39 | * Call a macro 40 | * @param string $name 41 | * @param array $args 42 | * @return mixed 43 | */ 44 | public function callMacro(string $name, array $args) 45 | { 46 | if (isset($this->macros[$name])) { 47 | return call_user_func_array($this->macros[$name], $args); 48 | } 49 | throw new RuntimeException('There is no macro with the given name "'.$name.'" to call'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Common/Traits/MicroTime.php: -------------------------------------------------------------------------------- 1 | $v) { 19 | $k = $this->updateStringToCamelCase($k); 20 | $newData[$k] = $v; 21 | } 22 | return $newData; 23 | } 24 | 25 | /** 26 | * @param string $string 27 | * @return string 28 | */ 29 | protected function updateStringToCamelCase(string $text): string 30 | { 31 | return lcfirst(str_replace(['_', '-', ' '],'', lcfirst(ucwords(strtolower($text), '_- ')))); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Common/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peak/common", 3 | "description": "Peak Common Component", 4 | "keywords": ["peak", "common", "misc", "tools", "library", "utilities"], 5 | "license": "MIT", 6 | "homepage": "https://peakframework.com", 7 | "support": { 8 | "issues": "https://github.com/peakphp/framework/issues", 9 | "source": "https://github.com/peakphp/framework" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Francois Lajoie", 14 | "email": "francois@peakframework.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.2" 19 | }, 20 | "autoload": { 21 | "files": [ 22 | "helpers.php" 23 | ], 24 | "psr-4": { 25 | "Peak\\Common\\": "" 26 | } 27 | }, 28 | "extra": { 29 | "branch-alias": { 30 | "dev-master": "4.0-dev" 31 | } 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true 35 | 36 | } -------------------------------------------------------------------------------- /src/Config/Config.php: -------------------------------------------------------------------------------- 1 | cacheId = $cacheId; 48 | $this->ttl = $ttl; 49 | $this->configFactory = $configFactory; 50 | $this->configCache = $configCache; 51 | } 52 | 53 | /** 54 | * @param array $resources 55 | * @return ConfigBlueprint 56 | * @throws \Psr\SimpleCache\InvalidArgumentException 57 | */ 58 | public function loadResources(array $resources): ConfigBlueprint 59 | { 60 | return $this->load($this->cacheId, $resources); 61 | } 62 | 63 | /** 64 | * @param array $resources 65 | * @param ConfigBlueprint $customConfig 66 | * @return ConfigBlueprint 67 | * @throws \Psr\SimpleCache\InvalidArgumentException 68 | */ 69 | public function loadResourcesWith(array $resources, ConfigBlueprint $customConfig): ConfigBlueprint 70 | { 71 | return $this->load($this->cacheId, $resources, $customConfig); 72 | } 73 | 74 | /** 75 | * @param string $cacheId 76 | * @param array $resources 77 | * @param ConfigBlueprint|null $customConfig 78 | * @return ConfigBlueprint 79 | * @throws \Psr\SimpleCache\InvalidArgumentException 80 | */ 81 | protected function load(string $cacheId, array $resources, ConfigBlueprint $customConfig = null): ConfigBlueprint 82 | { 83 | $config = $this->configCache->get($cacheId); 84 | if (null !== $config) { 85 | return $config; 86 | } 87 | 88 | if (isset($customConfig)) { 89 | $config = $this->configFactory->loadResourcesWith($resources, $customConfig); 90 | } else { 91 | $config = $this->configFactory->loadResources($resources); 92 | } 93 | 94 | $this->configCache->set($cacheId, $config, $this->ttl); 95 | 96 | return $config; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Config/ConfigFactory.php: -------------------------------------------------------------------------------- 1 | configResolver = $configResolver; 30 | } 31 | 32 | /** 33 | * @param FilesHandlers|null $filesHandlers 34 | * @return $this 35 | */ 36 | public function setFilesHandlers(?FilesHandlers $filesHandlers) 37 | { 38 | $this->filesHandlers = $filesHandlers; 39 | return $this; 40 | } 41 | 42 | /** 43 | * @param array $resources 44 | * @return ConfigBlueprint 45 | * @throws UnknownResourceException 46 | */ 47 | public function loadResources(array $resources): ConfigBlueprint 48 | { 49 | return $this->processResources($resources, new Config()); 50 | } 51 | 52 | /** 53 | * @param array $resources 54 | * @param ConfigBlueprint $customConfig 55 | * @return ConfigBlueprint 56 | * @throws UnknownResourceException 57 | */ 58 | public function loadResourcesWith(array $resources, ConfigBlueprint $customConfig): ConfigBlueprint 59 | { 60 | return $this->processResources($resources, $customConfig); 61 | } 62 | 63 | /** 64 | * @param array $resources 65 | * @param ConfigBlueprint $config 66 | * @return ConfigBlueprint 67 | * @throws UnknownResourceException 68 | */ 69 | protected function processResources(array $resources, ConfigBlueprint $config): ConfigBlueprint 70 | { 71 | $filesHandler = $this->filesHandlers; 72 | if (!isset($filesHandler)) { 73 | $filesHandler = new FilesHandlers(null); 74 | } 75 | 76 | $configResolver = $this->configResolver; 77 | if (!isset($configResolver)) { 78 | $configResolver = new ConfigResolver($filesHandler); 79 | } 80 | 81 | foreach ($resources as $resource) { 82 | $stream = $configResolver->resolve($resource); 83 | // @todo fix method not in signature 84 | $config->mergeRecursiveDistinct($stream->get()); 85 | } 86 | return $config; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Config/ConfigResolver.php: -------------------------------------------------------------------------------- 1 | filesHandlers = $filesHandlers; 40 | } 41 | 42 | /** 43 | * Resolve a config resource to a valid StreamInterface 44 | * 45 | * @param mixed $resource 46 | * @return Stream 47 | * @throws UnknownResourceException 48 | */ 49 | public function resolve($resource): Stream 50 | { 51 | // detect best way to load and process configuration content 52 | if (is_array($resource)) { 53 | return new DataStream($resource, new ArrayProcessor()); 54 | } elseif (is_callable($resource)) { 55 | return new DataStream($resource, new CallableProcessor()); 56 | } elseif ($resource instanceof Config) { 57 | return new DataStream($resource, new ConfigProcessor()); 58 | } elseif ($resource instanceof Collection) { 59 | return new DataStream($resource, new CollectionProcessor()); 60 | } elseif ($resource instanceof Stream) { 61 | return $resource; 62 | } elseif ($resource instanceof stdClass) { 63 | return new DataStream($resource, new StdClassProcessor()); 64 | } elseif (is_string($resource)) { 65 | $filesHandlers = $this->filesHandlers; 66 | if (null === $filesHandlers) { 67 | $filesHandlers = new FilesHandlers(null); 68 | } 69 | return new FileStream($resource, $filesHandlers); 70 | } 71 | 72 | throw new UnknownResourceException($resource); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Config/Exception/CacheInvalidKeyException.php: -------------------------------------------------------------------------------- 1 | path = $path; 25 | } 26 | 27 | /** 28 | * @return string 29 | */ 30 | public function getPath(): string 31 | { 32 | return $this->path; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Config/Exception/CachePathNotWritableException.php: -------------------------------------------------------------------------------- 1 | path = $path; 25 | } 26 | 27 | /** 28 | * @return string 29 | */ 30 | public function getPath(): string 31 | { 32 | return $this->path; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Config/Exception/FileNotFoundException.php: -------------------------------------------------------------------------------- 1 | configFile = $configFile; 24 | } 25 | 26 | /** 27 | * @return string 28 | */ 29 | public function getConfigFile(): string 30 | { 31 | return $this->file; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Config/Exception/FileNotReadableException.php: -------------------------------------------------------------------------------- 1 | configFile = $configFile; 24 | } 25 | 26 | /** 27 | * @return string 28 | */ 29 | public function getConfigFile(): string 30 | { 31 | return $this->configFile; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Config/Exception/LoaderException.php: -------------------------------------------------------------------------------- 1 | resource = $resource; 23 | parent::__construct('Unknown config resources'); 24 | } 25 | 26 | /** 27 | * @return mixed 28 | */ 29 | public function getResource() 30 | { 31 | return $this->resource; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Config/FilesHandlers.php: -------------------------------------------------------------------------------- 1 | [ 25 | 'loader' => \Peak\Config\Loader\PhpLoader::class, 26 | 'processor' => \Peak\Config\Processor\ArrayProcessor::class 27 | ], 28 | 'json' => [ 29 | 'loader' => \Peak\Config\Loader\DefaultLoader::class, 30 | 'processor' => \Peak\Config\Processor\JsonProcessor::class 31 | ], 32 | 'yml' => [ 33 | 'loader' => \Peak\Config\Loader\DefaultLoader::class, 34 | 'processor' => \Peak\Config\Processor\YamlProcessor::class 35 | ], 36 | 'ini' => [ 37 | 'loader' => \Peak\Config\Loader\DefaultLoader::class, 38 | 'processor' => \Peak\Config\Processor\IniProcessor::class 39 | ], 40 | 'txt' => [ 41 | 'loader' => \Peak\Config\Loader\TextLoader::class, 42 | 'processor' => \Peak\Config\Processor\ArrayProcessor::class 43 | ], 44 | 'log' => [ 45 | 'loader' => \Peak\Config\Loader\TextLoader::class, 46 | 'processor' => \Peak\Config\Processor\ArrayProcessor::class 47 | ], 48 | 'xml' => [ 49 | 'loader' => \Peak\Config\Loader\DefaultLoader::class, 50 | 'processor' => \Peak\Config\Processor\XmlProcessor::class 51 | ], 52 | 'env' => [ 53 | 'loader' => \Peak\Config\Loader\DefaultLoader::class, 54 | 'processor' => \Peak\Config\Processor\EnvProcessor::class 55 | ], 56 | ]; 57 | 58 | 59 | /** 60 | * FilesHandlers constructor. 61 | * 62 | * @param array|null $handlers if null, $defaultHandlers is used instead 63 | */ 64 | public function __construct(?array $handlers) 65 | { 66 | if (null === $handlers) { 67 | $handlers = $this->defaultHandlers; 68 | } 69 | $this->handlers = $handlers; 70 | } 71 | 72 | /** 73 | * Check if we have a specific file handlers 74 | * 75 | * @param string $name 76 | * @return bool 77 | */ 78 | public function has(string $name): bool 79 | { 80 | return array_key_exists($name, $this->handlers); 81 | } 82 | 83 | /** 84 | * Get a file handlers 85 | * 86 | * @param string $name 87 | * @return array 88 | * @throws NoFileHandlersException 89 | */ 90 | public function get(string $name): array 91 | { 92 | if (!$this->has($name)) { 93 | throw new NoFileHandlersException($name); 94 | } 95 | 96 | return $this->handlers[$name]; 97 | } 98 | 99 | /** 100 | * Get a file loader 101 | * 102 | * @param string $name 103 | * @return ResourceLoader 104 | * @throws NoFileHandlersException 105 | */ 106 | public function getLoader(string $name): ResourceLoader 107 | { 108 | $handlers = $this->get($name); 109 | return new $handlers['loader'](); 110 | } 111 | 112 | /** 113 | * Get a file processor 114 | * 115 | * @param string $name 116 | * @return ResourceProcessor 117 | * @throws NoFileHandlersException 118 | */ 119 | public function getProcessor(string $name): ResourceProcessor 120 | { 121 | $handlers = $this->get($name); 122 | return new $handlers['processor'](); 123 | } 124 | 125 | /** 126 | * Get current files handlers 127 | * 128 | * @return array 129 | */ 130 | public function getAll(): array 131 | { 132 | return $this->handlers; 133 | } 134 | 135 | /** 136 | * Add or override a file handlers 137 | * 138 | * @param string $name 139 | * @param string $loader 140 | * @param string $processor 141 | * @return FilesHandlers 142 | */ 143 | public function set(string $name, string $loader, string $processor): FilesHandlers 144 | { 145 | $this->handlers[$name] = [ 146 | 'loader' => new $loader(), 147 | 'processor' => new $processor(), 148 | ]; 149 | return $this; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Config/Loader/DefaultLoader.php: -------------------------------------------------------------------------------- 1 | toArray(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Config/Processor/ConfigProcessor.php: -------------------------------------------------------------------------------- 1 | toArray(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Config/Processor/EnvProcessor.php: -------------------------------------------------------------------------------- 1 | load($data); 35 | return $this->content; 36 | } 37 | 38 | /** 39 | * Loads in the ini file specified in filename, and returns the settings in 40 | * it as an associative multi-dimensional array 41 | * @param string $data 42 | * @throws ProcessorException 43 | */ 44 | public function load(string $data) 45 | { 46 | // we silence error(s) so we can catch them and throw a proper exception after 47 | $data = @parse_ini_string($data, true); 48 | 49 | // fail if there was an error while processing the specified ini file 50 | if ($data === false) { 51 | $error = error_get_last(); 52 | throw new ProcessorException(__CLASS__.' fail to parse data: '.$error['message']); 53 | } 54 | 55 | // reset the result array 56 | $this->content = []; 57 | 58 | // loop through each section 59 | foreach ($data as $section => $contents) { 60 | $this->processSection($section, $contents); 61 | } 62 | } 63 | 64 | /** 65 | * Process contents of the specified section 66 | * 67 | * @param string $section Section name 68 | * @param array $contents Section contents 69 | * @throws ProcessorException 70 | */ 71 | private function processSection(string $section, array $contents) 72 | { 73 | // the section does not extend another section 74 | if (stripos($section, ':') === false) { 75 | $this->content[$section] = $this->processSectionContents($contents); 76 | return; 77 | } 78 | 79 | // section extends another section 80 | // extract section names 81 | list($ext_target, $ext_source) = explode(':', $section); 82 | $ext_target = trim($ext_target); 83 | $ext_source = trim($ext_source); 84 | 85 | // check if the extended section exists 86 | if (!isset($this->content[$ext_source])) { 87 | throw new ProcessorException(__CLASS__.': Unable to extend section ' . $ext_source . ', section not found'); 88 | } 89 | 90 | // process section contents 91 | $this->content[$ext_target] = $this->processSectionContents($contents); 92 | 93 | // merge the new section with the existing section values 94 | $this->content[$ext_target] = $this->arrayMergeRecursiveDistinct( 95 | $this->content[$ext_source], 96 | $this->content[$ext_target] 97 | ); 98 | } 99 | 100 | /** 101 | * Process contents of a section 102 | * 103 | * @param array $contents Section contents 104 | * @return array 105 | */ 106 | private function processSectionContents(array $contents) 107 | { 108 | $result = []; 109 | 110 | // loop through each line and convert it to an array 111 | foreach ($contents as $path => $value) { 112 | // convert all a.b.c.d to multi-dimensional arrays 113 | $process = $this->processContentEntry($path, $value); 114 | // merge the current line with all previous ones 115 | $result = $this->arrayMergeRecursiveDistinct($result, $process); 116 | } 117 | 118 | return $result; 119 | } 120 | 121 | /** 122 | * Convert a.b.c.d paths to multi-dimensional arrays 123 | * 124 | * @param string $path Current ini file's line's key 125 | * @param mixed $value Current ini file's line's value 126 | * @return array 127 | */ 128 | private function processContentEntry($path, $value) 129 | { 130 | $pos = strpos($path, '.'); 131 | 132 | if ($pos === false) { 133 | return [ 134 | $path => $value 135 | ]; 136 | } 137 | 138 | $key = substr($path, 0, $pos); 139 | $path = substr($path, $pos + 1); 140 | 141 | return [ 142 | $key => $this->processContentEntry($path, $value) 143 | ]; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Config/Processor/JsonProcessor.php: -------------------------------------------------------------------------------- 1 | '.json_last_error_msg()); 28 | } 29 | 30 | return $content; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Config/Processor/StdClassProcessor.php: -------------------------------------------------------------------------------- 1 | data = $data; 30 | $this->processor = $processor; 31 | } 32 | 33 | /** 34 | * @return array 35 | */ 36 | public function get(): array 37 | { 38 | return $this->processor->process($this->data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Config/Stream/FileStream.php: -------------------------------------------------------------------------------- 1 | file = $file; 34 | $this->handlers = $handlers; 35 | } 36 | 37 | /** 38 | * @return array 39 | * @throws \Peak\Config\Exception\NoFileHandlersException 40 | */ 41 | public function get(): array 42 | { 43 | $ext = strtolower(pathinfo($this->file, PATHINFO_EXTENSION)); 44 | 45 | $loader = $this->handlers->getLoader($ext); 46 | $processor = $this->handlers->getProcessor($ext); 47 | 48 | return $processor->process($loader->load($this->file)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Config/Stream/JsonStream.php: -------------------------------------------------------------------------------- 1 | =7.2", 20 | "psr/simple-cache": "^1.0", 21 | "peak/blueprint": "^4.0", 22 | "peak/collection": "^4.0" 23 | }, 24 | "suggest": { 25 | "symfony/yaml": "Required to parse yaml file with Peak\\Config (^4.3)." 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Peak\\Config\\": "" 30 | }, 31 | "exclude-from-classmap": [ 32 | "/Tests/" 33 | ] 34 | }, 35 | "minimum-stability": "dev", 36 | "prefer-stable": true, 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "4.0-dev" 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Di/ArrayDefinition.php: -------------------------------------------------------------------------------- 1 | instantiator = new ClassInstantiator(); 43 | $this->classResolver = new ClassResolver(); 44 | } 45 | 46 | /** 47 | * @param array $definition 48 | * @param Container $container 49 | * @param array $args 50 | * @return object 51 | * @throws Exception\AmbiguousResolutionException 52 | * @throws Exception\ClassDefinitionNotFoundException 53 | * @throws Exception\InterfaceNotFoundException 54 | * @throws InfiniteLoopResolutionException 55 | * @throws \ReflectionException 56 | */ 57 | public function resolve(array $definition, Container $container, array $args = []) 58 | { 59 | $final_args = $definition; 60 | if (!empty($args)) { // add create argument at the end 61 | foreach ($args as $arg) { 62 | $final_args[] = $arg; 63 | } 64 | } 65 | $definition = array_shift($final_args); 66 | 67 | foreach ($final_args as $index => $arg) { 68 | if (is_array($arg)) { 69 | $final_args[$index] = $this->resolve($arg, $container); 70 | } elseif (is_callable($arg)) { 71 | $final_args[$index] = $arg($container); 72 | } elseif (is_object($arg)) { 73 | $final_args[$index] = $arg; 74 | } elseif (class_exists($arg) && $this->newInstanceOnly) { 75 | $this->n++; 76 | if ($this->n > 1) { 77 | throw new InfiniteLoopResolutionException($definition); 78 | } 79 | $subArg = $this->classResolver->resolve($arg, $container, $args); 80 | $final_args[$index] = $this->instantiator->instantiate($arg, $subArg); 81 | } elseif (class_exists($arg) && !$this->newInstanceOnly) { 82 | if ($container->has($arg)) { 83 | $final_args[$index] = $container->get($arg); 84 | } elseif ($container->hasDefinition($arg)) { 85 | $final_args[$index] = $container->resolve($arg); 86 | } else { 87 | $this->n++; 88 | if ($this->n > 1) { 89 | throw new InfiniteLoopResolutionException($definition); 90 | } 91 | $subArg = $this->classResolver->resolve($arg, $container, $args); 92 | $final_args[$index] = $this->instantiator->instantiate($arg, $subArg); 93 | } 94 | } 95 | } 96 | 97 | return $this->instantiator->instantiate($definition, $final_args); 98 | } 99 | 100 | /** 101 | * Set string resolution to new instance only 102 | * 103 | * @return $this 104 | */ 105 | public function newInstancesOnly() 106 | { 107 | $this->newInstanceOnly = true; 108 | return $this; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Di/Binding/AbstractBinding.php: -------------------------------------------------------------------------------- 1 | name = $name; 43 | $this->type = $type; 44 | $this->definition = $definition; 45 | } 46 | 47 | /** 48 | * Get name 49 | * 50 | * @return string 51 | */ 52 | public function getName() 53 | { 54 | return $this->name; 55 | } 56 | 57 | /** 58 | * @return int|mixed 59 | */ 60 | public function getType() 61 | { 62 | return $this->type; 63 | } 64 | 65 | /** 66 | * Get definition 67 | * 68 | * @return string 69 | */ 70 | public function getDefinition() 71 | { 72 | return $this->definition; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Di/Binding/BindingInterface.php: -------------------------------------------------------------------------------- 1 | definition; 36 | 37 | if (!is_null($explicit) && !empty($explicit)) { 38 | $definition = $explicit; 39 | } 40 | 41 | return $definition($container, $args); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Di/Binding/Prototype.php: -------------------------------------------------------------------------------- 1 | instantiator = new ClassInstantiator(); 49 | $this->arrayDefinition = new ArrayDefinition(); 50 | $this->classResolver = new ClassResolver(); 51 | parent::__construct($name, self::PROTOTYPE, $definition); 52 | } 53 | 54 | /** 55 | * Resolve the binding 56 | * 57 | * @param Container $container 58 | * @param array $args 59 | * @param callable|null $explicit 60 | * @return mixed|null 61 | * @throws \Exception 62 | */ 63 | public function resolve(Container $container, array $args = [], $explicit = null) 64 | { 65 | $definition = $this->definition; 66 | 67 | if (!is_null($explicit) && !empty($explicit)) { 68 | $definition = $explicit; 69 | } 70 | 71 | if (is_string($definition)) { 72 | // a string should be resolved once only, if more than one, it mean that the class behind the 73 | // string have also dependency on itself and may go on infinite loop 74 | $this->n++; 75 | if ($this->n > 1) { 76 | throw new InfiniteLoopResolutionException($definition); 77 | } 78 | $args = $this->classResolver->resolve($definition, $container, $args, $explicit); 79 | $instance = $this->instantiator->instantiate($definition, $args); 80 | // since it is a prototype, when we reach this line, it mean that prototype has been resolved properly, 81 | // so we decrease $n to allow other call to this prototype 82 | $this->n--; 83 | return $instance; 84 | } elseif (is_array($definition)) { 85 | return $this->arrayDefinition 86 | ->newInstancesOnly() 87 | ->resolve($definition, $container, $args); 88 | } 89 | 90 | throw new InvalidDefinitionException($this->name); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Di/Binding/Singleton.php: -------------------------------------------------------------------------------- 1 | instantiator = new ClassInstantiator(); 56 | $this->arrayDefinition = new ArrayDefinition(); 57 | $this->classResolver = new ClassResolver(); 58 | parent::__construct($name, self::SINGLETON, $definition); 59 | } 60 | 61 | /** 62 | * @param Container $container 63 | * @param array $args 64 | * @param null $explicit 65 | * @return mixed|object|null 66 | * @throws InfiniteLoopResolutionException 67 | * @throws InvalidDefinitionException 68 | * @throws \Peak\Di\Exception\AmbiguousResolutionException 69 | * @throws \Peak\Di\Exception\ClassDefinitionNotFoundException 70 | * @throws \Peak\Di\Exception\InterfaceNotFoundException 71 | * @throws \ReflectionException 72 | */ 73 | public function resolve(Container $container, array $args = [], $explicit = null) 74 | { 75 | $definition = $this->definition; 76 | 77 | if (null !== $this->storedInstance) { 78 | $definition = $this->storedInstance; 79 | } 80 | 81 | $is_explicit = false; 82 | if (!is_null($explicit) && !empty($explicit)) { 83 | $definition = $explicit; 84 | $is_explicit = true; 85 | } 86 | 87 | if (!$is_explicit && $container->has($this->name)) { 88 | return $container->get($this->name); 89 | } 90 | 91 | if (is_callable($definition)) { 92 | $instance = $definition($container, $args); 93 | } elseif (is_object($definition)) { 94 | $instance = $definition; 95 | } elseif(is_string($definition)) { 96 | // a string should be resolved once only, if more than one, it mean that the class behind the 97 | // string have also dependency on itself and may go on infinite loop 98 | $this->n++; 99 | if ($this->n > 1) { 100 | throw new InfiniteLoopResolutionException($definition); 101 | } 102 | $args = $this->classResolver->resolve($definition, $container, $args, $explicit); 103 | $instance = $this->instantiator->instantiate($definition, $args); 104 | } elseif (is_array($definition)) { 105 | $instance = $this->arrayDefinition->resolve($definition, $container, $args); 106 | } 107 | 108 | if (isset($instance)) { 109 | if (!$is_explicit) { 110 | $this->storedInstance = $instance; 111 | $container->set($instance); 112 | } 113 | return $instance; 114 | } 115 | 116 | throw new InvalidDefinitionException($this->name); 117 | } 118 | 119 | /** 120 | * Removed stored instance 121 | */ 122 | public function deleteStoredInstance() 123 | { 124 | $this->storedInstance = null; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Di/BindingResolver.php: -------------------------------------------------------------------------------- 1 | resolve($container, $args, $explicit); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Di/ClassInspector.php: -------------------------------------------------------------------------------- 1 | hasMethod($method)) { 36 | if ($method === '__construct') { 37 | return $dependencies; 38 | } 39 | throw new MethodNotFoundException($r->getName(), $method); 40 | } 41 | 42 | $rp = $r->getMethod($method)->getParameters(); 43 | 44 | foreach ($rp as $p) { 45 | $prop = $p->name; 46 | 47 | $dependencies[$prop] = []; 48 | $dependencies[$prop]['optional'] = $p->isOptional(); 49 | 50 | try { 51 | $class = $p->getClass(); 52 | if (isset($class)) { 53 | $dependencies[$prop]['class'] = $class->name; 54 | } 55 | } catch (\Exception $e) { 56 | $dependencies[$prop]['error'] = $e->getMessage(); 57 | } 58 | } 59 | 60 | return $dependencies; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Di/ClassInstantiator.php: -------------------------------------------------------------------------------- 1 | newInstanceArgs($args); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Di/ClassResolver.php: -------------------------------------------------------------------------------- 1 | inspector = new ClassInspector(); 44 | $this->explicit = new ExplicitResolver(); 45 | $this->iresolver = new InterfaceResolver(); 46 | } 47 | 48 | /** 49 | * Resolve class arguments dependencies 50 | * 51 | * @param mixed $class 52 | * @param Container $container 53 | * @param array $args 54 | * @param null $explicit 55 | * @return array 56 | * @throws AmbiguousResolutionException 57 | * @throws ClassDefinitionNotFoundException 58 | * @throws InterfaceNotFoundException 59 | * @throws \ReflectionException 60 | */ 61 | public function resolve($class, Container $container, array $args = [], $explicit = null) 62 | { 63 | $method = '__construct'; 64 | 65 | if (is_array($class)) { 66 | if (count($class) != 2) { 67 | throw new InvalidArgumentException('Expecting a valid callback definition'); 68 | } 69 | // treat $class as a callback 70 | $method = $class[1]; 71 | $class = $class[0]; 72 | } 73 | 74 | $dependencies = $this->inspector->inspect($class, $method); 75 | $class_args = []; 76 | $class_count = 0; 77 | $i = 0; 78 | 79 | foreach ($dependencies as $d) { 80 | if (isset($d['error'])) { 81 | throw new Exception($d['error']); 82 | } 83 | 84 | // its a class or an interface 85 | if (isset($d['class'])) { 86 | $name = $d['class']; 87 | ++$class_count; 88 | 89 | // look for object in explicit dependency declaration 90 | $result = $this->explicit->resolve($name, $container, $explicit); 91 | if ($result !== null) { 92 | $class_args[] = $result; 93 | } elseif ($container->has($name)) { 94 | // check if container has a stored instance 95 | $class_args[] = $container->get($name); 96 | } elseif (!$d['optional'] && interface_exists($name)) { 97 | // otherwise check if we are dealing with an interface dependency 98 | $class_args[] = $this->iresolver->resolve($name, $container, $explicit); 99 | } elseif (!$d['optional']) { 100 | // or resolve dependency by trying to instantiate object class name string 101 | $child_args = []; 102 | if (array_key_exists($name, $args)) { 103 | $child_args = $args[$name]; 104 | } 105 | $class_args[] = $container->create($name, $child_args, $explicit); 106 | } 107 | } elseif (array_key_exists($i - ($class_count), $args)) { 108 | // everything else that is not a type of class or interface 109 | $class_args[] = $args[$i - $class_count]; 110 | } 111 | 112 | ++$i; 113 | } 114 | 115 | return $class_args; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Di/Exception/AmbiguousResolutionException.php: -------------------------------------------------------------------------------- 1 | bindingResolver = new BindingResolver(); 27 | } 28 | 29 | /** 30 | * @param string $interface 31 | * @param Container $container 32 | * @param array $explicit 33 | * @return mixed|object 34 | * @throws AmbiguousResolutionException 35 | * @throws Exception\ClassDefinitionNotFoundException 36 | * @throws InterfaceNotFoundException 37 | * @throws \ReflectionException 38 | */ 39 | public function resolve(string $interface, Container $container, $explicit = []) 40 | { 41 | // try to find a match in the container for a class or an interface 42 | if ($container->hasInterface($interface)) { 43 | $instance = $container->getInterface($interface); 44 | if (is_array($instance)) { 45 | if (empty($explicit) || !array_key_exists($interface, $explicit)) { 46 | throw new AmbiguousResolutionException($interface, $instance); 47 | } 48 | return $container->get($explicit[$interface]); 49 | } 50 | return $container->get($instance); 51 | } 52 | 53 | // try to find a match in container definition 54 | if ($container->hasDefinition($interface)) { 55 | $definition = $container->getDefinition($interface); 56 | return $this->bindingResolver->resolve($definition, $container, [], $explicit); 57 | } 58 | 59 | throw new InterfaceNotFoundException($interface); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Di/README.md: -------------------------------------------------------------------------------- 1 | # Peak Peak/Di 2 | 3 | ##### PSR-11 Dependency Injection Container 4 | 5 | This package allows you to standardize and centralize the way objects are constructed in your application. 6 | 7 | ### Installation 8 | 9 | ``` 10 | $ composer require peak/di 11 | ``` 12 | 13 | ### Documentation 14 | 15 | See https://peakphp.github.io/docs/di -------------------------------------------------------------------------------- /src/Di/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peak/di", 3 | "description": "Peak Dependency Injection Container Component", 4 | "keywords": ["peak", "di", "dependency injection", "container", "psr-11"], 5 | "license": "MIT", 6 | "homepage": "https://peakframework.com", 7 | "support": { 8 | "issues": "https://github.com/peakphp/framework/issues", 9 | "source": "https://github.com/peakphp/framework" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Francois Lajoie", 14 | "email": "francois@peakframework.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.2", 19 | "psr/container": "^1.0" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Peak\\Di\\": "" 24 | } 25 | }, 26 | "extra": { 27 | "branch-alias": { 28 | "dev-master": "4.0-dev" 29 | } 30 | }, 31 | "minimum-stability": "dev", 32 | "prefer-stable": true 33 | } -------------------------------------------------------------------------------- /src/Http/Exception/BodyParserException.php: -------------------------------------------------------------------------------- 1 | stack = $stack; 25 | } 26 | 27 | /** 28 | * @return Stack 29 | */ 30 | public function getStack() 31 | { 32 | return $this->stack; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Http/Exception/JsonBodyParserException.php: -------------------------------------------------------------------------------- 1 | stack = $stack; 25 | } 26 | 27 | /** 28 | * @return Stack 29 | */ 30 | public function getStack() 31 | { 32 | return $this->stack; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Http/Middleware/CallableMiddleware.php: -------------------------------------------------------------------------------- 1 | callable = $callable; 29 | } 30 | 31 | /** 32 | * Process an incoming server request and return a response, optionally delegating 33 | * response creation to a handler. 34 | * 35 | * @param ServerRequestInterface $request 36 | * @param RequestHandlerInterface $handler 37 | * @return ResponseInterface 38 | */ 39 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 40 | { 41 | $fn = $this->callable; 42 | if (!$this->callable instanceof Closure) { 43 | $return = call_user_func_array($fn, [$request, $handler]); 44 | if (null === $return) { 45 | return $handler->handle($request); 46 | } 47 | return $return; 48 | } 49 | return $fn($request, $handler); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Http/Middleware/JsonBodyParserMiddleware.php: -------------------------------------------------------------------------------- 1 | hasHeader('content-type') && 24 | $request->getHeaderLine('content-type') === 'application/json') { 25 | 26 | $json = json_decode((string)$request->getBody(), true); 27 | if (json_last_error() !== 0) { 28 | throw new JsonBodyParserException(lcfirst(json_last_error_msg())); 29 | } 30 | $request = $request->withParsedBody($json); 31 | } 32 | // call the next 33 | return $handler->handle($request); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Http/README.md: -------------------------------------------------------------------------------- 1 | # Peak/Http 2 | 3 | Http foundation component compatible with PSR-7, PSR-11 and PSR-15. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ composer require peak/http 9 | ``` 10 | 11 | ### Documentation 12 | 13 | See https://peakphp.github.io/docs/http -------------------------------------------------------------------------------- /src/Http/Request/BlankRequest.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 22 | } 23 | 24 | /** 25 | * @return mixed 26 | */ 27 | public function getHandler() 28 | { 29 | return $this->handler; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Request/Exception/InvalidHandlerException.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 39 | } 40 | 41 | /** 42 | * @return mixed 43 | */ 44 | public function getHandler() 45 | { 46 | return $this->handler; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Http/Request/Exception/UnresolvableHandlerException.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 22 | } 23 | 24 | /** 25 | * @return mixed 26 | */ 27 | public function getHandler() 28 | { 29 | return $this->handler; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Request/HandlerResolver.php: -------------------------------------------------------------------------------- 1 | container = $container; 34 | } 35 | 36 | /** 37 | * @param mixed $handler 38 | * @return mixed|CallableMiddleware 39 | */ 40 | public function resolve($handler) 41 | { 42 | if ($handler instanceof Stack || 43 | $handler instanceof MiddlewareInterface || 44 | $handler instanceof RequestHandlerInterface) { 45 | return $handler; 46 | } 47 | 48 | if (is_callable($handler)) { 49 | return $this->resolveCallable($handler); 50 | } 51 | 52 | if (is_string($handler)) { 53 | $handler = $this->resolveString($handler); 54 | if (is_callable($handler) && !$handler instanceof MiddlewareInterface) { 55 | return $this->resolveCallable($handler); 56 | } 57 | return $handler; 58 | } 59 | 60 | throw new UnresolvableHandlerException($handler); 61 | } 62 | 63 | /** 64 | * @param callable $handler 65 | * @return CallableMiddleware 66 | */ 67 | protected function resolveCallable(callable $handler) 68 | { 69 | return new CallableMiddleware($handler); 70 | } 71 | 72 | /** 73 | * @param string $handler 74 | * @return mixed 75 | */ 76 | protected function resolveString(string $handler) 77 | { 78 | if (!class_exists($handler)) { 79 | throw new HandlerNotFoundException($handler); 80 | } 81 | 82 | // resolve using a container 83 | if (null !== $this->container) { 84 | return $this->container->get($handler); 85 | } 86 | 87 | // manual instantiation, work only with empty constructor classes 88 | return new $handler(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Http/Request/PreRoute.php: -------------------------------------------------------------------------------- 1 | getMethod() && $this->getMethod() !== $request->getMethod()) { 29 | return false; 30 | } 31 | 32 | // compile pseudo route syntax {param} and {param}:type into valid regex 33 | $routeRegex = (new RouteExpression($this->getPath()))->getRegex(); 34 | 35 | // look to match the route 36 | $this->pregMatch('#^'.$routeRegex.'#', $request->getUri()->getPath()); 37 | 38 | return !empty($this->getMatches()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Http/Request/Route.php: -------------------------------------------------------------------------------- 1 | method = isset($method) ? strtoupper(trim($method)) : null; 53 | $this->path = $path; 54 | $this->stack = $stack; 55 | } 56 | 57 | /** 58 | * @param Stack $parentStack 59 | */ 60 | public function setParent(Stack $parentStack) 61 | { 62 | $this->parentStack = $parentStack; 63 | $this->stack->setParent($parentStack); 64 | } 65 | 66 | /** 67 | * @return Stack 68 | */ 69 | public function getParent(): Stack 70 | { 71 | return $this->parentStack; 72 | } 73 | 74 | /** 75 | * @param ServerRequestInterface $request 76 | * @return bool 77 | */ 78 | public function match(ServerRequestInterface $request): bool 79 | { 80 | if (null !== $this->method && $this->method !== $request->getMethod()) { 81 | return false; 82 | } 83 | 84 | // compile pseudo route syntax {param} and {param}:type into valid regex 85 | $routeRegex = (new RouteExpression($this->path))->getRegex(); 86 | 87 | // look to match the route 88 | $this->pregMatch('#^'.$routeRegex.'$#', $request->getUri()->getPath()); 89 | return !empty($this->matches); 90 | } 91 | 92 | /** 93 | * Process an incoming server request and return a response, optionally delegating 94 | * response creation to a handler. 95 | * 96 | * @param ServerRequestInterface $request 97 | * @param RequestHandlerInterface $handler 98 | * @return ResponseInterface 99 | */ 100 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 101 | { 102 | $isMatching = $this->match($request); 103 | 104 | // add regex matches(aka route arguments) to the request. 105 | $routeArgs = new RouteArgs($this->matches); 106 | foreach ($routeArgs->toArray() as $name => $value) { 107 | $request = $request->withAttribute($name, $value); 108 | } 109 | // $request->args works but "pollute" the request object and will be removed in the next major version. 110 | // use the PSR-7 method $request->getAttribute() instead for retrieving an route argument 111 | $request->args = $routeArgs; 112 | 113 | if (!$isMatching) { 114 | return $this->processParent($request, $handler); 115 | } 116 | return $this->stack->handle($request); 117 | } 118 | 119 | /** 120 | * Handle the request and return a response. 121 | * 122 | * @param ServerRequestInterface $request 123 | * @return ResponseInterface 124 | */ 125 | public function handle(ServerRequestInterface $request): ResponseInterface 126 | { 127 | return $this->process($request, $this); 128 | } 129 | 130 | /** 131 | * @param ServerRequestInterface $request 132 | * @param RequestHandlerInterface $handler 133 | * @return ResponseInterface 134 | */ 135 | public function processParent(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 136 | { 137 | if (!isset($this->parentStack)) { 138 | throw new StackEndedWithoutResponseException($this); 139 | } 140 | return $this->parentStack->process($request, $handler); 141 | } 142 | 143 | /** 144 | * @return string|null 145 | */ 146 | public function getMethod(): ?string 147 | { 148 | return $this->method; 149 | } 150 | 151 | /** 152 | * @return string 153 | */ 154 | public function getPath(): string 155 | { 156 | return $this->path; 157 | } 158 | 159 | /** 160 | * @return array 161 | */ 162 | public function getHandlers(): array 163 | { 164 | return $this->stack->getHandlers(); 165 | } 166 | 167 | /** 168 | * @return array 169 | */ 170 | protected function getMatches(): array 171 | { 172 | return $this->matches; 173 | } 174 | 175 | /** 176 | * @param string $pattern 177 | * @param string $path 178 | * @return array 179 | */ 180 | protected function pregMatch(string $pattern, string $path): array 181 | { 182 | preg_match($pattern, $path, $this->matches); 183 | return $this->matches; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Http/Request/RouteArgs.php: -------------------------------------------------------------------------------- 1 | args = $args; 23 | } 24 | 25 | /** 26 | * @deprecated 27 | * @return array 28 | */ 29 | public function raw() 30 | { 31 | return $this->args; 32 | } 33 | 34 | /** 35 | * @return array 36 | */ 37 | public function toArray() 38 | { 39 | return $this->args; 40 | } 41 | 42 | /** 43 | * @param string $name 44 | * @return mixed 45 | */ 46 | public function __get(string $name) 47 | { 48 | return $this->args[$name]; 49 | } 50 | 51 | /** 52 | * @param string $name 53 | * @return bool 54 | */ 55 | public function __isset(string $name) 56 | { 57 | return isset($this->args[$name]); 58 | } 59 | 60 | /** 61 | * Offset to retrieve 62 | * 63 | * @param string $offset 64 | * @return mixed 65 | */ 66 | public function offsetGet($offset) 67 | { 68 | return isset($this->args[$offset]) ? $this->args[$offset] : null; 69 | } 70 | 71 | /** 72 | * @param string $offset 73 | * @return bool 74 | */ 75 | public function offsetExists($offset): bool 76 | { 77 | return isset($this->args[$offset]); 78 | } 79 | 80 | /** 81 | * @param mixed $offset 82 | * @param mixed $value 83 | */ 84 | public function offsetSet($offset, $value) 85 | { 86 | $this->args[$offset] = $value; 87 | } 88 | 89 | /** 90 | * @param mixed $offset 91 | */ 92 | public function offsetUnset($offset) 93 | { 94 | unset($this->args[$offset]); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Http/Request/RouteExpression.php: -------------------------------------------------------------------------------- 1 | '[^\/]+', 31 | ':negnum' => '-[0-9]+', 32 | ':posnum' => '[0-9]+', 33 | ':num' => '-?[0-9]+', 34 | // for float pattern, string like .5 is not valid, it must be 0.5 35 | ':negfloat' => '-([0-9]+\.[0-9]+|[0-9]+)', 36 | ':posfloat' => '([0-9]+\.[0-9]+|[0-9]+)', 37 | ':float' => '[-+]?([0-9]+\.[0-9]+|[0-9]+)', 38 | // chars, numbers, -, _ and + only 39 | ':permalink' => '[a-zA-Z0-9+_-]+', 40 | ':alphanum' => '[a-zA-Z0-9]+', 41 | ':alpha' => '[a-zA-Z]+', 42 | ':year' => '[12][0-9]{3}', // 1000 to 2999 43 | ':month' => '0[1-9]|1[012]|[1-9]', // valid ex: 07, 7, 12, 31 44 | ':day' => '[12][0-9]|3[01]|0?[1-9]', // valid ex: 2, 12, 02, 15, 31 45 | ]; 46 | 47 | /** 48 | * RouteExpression constructor. 49 | * @param string $expression 50 | */ 51 | public function __construct(string $expression) 52 | { 53 | $this->expression = $expression; 54 | $this->regex = $expression; 55 | $this->compile(); 56 | } 57 | 58 | /** 59 | * @return string 60 | */ 61 | public function getRegex(): string 62 | { 63 | return $this->regex; 64 | } 65 | 66 | /** 67 | * @return string 68 | */ 69 | public function getExpression(): string 70 | { 71 | return $this->expression; 72 | } 73 | 74 | /** 75 | * Compile expression to valid regex 76 | */ 77 | private function compile() 78 | { 79 | // replace pseudo {param}:type syntax to valid regex 80 | if (strpos($this->regex, '}:') !== false) { 81 | $this->regex = preg_replace('#\{([a-zA-Z0-9_-]+)\}:([a-z]+)#', '(?P<$1>:$2)', $this->expression); 82 | $this->regex = str_replace( 83 | array_keys($this->regexExpression), 84 | array_values($this->regexExpression), 85 | $this->regex 86 | ); 87 | } 88 | 89 | // replace pseudo {param} syntax without type to valid regex 90 | if (strpos($this->regex, '}') !== false) { 91 | $this->regex = preg_replace('#\{([a-zA-Z_]+)\}#', '(?P<$1>[^\/]+)', $this->regex); 92 | } 93 | 94 | // allow optional trailing slash at the end 95 | if (substr($this->regex, -1) !== '/') { 96 | $this->regex .= '[\/]?'; 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /src/Http/Request/RouteServerRequest.php: -------------------------------------------------------------------------------- 1 | request = $request; 32 | if (isset($request->args)) { 33 | $this->routeArgs = $request->args; 34 | } 35 | } 36 | 37 | /** 38 | * @param string $name 39 | * @param string|null $type 40 | * @return bool 41 | */ 42 | public function hasArg(string $name, string $type = null): bool 43 | { 44 | if (!isset($this->routeArgs)) { 45 | return false; 46 | } 47 | $exists = isset($this->routeArgs->$name); 48 | if (!$exists || !isset($type)) { 49 | return $exists; 50 | } 51 | 52 | return (gettype($this->routeArgs->$name) === $type) ? true : false; 53 | } 54 | 55 | /** 56 | * @param string $name 57 | * @param mixed $default 58 | * @return mixed|null 59 | */ 60 | public function getArg(string $name, $default = null) 61 | { 62 | if (!isset($this->routeArgs) || !$this->hasArg($name)) { 63 | return $default; 64 | } 65 | return $this->routeArgs->$name; 66 | } 67 | 68 | /** 69 | * ---- end of wrapper ---- 70 | * The rest of methods below are only there for satisfying ServerRequestInterface 71 | */ 72 | 73 | public function getProtocolVersion() 74 | { 75 | return $this->request->getProtocolVersion(); 76 | } 77 | 78 | public function withProtocolVersion($version) 79 | { 80 | return new self($this->request->withProtocolVersion($version)); 81 | } 82 | 83 | public function getHeaders() 84 | { 85 | return $this->request->getHeaders(); 86 | } 87 | 88 | public function hasHeader($name) 89 | { 90 | return $this->request->hasHeader($name); 91 | } 92 | 93 | public function getHeader($name) 94 | { 95 | return $this->request->getHeader($name); 96 | } 97 | 98 | public function getHeaderLine($name) 99 | { 100 | return $this->request->getHeaderLine($name); 101 | } 102 | 103 | public function withHeader($name, $value) 104 | { 105 | return new self($this->request->withHeader($name, $value)); 106 | } 107 | 108 | public function withAddedHeader($name, $value) 109 | { 110 | return new self($this->request->withAddedHeader($name, $value)); 111 | } 112 | 113 | public function withoutHeader($name) 114 | { 115 | return new self($this->request->withoutHeader($name)); 116 | } 117 | 118 | public function getBody() 119 | { 120 | return $this->request->getBody(); 121 | } 122 | 123 | public function withBody(StreamInterface $body) 124 | { 125 | return new self($this->request->withBody($body)); 126 | } 127 | 128 | public function getRequestTarget() 129 | { 130 | return $this->request->getRequestTarget(); 131 | } 132 | 133 | public function withRequestTarget($requestTarget) 134 | { 135 | return new self($this->request->withRequestTarget($requestTarget)); 136 | } 137 | 138 | public function getMethod() 139 | { 140 | return $this->request->getMethod(); 141 | } 142 | 143 | public function withMethod($method) 144 | { 145 | return new self($this->request->withMethod($method)); 146 | } 147 | 148 | public function getUri() 149 | { 150 | return $this->request->getUri(); 151 | } 152 | 153 | public function withUri(UriInterface $uri, $preserveHost = false) 154 | { 155 | return new self($this->request->withUri($uri, $preserveHost)); 156 | } 157 | 158 | public function getServerParams() 159 | { 160 | return $this->request->getServerParams(); 161 | } 162 | 163 | public function getCookieParams() 164 | { 165 | return $this->request->getCookieParams(); 166 | } 167 | 168 | public function withCookieParams(array $cookies) 169 | { 170 | return new self($this->request->withCookieParams($cookies)); 171 | } 172 | 173 | public function getQueryParams() 174 | { 175 | return $this->request->getQueryParams(); 176 | } 177 | 178 | public function withQueryParams(array $query) 179 | { 180 | return new self($this->request->withQueryParams($query)); 181 | } 182 | 183 | public function getUploadedFiles() 184 | { 185 | return $this->request->getUploadedFiles(); 186 | } 187 | 188 | public function withUploadedFiles(array $uploadedFiles) 189 | { 190 | return new self($this->request->withUploadedFiles($uploadedFiles)); 191 | } 192 | 193 | public function getParsedBody() 194 | { 195 | return $this->request->getParsedBody(); 196 | } 197 | 198 | public function withParsedBody($data) 199 | { 200 | return new self($this->request->withParsedBody($data)); 201 | } 202 | 203 | public function getAttributes() 204 | { 205 | return $this->request->getAttributes(); 206 | } 207 | 208 | public function getAttribute($name, $default = null) 209 | { 210 | return $this->request->getAttribute($name, $default); 211 | } 212 | 213 | public function withAttribute($name, $value) 214 | { 215 | return new self($this->request->withAttribute($name, $value)); 216 | } 217 | 218 | public function withoutAttribute($name) 219 | { 220 | return new self($this->request->withoutAttribute($name)); 221 | } 222 | } -------------------------------------------------------------------------------- /src/Http/Response/Emitter.php: -------------------------------------------------------------------------------- 1 | assertNoPreviousOutput(); 36 | $this->emitHeaders($response); 37 | $this->emitStatusLine($response); 38 | } 39 | 40 | $this->emitBody($response); 41 | 42 | return true; 43 | } 44 | 45 | /** 46 | * Emit the message body. 47 | * 48 | * @param ResponseInterface $response 49 | */ 50 | private function emitBody(ResponseInterface $response) 51 | { 52 | echo $response->getBody(); 53 | } 54 | 55 | /** 56 | * Checks to see if content has previously been sent. 57 | * 58 | * If either headers have been sent or the output buffer contains content, 59 | * raises an exception. 60 | * 61 | * @throws \RuntimeException if headers have already been sent. 62 | * @throws \RuntimeException if output is present in the output buffer. 63 | */ 64 | private function assertNoPreviousOutput() 65 | { 66 | // headers_sent() will return false, even if you sent something to the ouptut using print() or header(), 67 | // if output_buffering is different from Off in you php.ini, and the length of what you sent does not 68 | // exceed the size of output_buffering. 69 | if (headers_sent()) { 70 | throw new \RuntimeException('Unable to emit response; headers already sent'); 71 | } 72 | 73 | /* 74 | if (ob_get_level() > 0 && ob_get_length() > 0) { 75 | throw new \RuntimeException('Output has been emitted previously; cannot emit response'); 76 | } 77 | */ 78 | } 79 | 80 | /** 81 | * Emit the status line. 82 | * 83 | * Emits the status line using the protocol version and status code from 84 | * the response; if a reason phrase is available, it, too, is emitted. 85 | * 86 | * It is important to mention that this method should be called after 87 | * `emitHeaders()` in order to prevent PHP from changing the status code of 88 | * the emitted response. 89 | * 90 | * @param ResponseInterface $response 91 | * 92 | * @see \Zend\Diactoros\Response\SapiEmitterTrait::emitHeaders() 93 | */ 94 | private function emitStatusLine(ResponseInterface $response) 95 | { 96 | $reasonPhrase = $response->getReasonPhrase(); 97 | $statusCode = $response->getStatusCode(); 98 | 99 | header(sprintf( 100 | 'HTTP/%s %d%s', 101 | $response->getProtocolVersion(), 102 | $statusCode, 103 | ($reasonPhrase ? ' ' . $reasonPhrase : '') 104 | ), true, $statusCode); 105 | } 106 | 107 | /** 108 | * Emit response headers. 109 | * 110 | * Loops through each header, emitting each; if the header value 111 | * is an array with multiple values, ensures that each is sent 112 | * in such a way as to create aggregate headers (instead of replace 113 | * the previous). 114 | * 115 | * @param ResponseInterface $response 116 | */ 117 | private function emitHeaders(ResponseInterface $response) 118 | { 119 | $statusCode = $response->getStatusCode(); 120 | 121 | foreach ($response->getHeaders() as $header => $values) { 122 | $name = $this->filterHeader($header); 123 | $first = $name === 'Set-Cookie' ? false : true; 124 | foreach ($values as $value) { 125 | header(sprintf( 126 | '%s: %s', 127 | $name, 128 | $value 129 | ), $first, $statusCode); 130 | $first = false; 131 | } 132 | } 133 | } 134 | 135 | /** 136 | * Filter a header name to wordcase 137 | * 138 | * @param string $header 139 | * @return string 140 | */ 141 | private function filterHeader($header) 142 | { 143 | $filtered = str_replace('-', ' ', $header); 144 | $filtered = ucwords($filtered); 145 | return str_replace(' ', '-', $filtered); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Http/Stack.php: -------------------------------------------------------------------------------- 1 | handlers = $handlers; 57 | $this->handlerResolver = $handlerResolver; 58 | } 59 | 60 | /** 61 | * @param \Peak\Blueprint\Http\Stack $parentStack 62 | */ 63 | public function setParent(\Peak\Blueprint\Http\Stack $parentStack) 64 | { 65 | $this->parentStack = $parentStack; 66 | } 67 | 68 | /** 69 | * @return \Peak\Blueprint\Http\Stack 70 | */ 71 | public function getParent(): \Peak\Blueprint\Http\Stack 72 | { 73 | return $this->parentStack; 74 | } 75 | 76 | /** 77 | * @return array 78 | */ 79 | public function getHandlers(): array 80 | { 81 | return $this->handlers; 82 | } 83 | 84 | /** 85 | * Process an incoming server request and return a response, optionally delegating 86 | * response creation to a handler. 87 | * 88 | * @param ServerRequestInterface $request 89 | * @param RequestHandlerInterface $handler 90 | * @return ResponseInterface 91 | */ 92 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 93 | { 94 | $this->nextHandler = (isset($this->nextHandler)) ? next($this->handlers) : current($this->handlers); 95 | 96 | // nextHandler is a stack 97 | if ($this->nextHandler instanceof \Peak\Blueprint\Http\Stack) { 98 | return $this->returnResponse($this->handleStack($this->nextHandler, $request)); 99 | } 100 | 101 | // no more handlers, look for parent 102 | if ($this->nextHandler === false && isset($this->parentStack)) { 103 | return $this->returnResponse($this->parentStack->process($request, $this->parentStack)); 104 | } elseif ($this->nextHandler === false) { 105 | throw new StackEndedWithoutResponseException($this); 106 | } 107 | 108 | // resolve nextHandler if not already a object 109 | $handlerInstance = $this->nextHandler; 110 | if (is_callable($handlerInstance) || !is_object($handlerInstance)) { 111 | $handlerInstance = $this->handlerResolver->resolve($this->nextHandler); 112 | } 113 | 114 | // how to call the handler (MiddlewareInterface or RequestHandlerInterface) 115 | if ($handlerInstance instanceof MiddlewareInterface) { 116 | return $this->returnResponse($handlerInstance->process($request, $this)); 117 | } elseif($handlerInstance instanceof RequestHandlerInterface) { 118 | return $this->returnResponse($handlerInstance->handle($request)); 119 | } 120 | 121 | // at this point, the handler is not good 122 | throw new InvalidHandlerException($handlerInstance); 123 | } 124 | 125 | /** 126 | * Handle the request and return a response. 127 | * 128 | * @param ServerRequestInterface $request 129 | * @return ResponseInterface 130 | */ 131 | public function handle(ServerRequestInterface $request): ResponseInterface 132 | { 133 | return $this->process($request, $this); 134 | } 135 | 136 | /** 137 | * Handle a child stack 138 | * 139 | * @param \Peak\Blueprint\Http\Stack $stack 140 | * @param ServerRequestInterface $request 141 | * @return ResponseInterface 142 | */ 143 | protected function handleStack(\Peak\Blueprint\Http\Stack $stack, ServerRequestInterface $request): ResponseInterface 144 | { 145 | $stack->setParent($this); 146 | return $stack->handle($request); 147 | } 148 | 149 | /** 150 | * Reset the stack before returning the response, 151 | * This allow the stack to be re-handle without throwing exception 152 | * @param ResponseInterface $response 153 | * @return ResponseInterface 154 | */ 155 | protected function returnResponse(ResponseInterface $response): ResponseInterface 156 | { 157 | $this->nextHandler = null; 158 | reset($this->handlers); 159 | return $response; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/Http/StackFactory.php: -------------------------------------------------------------------------------- 1 | handlerResolver = $handlerResolver; 24 | } 25 | 26 | /** 27 | * @param array $handlers 28 | * @param ResourceResolver|null $handlerResolver 29 | * @return Stack 30 | */ 31 | public function create(array $handlers, ResourceResolver $handlerResolver = null) 32 | { 33 | $handlerResolver = $handlerResolver ?? $this->handlerResolver; 34 | $stack = new Stack($handlers, $handlerResolver); 35 | return $stack; 36 | } 37 | } -------------------------------------------------------------------------------- /src/Http/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peak/http", 3 | "description": "Peak Http Component", 4 | "type": "library", 5 | "keywords": ["peak", "http", "psr-7", "psr-15"], 6 | "license": "MIT", 7 | "homepage": "https://peakframework.com", 8 | "support": { 9 | "issues": "https://github.com/peakphp/framework/issues", 10 | "source": "https://github.com/peakphp/framework" 11 | }, 12 | "authors": [ 13 | { 14 | "name": "Francois Lajoie", 15 | "email": "francois@peakframework.com" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=7.2", 20 | "psr/http-message": "^1.0", 21 | "psr/http-server-handler": "^1.0", 22 | "psr/http-server-middleware": "^1.0", 23 | "peak/blueprint": "^4.0" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "Peak\\Http\\": "" 28 | }, 29 | "exclude-from-classmap": [ 30 | "/Tests/" 31 | ] 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true, 35 | "extra": { 36 | "branch-alias": { 37 | "dev-master": "4.0-dev" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /supporters.txt: -------------------------------------------------------------------------------- 1 | Valérie Bastille 2 | Dany Bouchard --------------------------------------------------------------------------------