Skip to content

SignorMassimo/lynx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🐾 Lynx Framework (PHP)

PHP Version NestJS Inspired Zero Composer Dependencies Lynx CLI License


Lynx is a high-performance, modern backend framework for PHP 8.1+ that brings the modular structure, decorator (attribute) system, and recursive dependency injection of TypeScript's popular NestJS framework to the PHP ecosystem.

The core differentiator of Lynx is its zero external Composer dependency design (Zero Composer Dependencies). Its engine and components are written entirely in pure PHP, enabling you to bootstrap applications without a bloated vendor/ directory while retaining full control over your codebase. (You can still integrate standard Composer libraries at any point if required).


📖 Table of Contents


1. Installation & CLI Setup (Compilation)

Lynx applications use a dedicated command-line interface written in Rust to bootstrap projects and generate structural templates (controllers, services, modules, etc.). Pre-compiled binaries are not distributed; developers must clone and build the tool locally.

CLI Compilation and Installation Steps

  1. Clone the Repository:
    git clone https://github.com/SignorMassimo/lynx_cli
  2. Build the Binary: Make sure the Rust toolchain (Rustup/Cargo) is installed on your machine, then run the following in the repository root:
    cd lynx-cli
    cargo build --release
  3. Add to PATH: Once compilation is complete, locate the compiled executable under target/release/lynx (or lynx.exe on Windows) and add it to your system's environment variables (PATH).

CLI Command Reference

  • Create a New Project:
    lynx new project-name
    This command initializes the latest pre-configured Lynx application skeleton in the target directory.
  • Generate a Controller: lynx g controller <name>
  • Generate a Service / Provider: lynx g service <name>
  • Generate a Module: lynx g module <name>
  • Generate a Database Entity: lynx g entity <name>

2. Project Directory Structure

The standard file organization of a Lynx project is structured as follows:

├── App/                  # Core framework engine (Do not modify)
│   ├── Builder/          # Structural builders (e.g., ExceptionBuilder)
│   ├── Cache/            # Runtime cache & system log directory
│   ├── Config/           # Log & system configurations
│   ├── Core/             # Engines (Router, Request, Response, LynxApplication, etc.)
│   ├── Decorators/       # Native PHP 8 attributes (Decorators)
│   ├── Exception/        # Core system & HTTP exceptions
│   ├── Helper/           # Console, Encryption, and System Utilities
│   ├── Interface/        # Interfaces (Middleware, ExceptionFilter, etc.)
│   └── Templates/        # Default error/fallback pages (e.g., notfound.php)
├── Src/                  # Source directory for application code
│   ├── views/            # HTML/PHP view templates
│   ├── AppController.php # Core application controller
│   ├── AppMiddleware.php # Global application middleware
│   └── AppModule.php      # Main application module definition
├── php/                  # Directory for global procedural helper files
├── uploads/              # Default destination folder for uploaded files
├── .env                  # Configuration & database environment file
├── .htaccess             # Apache web server routing configuration (URL Rewrite)
└── index.php             # Application entry point (Bootstrap)

3. Core Architecture & Autoloader

A Lynx application is bootstrapped in the root index.php file by loading App/Core/AutoLoader.php:

<?php
// index.php
require_once './App/Core/AutoLoader.php';

use App\Core\LynxApplication;
use Src\AppModule;

global $app;
$app = new LynxApplication(AppModule::class);
$app->run();

Roles of AutoLoader.php

  1. Global Error & Shutdown Handling:
    • Configures PHP error reporting (E_ALL) but disables raw error rendering (display_errors = 0).
    • Registers custom handlers (set_error_handler and register_shutdown_function) to intercept notices, warnings, and fatal compilation/parse/runtime errors, mapping them into the framework's App\Core\Exception model. This yields structured JSON responses and terminal highlights for debugging.
  2. Namespace Autoloading:
    • Declares two discrete spl_autoload_register autoloader definitions to map namespaces without Composer:
      • Maps the App\ namespace prefix to the /App folder.
      • Maps the Src\ namespace prefix to the /Src folder.

4. Decorators (Attributes) Reference

Lynx uses native PHP 8 attributes to attach routing and structural metadata to classes, methods, or constructor parameters:

Modül Dekoratörü: #[Module]

Declares a modular context. Accepts a configuration array:

  • controllers: Controller classes registered in this module.
  • providers: Services / providers to be registered in this module.
  • imports (or modules): Child modules imported into this module.
  • exports: Registered providers exported for use in importing modules.
  • entities: Database schemas (Entities) to register.
  • middlewares: Module-scoped middlewares.
#[Module([
    'imports' => [UserModule::class],
    'controllers' => [AppController::class],
    'providers' => [AppService::class],
    'exports' => [AppService::class],
    'entities' => [ProductEntity::class]
])]
class AppModule {}

Denetleyici Dekoratörü: #[Controller]

Designates a class as an HTTP routing controller. Accepts a route prefix:

#[Controller('/api/v1/products')]
class ProductController { ... }

Yönlendirme Dekoratörleri: #[Get], #[Post], #[Put], #[Patch], #[Delete], #[Route]

Binds controller methods to specific HTTP verbs:

  • #[Get(string $route = '')]
  • #[Post(string $route = '')]
  • #[Put(string $route = '')]
  • #[Patch(string $route = '')]
  • #[Delete(string $route = '')]
  • #[Route(string $route, array $methods)] (For binding to multiple HTTP verbs)

Example usage:

#[Get('/detail/:id')]
public function detail(Request $req, Response $res) { ... }

Bağımlılık Enjeksiyonu Dekoratörü: #[Inject]

Triggers dependency resolution for constructor parameters.

Ara Yazılım Dekoratörü: #[Middleware]

Applies middlewares to a class or a specific method:

#[Middleware([AuthMiddleware::class])]

Veritabanı Dekoratörleri: #[Entity], #[Column]

Maps classes and properties to database tables and columns:

  • #[Entity(string $tableName)]: Binds the class to a database table.
  • #[Column(string $type, bool $nullable = false, ?int $length = null, bool $primary = false, bool $unique = false, ?string $default = null, ?array $enum = null)]: Configures column data attributes.
#[Entity('users')]
class User {
    #[Column('INT', primary: true)]
    public int $id;

    #[Column('VARCHAR', length: 150, unique: true)]
    public string $email;

    #[Column('ENUM', enum: ['admin', 'user'], default: 'user')]
    public string $role;
}

5. Dependency Injection (DI) & Encapsulation Boundaries

Lynx features a fully recursive Dependency Injection (DI) container. By type-hinting dependency classes in constructor parameters, the container resolves, instantiates, and caches (singletons) dependencies dynamically via ReflectionClass.

Scoping and Boundaries (Module Boundary Control)

Modularity is strictly enforced by the Router to protect structural integrity:

  1. A Controller or Service can only inject providers that are registered in its parent module's providers array, or exported in the exports array of another module imported into the current module.
  2. If a dependency is injected without being registered or exported correctly, the framework throws a encapsulation violation SystemException at runtime:
    • Example message: Error: The dependency 'X' needed by 'Y' is not accessible in the context of module 'Z'. You must register it as a provider or import a module that exports it.

6. HTTP Request & Response API Reference

Route methods receive instance arguments of App\Core\Request and App\Core\Response.

Request Methods & Properties

  • $req->body (property): Holds the parsed form or JSON payload as an object. application/json payloads are automatically decoded.
  • $req->params (property): Associative array containing route wildcards (e.g., id from /users/:id).
  • $req->statusCode (property): The request status code.
  • body(string $key = null): Returns the body or a specific property of the body.
  • isJson(): bool: Checks if the Content-Type is application/json.
  • isPost(): bool / isGet(): bool: Verifies the HTTP verb.
  • getQuery(string $key = null): Retrieves query parameter fields (?search=foo).
  • getCookie(string $name): ?string: Returns the value of a cookie.
  • hasCookie(string $name): bool: Verifies cookie presence.

Response Methods & Properties

  • render(string $view, array $data = []): self: Evaluates and includes the view template from Src/views/{$view}.php. Automatically injects these variables:
    • $protocol: http or https
    • $hostname: Server hostname (HTTP_HOST)
    • $server_url: Root URL of the site
    • $public: URL path mapping to /uploads/
    • $public_cdn: Prefix for serving uploaded files via /cdn?file=
    • $csrf: HTML string containing a hidden CSRF token input field (<input type="hidden" ...>)
  • send($data): self: Echoes raw output and terminates the process.
  • json($data): self: Sets Content-Type: application/json and returns the data encoded in JSON.
  • redirect(string $url, array $data = []): self: Redirects to a URL. The second argument can pass flash state (redirect_data) to the destination.
  • back(): self: Redirects back to the referrer page (HTTP_REFERER).
  • plainText(string $text): self: Renders flat text output.
  • xml($data): self: Converts array maps into XML formats.
  • jsonp(string $callback, $data): self: Wraps JSON outputs in a JS callback function.
  • download(string $filePath, string $downloadName = null): void: Triggers file transfer downloads.
  • uploadFile(string $inputName, string $destination): self|null: Moves uploaded files after validating.
  • setCookie(...) / deleteCookie(...): Cookie management methods.
  • setFlash(string $key, $message) / getFlash(string $key): Handles session-based one-time flash messages.
  • setStatusCode(int $code): self: Set response HTTP code.
  • setHeader(string $name, string $value): self: Adds custom headers.

7. Database Engine & ORM (Repository Pattern)

Lynx features a built-in ORM that dynamically handles database migrations and mapping.

Auto Schema DDL Generation

When DB_STATUS=active in .env, LynxQuery inspects all Entity classes tesciled inside modules.

  • If a table is missing, it parses column metadata (type, nullable, length, primary, unique, default, enum) and executes CREATE TABLE IF NOT EXISTS commands automatically.

Repository<T> Reference

Database actions are processed using a generic Repository class:

  • findAll(): array: Fetches all records, instantiates Entity objects, and returns them as an array.
  • findBy(array $criteria): array: Filters queries based on criteria, returning mapped entities.
  • findOne(array $criteria): ?object: Resolves the first matching record as an entity instance, or null.
  • create(array|object $data): object: Inserts a record and returns the hydrated entity.
  • update(array|object $where, array|object $data): object: Modifies matching records and returns the updated state.
  • delete(array $conditions): bool: Deletes records matching the conditions, returning true on success.

DateTime Hydration

When mapping veritabanı records (mapToEntity), string date columns are automatically cast to native PHP DateTime objects if the entity class property is type-hinted as DateTime.


8. Middleware & Conditional Execution

Custom middleware must implement the App\Interface\Middleware interface, which defines the use method contract:

namespace Src;

use App\Core\Request;
use App\Core\Response;
use App\Interface\Middleware;

class AppMiddleware implements Middleware
{
    public function use(Request $req, Response $res, mixed $next): void
    {
        // Logic executing before request
        $next($req, $res); // Proceed to next middleware or controller
    }
}

Conditional Middleware Filters

The #[Middleware] attribute accepts add and remove rules in its arguments parameters to scope routing filters:

  • add: Restricts middleware activation to specific routes.
  • remove: Exempts specific routes from middleware evaluation.
// Active only on GET /admin
#[Middleware([AuthMiddleware::class], ['add' => [['GET', '/admin']]])]
class AdminController { ... }

// Evaluate LogMiddleware everywhere except POST /upload
#[Middleware([LogMiddleware::class], ['remove' => [['POST', '/upload']]])]
class ImageController { ... }

9. Error Handling & Exception Boundaries

Lynx provides a verbose error boundary system to streamline local development.

Visual Stack Highlighter

When an exception is thrown, App\Core\Exception intercepts the stack and determines the source line of code. It reads the source file and outputs a log block showing 5 lines of context before and after the error, underlining the exact line with ~ glyphs. This allows debugging immediately from console stdout.

Core Exceptions List

App\Exception\ exposes HTTP error boundary classes representing status codes:

  • BadRequestException (400)
  • UnauthorizedException (401)
  • PaymentRequiredException (402)
  • ForbiddenException (403)
  • NotFoundException (404)
  • MethodNotAllowedException (405)
  • NotAcceptableException (406)
  • RequestTimeoutException (408)
  • ConflictException (409)
  • GoneException (410)
  • LengthRequiredException (411)
  • PreconditionFailedException (412)
  • PayloadTooLargeException (413)
  • UnsupportedMediaTypeException (415)
  • TooManyRequestsException (429)
  • InternalServerErrorException (500)
  • NotImplementedException (501)
  • BadGatewayException (502)
  • ServiceUnavailableException (503)
  • GatewayTimeoutException (504)
  • HttpException (Custom status code exception)
  • SystemException (Framework execution runtime exception)

Global Exception Filter

You can hook a class implementing ExceptionFilter to intercept uncaught application errors and format custom JSON structures globally:

use App\Interface\ExceptionFilter;

class CustomExceptionFilter implements ExceptionFilter
{
    public function catch($e, Request $req, Response $res)
    {
        $res->setStatusCode($e->statusCode ?? 500)->json([
            'status' => 'error',
            'msg' => $e->message
        ]);
    }
}

// Register inside index.php or during application bootstrap:
$app->useGlobalExceptionFilter(new CustomExceptionFilter());

10. Log Management

Logs are handled by the static App\Helper\Console class.

Log Levels (LogLevel):

  • LogLevel::INFO (info)
  • LogLevel::WARNING (warning)
  • LogLevel::ERROR (error)
  • LogLevel::CRITICAL (critical)
  • LogLevel::TRACE (trace)
  • LogLevel::SUCCESS (success)
  • LogLevel::DEBUG (debug)
  • LogLevel::DEFAULT (default)

Logging Output & Files:

Executing Console::log('level', ...messages):

  1. serializes parameters into standard JSON formats.
  2. Writes a unique JSON log file named log_<microtime>_<random>.json into the /App/Cache/logs/ directory. This avoids lock collisions and facilitates log-parsing pipelines.

11. Helper Classes & Custom Encryption

App\Helper\Helper provides a set of static utility methods for environmental variables, security, and objects:

  • getEnv(string $key, $default = null): Parses .env key-value pairs.
  • tokenGenerator(int $length, ?string $chars = null): string: Generates random token strings, ensuring the first character is never '0'.
  • encrypt(string $data, ?array $options = null): ?string: Secures text using salted buffers, character shifts, and two-layer base64 operations. Runs self-tests during encryption to verify decryption accuracy.
  • decrypt(string $data, ?array $options = null): ?string: Reverses operations and returns the plain string.
  • isEqual(string $plain, string $hashedOrEncrypted, ?array $options = null): array: Assesses equivalence by comparing values plain, encrypted, or decrypted, returning ['isEqual' => true/false, 'method' => 'matched_approach'].
  • encryptObject(array $data, string $key) / decryptObject(array $data, string $key): Recursively encrypts or decrypts array indices and nested objects.

12. Native Helper Functions Folder (/php)

The /php directory in the project root is dedicated to housing global procedural PHP helper files (e.g., ip.php) that lie outside the object-oriented framework engine.

  • Any procedural scripts stored in this folder run in application scope and can be referenced in controllers, templates, or services.
  • Example (/php/ip.php):
    <?php
    function getClientIP() {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
        }
        return trim($ip);
    }
    Functions defined here are directly callable globally within your application classes.

13. Type Safety in Views

To enable autocomplete, validation, and type safety for variables inside views (Src/views/*.php), we recommend defining PHPDoc annotations at the top of templates.

Because Response::render() extracts keys into local scopes, annotations help modern IDEs map variable contracts.

Example (Src/views/welcome.php):

<?php
/**
 * @var string $public
 * @var string $server_url
 * @var string $name
 * @var string $csrf
 */
?>
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="<?= $public ?>css/style.css">
    <title>Welcome - <?= $name ?></title>
</head>
<body>
    <form action="<?= $server_url ?>logout" method="POST">
        <?= $csrf ?>
        <button type="submit">Sign Out</button>
    </form>
</body>
</html>

📄 License

Lynx Framework is distributed as open-source software under the MIT License.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors