pier-infor/nymfonya

Nymfonya Rest App

1.0.6 2020-12-30 18:25 UTC

This package is auto-updated.

Last update: 2024-11-08 07:37:32 UTC


README

is not a Symfony app...¯\(ツ)

TravsisBadgeBuild CoverageCi ScrutinizerCq Latest Stable Version Total Downloads Latest Unstable Version

logo

ʕʘ̅͜ʘ̅ʔ Why

I want :

  • Play rest only.
  • Slim conf.
  • Console features.
  • Low memory footprint.
  • Minimize load files (max 150) for any final purpose.
  • Response time always lower than 10ms.
  • All testable.
  • Packageable.
  • CI compliant.

👀 Introduction

Involved into backend projects based on Symfony or Laravel with Angular as front, I was frustrated about performances. I turned to micro frameworks like Silex, Slim...to improve more. A lot of them have been abandonned in favor of majors actors. I can't figure why, maybe a lake of maintainers or people prefered something more generalistic and large feature oriented.

Composer package manager, introduced in 2011 changed the way to design php app with the concept of package and dependencie. Autoloading RFCs as PSR-0 then PSR-4 were introduced a bit later aligning packages with namespace as vendor/feature.
Php is still interpreted and requires sources to be loaded, parsed(AST), assembled(as opcode) then executed in Zend VM @ request time. Some companies tried to compile source with JIT to speed up these steps (HipHop, HHVM) but did not ensure compatibility with the native source... 😞 !

😿 Situation

To speed up packages's dependencies Composer introduced optimization options to build a 'resource catalog' by namespace. To speed up the interpretation process php introduced Opcache as opcode cache but first run is still slower. Despite all these benefits, some frameworks require around 3K files to boot, multiplying I/O on storage device and wasting memory.
This is frustrating 😡 !
We can do better, don't we ?

🌊 Best practices

  • Instanciate objects once.
  • Inject container everywhere.
  • Type hinting usage when we can (better with php => 7.3).
  • Avoid slow processes as Reflection when we can.
  • Prohibit functional annotations.
  • Log everything (Monolog) by env even errors.
  • Responds minimal error structure with error code and message even no error.
  • No magic methods.
  • No globals nor define.
  • Avoid static.
  • Declare class constant in Interfaces.

🐴 Arch

  • 😎 rest App.
  • 🐠 Extra packages as features (unsupported).
  • 🍺 I would say Mvc but I love to forget about controllers and use Closure instead.
  • 🌀 Configs in config/$env.
  • 🐚 Run with php >= 7.0.

Config

  • 🐟 nymfonya-config external package dependencie (supported).
  • Config manager.
  • Keep config as associative arrays.
  • Isolate by Env.
  • Testable : check autoload-dev psr-4 for namespace.

Container

  • 🐟 nymfonya-container external package dependencie (supported).
  • not PSR-11.
  • Instanciate classes with injected params.
  • Roll-in precedence order.
  • Container config is associative array (classname => params).
  • Testable : check autoload-dev psr-4 for namespace.

Kernel & Http Foundation

  • 🐟 nymfonya-http-foundation external package (supported).
  • Load Config.
  • Run Kernel in server and cli mode.
  • Instanciate services from container.
  • Setup middlewares in design order (before/core/after).
  • Run middleware sequence when routes match.
  • Execute controler action as middleware core Closure.
  • Testable : check autoload-dev psr-4 for namespace.

Services

  • Config
  • Logger (Monolog)
  • Request (not PSR-7 yet)
  • Routes
  • Router
  • Controller
  • Response (nor PSR-7 yet)

Rules

  • Request is 'Immutable'.
  • Container services can be modified.
  • Container injects Peel method middleware as first param.
  • Container injects Constructor controller method as first param.
  • Response modified at any time both by middlewares and controllers.

Middlewares

Middleware stack must contain at least one.

  • Cors : play with cross domain request (\ö/ Angular OPTIONS preflight).
  • Jwt : deal with auth based on Jwt Token.
  • After : terminator to ensure middleware logic is fully functional.

💥 Usage

Server

Start

From root path

composer run start

Play

Postman recommended.

Middleware Jwt
#!/bin/bash
curl -v \
	--request POST \
	--url http://localhost:8888/api/v1/auth/login \
	--header 'Cache-Control: no-cache' \
	--header 'Connection: keep-alive' \
	--header 'Content-Type: application/json' \
	--header 'Host: localhost:8888' \
	--header 'cache-control: no-cache' \
	--data '{"login": "admin@domain.tld","password": "adminadmin"}'

Free access

Bearer Token required

Middleware Restufl
Middleware Cors & After

Enabled for all endpoints.

Cli

Ideal for batch mode, cron stuffs. From the root of the project. This does not require any server to be running. Middleware Jwt is disable.
Use jq to pretty or filter json in console.

Sample Auth

php src/index.php '/api/v1/auth/login?login=admin@domain.tld&password=adminadmin' | jq

Sample Stat Opcache

php src/index.php '/api/v1/stat/opcache' | jq

Sample OpenApi Generator

php src/index.php '/config/swaggerdoc'

🌟 Code

Catalog

find ./src -name *.php | grep -E -v 'vendor|config|test' | wc -l

Complexity

  • run phploc
composer run cq

Mess

  • run phpmd (mess detector)
composer run md

😇 Tests

Run phpunit all tests.

composer run test

To get the code coverage the command below then browse coverage/index.html file.

To run xdebug driver (xdebug required), considered as fat and buggy.

composer run cov

To run phpdbg driver (phpdbg required), faster and precise.

composer run covdbg

You can also use a very fast driver PCOV.
Check S.Bergmann travis config.

🔥 Security

Develop app is dev job, secure it is one other.

Dev side

Use filters and validators everywhere and never trust input params.

Devops side

Devops/SysAdmins should propose/setup solutions.

  • Prefer php-fpm to modules for flexibility.
  • Prefer Nginx to Apache for performance and http/2.
  • Do not use php server in prod (single threaded).
  • For static content setup varnish.

I would recommend third part services if exposed as below :

  • apache(2) mod_security(2) to let OWASP breaks and logs most common attacks.
  • Nginx ModSecurity.
  • fail2ban in conjonction with mod_security(2) or ModSecurity to ban recurent attackers.

This should stop most common attacks but do not exempt your app to be bullet proofed.

Check your app logs with Tail for Monolog make logs reading easier.

Change some settings in php.ini

🐹 Todo

Core

  • Refacto Request to use behind a proxy.
  • Console tool.
  • More middlewares before Acl, Waf, after Transfos...
  • Implement Controller forward method.
  • Implement Response redirect 30X.

Extra packages

  • Model with doctrine, mongodb, elasticsearch.
  • Mail with swiftmailer.

🐔 Naming convention

  • (I)nterface.
  • (T)rait.

Controller action must be declared as final public.

👅 Conclusion

I'm aware it requires improvements on many points and I'm sorry to break most PSR.(⌐■_■)
Performance rocks (response 15ms for a 40X at first run, then 6ms).

I attempted to keep the code :

  • short.
  • readable and annotated.
  • testable (Wip 93% methods coverage).
  • explicit.
  • decoupled.
  • linted.
  • type hinted (insofar as php v7.X compatibility).

Can't wait trying Php v8.0 JIT with DynAsm.
It should perf x4 face to v7.4 and generate native code (VM + AOT).
You can try JIT from Php v7.4 as experimental feature (compile time) !
Star and or fork then PR to improve it.
Test it and be happy or not ¯\(ツ)

🐼 Extras