script-php/easyapp

EasyAPP is a different type of Framework. EasyAPP aims to help you in the easy development of any type of website, without consuming resources in vain.

Installs: 1

Dependents: 0

Suggesters: 0

Security: 0

Stars: 8

Watchers: 1

Forks: 0

Open Issues: 0

Type:project

pkg:composer/script-php/easyapp

dev-main 2026-01-01 22:12 UTC

This package is auto-updated.

Last update: 2026-01-01 22:12:42 UTC


README

EasyAPP Framework

A Modern, Lightweight PHP Framework for Rapid Development

PHP Version License Version

FeaturesInstallationQuick StartDocumentationContributing

EasyAPP is a powerful yet elegant PHP framework designed to make web application development fast, secure, and enjoyable. Built with modern PHP practices, it features a clean MVC architecture, advanced routing, ORM support, and comprehensive development tools.

Features

Core Architecture

  • Modern MVC Pattern - Clean separation with Controllers, Models, Views, Services, and Libraries
  • Dependency Injection - Registry-based DI container with automatic service resolution
  • Event System - Powerful event-driven architecture with lifecycle hooks
  • Service Layer - Business logic separation with service components

Database & ORM

  • Active Record ORM - Eloquent-style ORM with relationships (hasOne, hasMany, belongsTo, belongsToMany)
  • Query Builder - Fluent PDO-based database abstraction layer
  • Migrations - Database version control and schema management
  • Prepared Statements - Built-in SQL injection protection

Routing & Requests

  • Advanced Router - RESTful routing with parameters, patterns, and named routes
  • Multiple Route Formats - Support for /, |, and - separators
  • Request Handling - Comprehensive request/response abstractions
  • Method Spoofing - PUT, PATCH, DELETE support via POST

Development Tools

  • CLI Tool - Powerful command-line interface for scaffolding and management
  • Debug Mode - Beautiful error pages with stack traces and context
  • Logging System - PSR-3 compatible multi-level logging
  • Testing Support - Built-in testing utilities and examples

Performance & Caching

  • Smart Caching - File-based caching with automatic invalidation
  • Class Caching - Automatic model/controller/service instance caching
  • Lazy Loading - On-demand component loading for optimal performance
  • Proxy Pattern - AOP-style method interception and monitoring

Security

  • CSRF Protection - Automatic token generation and validation
  • Input Sanitization - XSS prevention and data filtering
  • Secure Headers - Configurable security headers
  • Path Traversal Protection - File access security validation

Internationalization

  • Multi-language Support - Built-in i18n system with language files
  • Template System - Parameter substitution with language variables
  • Dynamic Language Switching - Runtime language detection and switching

Requirements

  • PHP: 7.4 or higher (8.0+ recommended)
  • Extensions: PDO, JSON, MBString (recommended)
  • Web Server: Apache with mod_rewrite or Nginx
  • Database: MySQL 5.7+, MariaDB 10.2+, or PostgreSQL
  • Composer: Optional (for dependency management)

Installation

Via Git Clone

# Clone the repository
git clone https://github.com/script-php/EasyAPP.git my-project
cd my-project

# Configure environment
cp .env.example .env
nano .env  # Edit with your settings

# Set permissions (Linux/Mac)
chmod -R 755 storage/
chmod 644 .env

# Start development server
php easy serve localhost 8000

Via Composer (Coming Soon)

composer create-project script-php/easyapp my-project
cd my-project
php easy serve

Web Server Configuration

Apache Configuration
<VirtualHost *:80>
    ServerName myapp.local
    DocumentRoot "/path/to/my-project"
    
    <Directory "/path/to/my-project">
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    
    ErrorLog "/path/to/logs/myapp-error.log"
    CustomLog "/path/to/logs/myapp-access.log" common
</VirtualHost>
Nginx Configuration
server {
    listen 80;
    server_name myapp.local;
    root /path/to/my-project;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Quick Start

1. Create Your First Controller

php easy make:controller Welcome

This generates app/controller/welcome.php:

<?php

class ControllerWelcome extends Controller {
    
    public function index() {
        $data = [];
        $data['title'] = 'Welcome to EasyAPP';
        $data['message'] = 'Your application is ready!';
        
        $this->response->setOutput(
            $this->load->view('welcome/index.html', $data)
        );
    }
}

2. Define Routes

Edit app/router.php:

<?php

// Basic routes
$router->get('/', 'home');
$router->get('/welcome', 'welcome');

// RESTful routes
$router->get('/users', 'users|index');
$router->get('/users/{id}', 'users|show');
$router->post('/users', 'users|create');
$router->put('/users/{id}', 'users|update');
$router->delete('/users/{id}', 'users|delete');

// Route patterns
$router->pattern('id', '[0-9]+');

// Fallback
$router->fallback('not_found');

3. Create a Model

Traditional Style:

php easy make:model User
<?php

class ModelUser extends Model {
    
    public function getAll() {
        $sql = "SELECT * FROM `" . DB_PREFIX . "users` ORDER BY created_at DESC";
        $query = $this->db->query($sql);
        return $query->rows;
    }
    
    public function getById($id) {
        $sql = "SELECT * FROM `" . DB_PREFIX . "users` WHERE id = :id";
        $query = $this->db->query($sql, [':id' => $id]);
        return $query->row;
    }
}

ORM Style:

<?php

class User extends Orm {
    
    protected $table = 'users';
    protected $fillable = ['name', 'email', 'status'];
    
    // Define relationships
    public function posts() {
        return $this->hasMany(Post::class, 'user_id');
    }
    
    public function profile() {
        return $this->hasOne(Profile::class, 'user_id');
    }
}

// Usage
$user = User::find(1);
$posts = $user->posts()->where('status', 'published')->get();

4. Use in Controller

<?php

class ControllerUsers extends Controller {
    
    public function index() {
        // Traditional style
        $userModel = $this->load->model('user');
        $users = $userModel->getAll();
        
        // OR ORM style
        $users = User::with('posts')->where('active', 1)->get();
        
        $data['users'] = $users;
        
        $this->response->setOutput(
            $this->load->view('users/index.html', $data)
        );
    }
    
    public function show() {
        $id = $this->request->get('id');
        
        // ORM with relationships
        $user = User::with(['posts', 'profile'])->find($id);
        
        if (!$user) {
            $this->response->redirect('/404');
            return;
        }
        
        $data['user'] = $user;
        
        $this->response->setOutput(
            $this->load->view('users/show.html', $data)
        );
    }
}

5. Create Views

Create app/view/users/index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Users</title>
</head>
<body>
    <h1>User List</h1>
    
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Email</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($users as $user): ?>
            <tr>
                <td><?php echo $user['id']; ?></td>
                <td><?php echo htmlspecialchars($user['name']); ?></td>
                <td><?php echo htmlspecialchars($user['email']); ?></td>
                <td>
                    <a href="/users/<?php echo $user['id']; ?>">View</a>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</body>
</html>

Core Concepts

Model Loading Patterns

EasyAPP supports flexible model loading with three patterns:

// Pattern 1: Explicit (Recommended)
$userModel = $this->load->model('user');
$users = $userModel->getAll();

// Pattern 2: Magic Access (Auto-registered with model_ prefix)
$this->load->model('user');
$users = $this->model_user->getAll();

// Pattern 3: Method Chaining
$users = $this->load->model('user')->getAll();

// Subdirectories
$this->load->model('common/helper');
$data = $this->model_common_helper->getData();

Dependency Injection

All components receive the registry through constructor injection:

class ControllerUsers extends Controller {
    
    private $userService;
    
    public function __construct($registry) {
        parent::__construct($registry);
        
        // Load service
        $this->load->service('UserService');
        $this->userService = $this->UserService;
    }
    
    public function register() {
        $result = $this->userService->register($this->request->post);
        $this->response->json(['success' => true, 'user_id' => $result]);
    }
}

Services Layer

Services encapsulate business logic:

php easy make:service OrderService
<?php

class ServiceOrderService extends Service {
    
    public function processOrder($orderData) {
        // Load dependencies
        $orderModel = $this->load->model('order');
        $inventoryModel = $this->load->model('inventory');
        $paymentModel = $this->load->model('payment');
        
        // Business logic
        if (!$inventoryModel->checkStock($orderData['items'])) {
            throw new Exception('Insufficient stock');
        }
        
        $orderId = $orderModel->create($orderData);
        $paymentModel->process($orderId, $orderData['payment']);
        $inventoryModel->reserve($orderData['items']);
        
        // Trigger event
        $this->events->trigger('order.created', ['order_id' => $orderId]);
        
        return $orderId;
    }
}

Directory Structure

EasyAPP/
│
├── app/                      # Application Layer
│   ├── controller/           # Controllers (HTTP request handlers)
│   ├── model/                # Models (data access layer)
│   ├── view/                 # Views (presentation templates)
│   ├── service/              # Services (business logic)
│   ├── library/              # Libraries (reusable components)
│   ├── language/             # Language files (i18n)
│   │   ├── en-gb/           # English translations
│   │   ├── fr-fr/           # French translations
│   │   └── ro-ro/           # Romanian translations
│   ├── config.php           # Application configuration
│   ├── router.php           # Route definitions
│   └── helper.php           # Application helper functions
│
├── system/                   # Framework Core
│   ├── Framework/            # Core framework classes
│   │   ├── Db.php           # Database abstraction
│   │   ├── Router.php       # Routing system
│   │   ├── Orm.php          # Active Record ORM
│   │   ├── Cache.php        # Caching system
│   │   ├── Events.php       # Event system
│   │   ├── Request.php      # HTTP request
│   │   ├── Response.php     # HTTP response
│   │   ├── Load.php         # Component loader
│   │   ├── Registry.php     # DI container
│   │   ├── Migration.php    # Migration system
│   │   └── Exceptions/      # Exception classes
│   ├── Autoloader.php       # PSR-4 autoloader
│   ├── Framework.php        # Framework bootstrap
│   ├── Controller.php       # Base controller
│   ├── Model.php            # Base model
│   ├── Service.php          # Base service
│   ├── Library.php          # Base library
│   └── Vendor/              # Composer dependencies
│
├── storage/                  # Storage (writable)
│   ├── cache/               # Cache files
│   ├── logs/                # Application logs
│   ├── sessions/            # Session data
│   └── uploads/             # File uploads
│
├── migrations/               # Database Migrations
│   └── 001_create_initial_user_system.php
│
├── assets/                   # Public Assets
│   └── app/
│       ├── images/          # Image files
│       ├── javascript/      # JavaScript files
│       └── stylesheet/      # CSS files
│
├── tests/                    # Test Suite
│   ├── OrmTest.php          # ORM tests
│   ├── OrmRelationshipsTest.php
│   └── SystemIntegrationTest.php
│
├── docs/                     # Documentation
│   ├── 01-getting-started.md
│   ├── 02-configuration.md
│   ├── 03-directory-structure.md
│   ├── 04-architecture.md
│   ├── 05-request-lifecycle.md
│   ├── 06-dependency-injection.md
│   ├── 07-controllers.md
│   ├── 08-models-traditional.md
│   ├── 09-models-orm.md
│   ├── 10-views.md
│   ├── 11-services.md
│   ├── 12-libraries.md
│   ├── 13-language.md
│   └── 14-model-loading.md
│
├── .env                      # Environment configuration
├── .env.example             # Environment template
├── .htaccess                # Apache rewrite rules
├── nginx.conf               # Nginx configuration example
├── index.php                # Application entry point
├── config.php               # Framework configuration
├── easy                     # CLI tool (Unix)
├── composer.json            # Composer dependencies
├── LICENSE                  # GPL v3 License
└── README.md                # This file

CLI Commands

EasyAPP includes a powerful CLI tool for rapid development:

Code Generation

# Controllers
php easy make:controller UserController
php easy make:controller Admin/UserController

# Models
php easy make:model User
php easy make:model Common/Helper

# Services
php easy make:service UserService
php easy make:service Payment/StripeService

# Libraries
php easy make:library ImageProcessor

Database Migrations

# Create migration
php easy make:migration create_users_table

# Run migrations
php easy migrate

# Rollback
php easy migrate:rollback

# Migration status
php easy migrate:status

Development Tools

# Start development server
php easy serve localhost 8000

# Clear cache
php easy cache:clear

# View routes
php easy routes

# Run tests
php easy test

# Show version
php easy version

# Help
php easy help

Configuration

Environment Variables

Create .env file in project root:

# Application Settings
APP_NAME="My EasyAPP"
APP_ENV=development
APP_URL=http://localhost
APP_TIMEZONE=UTC
DEBUG=true

# Database Configuration
DB_DRIVER=mysql
DB_HOSTNAME=localhost
DB_DATABASE=easyapp_db
DB_USERNAME=root
DB_PASSWORD=your_password
DB_PORT=3306
DB_PREFIX=ea_

# Cache Settings
CACHE_ENABLED=true
CACHE_DRIVER=file
CACHE_TTL=3600

# Session Configuration
SESSION_DRIVER=file
SESSION_LIFETIME=7200
SESSION_COOKIE_NAME=easyapp_session

# Security
CSRF_PROTECTION=true
CSRF_TOKEN_NAME=csrf_token

# Logging
LOG_ENABLED=true
LOG_LEVEL=debug
LOG_PATH=storage/logs/

# Email (Optional)
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="${APP_NAME}"

Application Configuration

Edit app/config.php:

<?php

// Application Settings
$config['platform'] = env('APP_NAME', 'EasyAPP');
$config['debug'] = env('DEBUG', false);
$config['timezone'] = env('APP_TIMEZONE', 'UTC');

// Startup Services (loaded on application bootstrap)
$config['services'] = [
    'auth',                    // Authentication service
    'email|initialize',        // Email service with initialize method
];

// Language Settings
$config['language_default'] = 'en-gb';
$config['language_auto_detect'] = true;

// Security
$config['csrf_protection'] = env('CSRF_PROTECTION', true);
$config['session_security'] = true;

// Performance
$config['cache_views'] = true;
$config['compress_output'] = true;

Database Usage

Traditional PDO Style

class ModelUser extends Model {
    
    public function getActiveUsers() {
        $sql = "SELECT * FROM `" . DB_PREFIX . "users` 
                WHERE active = :active 
                ORDER BY created_at DESC";
        
        $query = $this->db->query($sql, [':active' => 1]);
        
        return $query->rows;  // All rows
    }
    
    public function findById($id) {
        $sql = "SELECT * FROM `" . DB_PREFIX . "users` WHERE id = :id";
        $query = $this->db->query($sql, [':id' => $id]);
        
        return $query->row;  // Single row
    }
    
    public function createUser($data) {
        $sql = "INSERT INTO `" . DB_PREFIX . "users` 
                (name, email, password, created_at) 
                VALUES (:name, :email, :password, NOW())";
        
        $this->db->query($sql, [
            ':name' => $data['name'],
            ':email' => $data['email'],
            ':password' => password_hash($data['password'], PASSWORD_DEFAULT)
        ]);
        
        return $this->db->getLastId();
    }
}

ORM Style (Active Record)

class User extends Orm {
    
    protected $table = 'users';
    protected $primaryKey = 'id';
    protected $fillable = ['name', 'email', 'status'];
    protected $hidden = ['password'];
    protected $timestamps = true;
    
    // Relationships
    public function posts() {
        return $this->hasMany(Post::class, 'user_id');
    }
    
    public function profile() {
        return $this->hasOne(Profile::class, 'user_id');
    }
    
    public function roles() {
        return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');
    }
}

// Usage Examples
// Find by ID
$user = User::find(1);

// Find with conditions
$users = User::where('status', 'active')
             ->where('role', 'admin')
             ->orderBy('created_at', 'DESC')
             ->limit(10)
             ->get();

// Eager load relationships
$user = User::with(['posts', 'profile'])->find(1);
$posts = $user->posts;

// Create
$user = User::create([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'status' => 'active'
]);

// Update
$user = User::find(1);
$user->name = 'Jane Doe';
$user->save();

// Delete
$user = User::find(1);
$user->delete();

// Query builder methods
$count = User::where('active', 1)->count();
$exists = User::where('email', 'test@example.com')->exists();
$user = User::firstOrCreate(['email' => 'new@example.com'], ['name' => 'New User']);

Transactions

$this->db->beginTransaction();

try {
    // Create user
    $userId = $this->load->model('user')->create($userData);
    
    // Create profile
    $this->load->model('profile')->create([
        'user_id' => $userId,
        'bio' => $profileData['bio']
    ]);
    
    // Commit transaction
    $this->db->commit();
    
    return $userId;
    
} catch (Exception $e) {
    // Rollback on error
    $this->db->rollBack();
    
    $this->logger->error('Transaction failed: ' . $e->getMessage());
    throw $e;
}

Routing

Route Definition

Edit app/router.php:

<?php

// GET Routes
$router->get('/', 'home');
$router->get('/about', 'pages|about');
$router->get('/contact', 'pages|contact');

// POST Routes
$router->post('/contact/send', 'contact|send');
$router->post('/login', 'auth|login');

// RESTful Resource Routes
$router->get('/users', 'users|index');           // List
$router->get('/users/{id}', 'users|show');       // Show
$router->post('/users', 'users|create');         // Create
$router->put('/users/{id}', 'users|update');     // Update
$router->delete('/users/{id}', 'users|delete');  // Delete

// Route Parameters with Patterns
$router->pattern('id', '[0-9]+');
$router->pattern('slug', '[a-z0-9-]+');
$router->pattern('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');

$router->get('/posts/{slug}', 'posts|show');
$router->get('/api/users/{uuid}', 'api/users|show');

// Multiple Parameters
$router->get('/blog/{category}/{slug}', 'blog|show');

// Optional Parameters
$router->get('/search/{query?}', 'search|index');

// Grouped Routes
$router->prefix('/admin', function($router) {
    $router->get('/dashboard', 'admin/dashboard');
    $router->get('/users', 'admin/users|index');
    $router->get('/settings', 'admin/settings');
});

// API Routes
$router->prefix('/api/v1', function($router) {
    $router->get('/users', 'api/users|index');
    $router->post('/users', 'api/users|create');
    $router->get('/users/{id}', 'api/users|show');
});

// Fallback (404)
$router->fallback('not_found');

Accessing Route Parameters

class ControllerUsers extends Controller {
    
    public function show() {
        // Method 1: Via router
        $id = $this->router->getParam('id');
        
        // Method 2: Via request
        $id = $this->request->get('id');
        
        // Load user
        $user = User::find($id);
        
        if (!$user) {
            $this->response->redirect('/404');
            return;
        }
        
        $data['user'] = $user;
        $this->response->setOutput($this->load->view('users/show.html', $data));
    }
    
    public function blogPost() {
        $category = $this->router->getParam('category');
        $slug = $this->router->getParam('slug');
        
        $post = Post::where('category', $category)
                    ->where('slug', $slug)
                    ->first();
        
        // ... render view
    }
}

Route Formats

EasyAPP supports multiple route separator formats:

// Slash format (recommended)
$router->get('/users', 'users|index');

// Pipe format
$router->get('/users', 'users|index');

// Dash format
$router->get('/users', 'users-index');

Views and Templates

Loading Views

class ControllerProducts extends Controller {
    
    public function index() {
        // Prepare data
        $data = [];
        $data['title'] = 'Product List';
        $data['products'] = Product::where('active', 1)->get();
        $data['categories'] = Category::all();
        
        // Load view
        $this->response->setOutput(
            $this->load->view('products/index.html', $data)
        );
    }
}

View Templates

Create app/view/products/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?php echo htmlspecialchars($title); ?></title>
    <link rel="stylesheet" href="/assets/app/stylesheet/style.css">
</head>
<body>
    <div class="container">
        <h1><?php echo $title; ?></h1>
        
        <!-- Category Filter -->
        <div class="categories">
            <?php foreach ($categories as $category): ?>
                <a href="/products?category=<?php echo $category->id; ?>" class="btn">
                    <?php echo htmlspecialchars($category->name); ?>
                </a>
            <?php endforeach; ?>
        </div>
        
        <!-- Product Grid -->
        <div class="products-grid">
            <?php if (!empty($products)): ?>
                <?php foreach ($products as $product): ?>
                    <div class="product-card">
                        <img src="<?php echo $product->image; ?>" alt="<?php echo htmlspecialchars($product->name); ?>">
                        <h3><?php echo htmlspecialchars($product->name); ?></h3>
                        <p class="price">$<?php echo number_format($product->price, 2); ?></p>
                        <a href="/products/<?php echo $product->id; ?>" class="btn btn-primary">View Details</a>
                    </div>
                <?php endforeach; ?>
            <?php else: ?>
                <p class="no-results">No products found.</p>
            <?php endif; ?>
        </div>
    </div>
    
    <script src="/assets/app/javascript/app.js"></script>
</body>
</html>

Partial Views

// Load partial view
$header = $this->load->view('common/header.html', $data);
$footer = $this->load->view('common/footer.html', $data);

// Combine views
$data['header'] = $header;
$data['footer'] = $footer;

$this->response->setOutput($this->load->view('page.html', $data));

JSON Responses

class ControllerApi extends Controller {
    
    public function users() {
        $users = User::all();
        
        $this->response->json([
            'success' => true,
            'data' => $users,
            'count' => count($users)
        ]);
    }
    
    public function create() {
        try {
            $user = User::create($this->request->post);
            
            $this->response->json([
                'success' => true,
                'message' => 'User created successfully',
                'user_id' => $user->id
            ]);
            
        } catch (Exception $e) {
            $this->response->json([
                'success' => false,
                'error' => $e->getMessage()
            ], 400);
        }
    }
}

Security

CSRF Protection

// Automatically enabled in forms
<form method="POST" action="/users/create">
    <?php echo $this->csrf->token(); ?>
    
    <input type="text" name="username">
    <button type="submit">Submit</button>
</form>

// Manual validation
if (!$this->csrf->validate()) {
    throw new Exception('CSRF token validation failed');
}

Input Validation

class ControllerUsers extends Controller {
    
    public function create() {
        $rules = [
            'name' => 'required|min:3|max:50',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8|confirmed',
            'age' => 'integer|min:18'
        ];
        
        $validator = $this->validator->make($this->request->post, $rules);
        
        if ($validator->fails()) {
            $this->response->json([
                'success' => false,
                'errors' => $validator->errors()
            ], 422);
            return;
        }
        
        // Process valid data
        $user = User::create($validator->validated());
        
        $this->response->json(['success' => true, 'user_id' => $user->id]);
    }
}

SQL Injection Prevention

// ✓ SAFE: Using prepared statements
$sql = "SELECT * FROM users WHERE email = :email";
$query = $this->db->query($sql, [':email' => $email]);

// ✓ SAFE: Using ORM
$user = User::where('email', $email)->first();

// ✗ UNSAFE: Direct string concatenation (don't do this)
$sql = "SELECT * FROM users WHERE email = '" . $email . "'";

XSS Prevention

// Always escape output in views
<?php echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8'); ?>

// Short helper
<?php echo e($user_input); ?>

Secure Headers

// Configured in app/config.php
$config['security_headers'] = [
    'X-Frame-Options' => 'SAMEORIGIN',
    'X-Content-Type-Options' => 'nosniff',
    'X-XSS-Protection' => '1; mode=block',
    'Referrer-Policy' => 'strict-origin-when-cross-origin'
];

Caching

Basic Cache Usage

// Check and get from cache
$users = $this->cache->get('users_list');

if (!$users) {
    // Cache miss - fetch from database
    $users = User::where('active', 1)->get();
    
    // Store in cache for 1 hour (3600 seconds)
    $this->cache->set('users_list', $users, 3600);
}

return $users;

Cache Remember Pattern

// Fetch or cache in one call
$products = $this->cache->remember('featured_products', function() {
    return Product::where('featured', 1)
                  ->orderBy('views', 'DESC')
                  ->limit(10)
                  ->get();
}, 3600);

Cache Management

// Delete specific cache key
$this->cache->delete('users_list');

// Clear all cache
$this->cache->clear();

// Check if key exists
if ($this->cache->has('settings')) {
    $settings = $this->cache->get('settings');
}

// Cache forever (no expiration)
$this->cache->forever('app_settings', $settings);

Cache Tags (Grouping)

// Tag-based caching
$this->cache->tags(['users', 'active'])->set('user_list', $users, 3600);

// Flush by tag
$this->cache->tags(['users'])->flush();

Logging

Log Levels

// Emergency: System is unusable
$this->logger->emergency('Database server is down');

// Alert: Action must be taken immediately
$this->logger->alert('Disk space critically low');

// Critical: Critical conditions
$this->logger->critical('Application component unavailable');

// Error: Runtime errors
$this->logger->error('Failed to send email', [
    'to' => $email,
    'error' => $e->getMessage()
]);

// Warning: Exceptional occurrences that are not errors
$this->logger->warning('Deprecated API called', ['method' => 'oldMethod']);

// Notice: Normal but significant events
$this->logger->notice('User password changed', ['user_id' => $userId]);

// Info: Interesting events
$this->logger->info('User logged in', [
    'user_id' => $userId,
    'ip' => $this->request->ip()
]);

// Debug: Detailed debug information
$this->logger->debug('API Response', ['data' => $response]);

Exception Logging

try {
    $result = $this->someRiskyOperation();
    
} catch (Exception $e) {
    // Log exception with full context
    $this->logger->exception($e, [
        'user_id' => $this->session->get('user_id'),
        'request' => $this->request->all()
    ]);
    
    // Re-throw or handle
    throw $e;
}

Custom Log Channels

// Log to specific file
$this->logger->channel('payments')->info('Payment processed', [
    'order_id' => $orderId,
    'amount' => $amount
]);

$this->logger->channel('security')->warning('Failed login attempt', [
    'username' => $username,
    'ip' => $ip
]);

Events System

Registering Event Listeners

// In app/config.php or bootstrap
$this->events->on('user.created', function($data) {
    // Send welcome email
    $this->load->service('EmailService')->sendWelcome($data['user']);
});

$this->events->on('order.completed', function($data) {
    // Update inventory
    $this->load->model('inventory')->updateStock($data['items']);
    
    // Send confirmation email
    $this->load->service('EmailService')->orderConfirmation($data['order']);
});

$this->events->on('product.viewed', function($data) {
    // Track analytics
    $this->load->model('analytics')->track('product_view', $data['product_id']);
});

Triggering Events

class ControllerUsers extends Controller {
    
    public function register() {
        $userData = $this->request->post;
        
        // Create user
        $user = User::create($userData);
        
        // Trigger event
        $this->events->trigger('user.created', [
            'user' => $user,
            'ip' => $this->request->ip(),
            'timestamp' => time()
        ]);
        
        $this->response->json(['success' => true, 'user_id' => $user->id]);
    }
}

Framework Events

// Before/After controller execution
$this->events->on('before:controller.execute', function($data) {
    // Log request
    $this->logger->info('Controller executing', [
        'route' => $data['route'],
        'method' => $data['method']
    ]);
});

// Model loaded
$this->events->on('model.loaded', function($data) {
    // Track model usage
});

// View rendered
$this->events->on('after:view', function($data) {
    // Minify HTML output
    $data['output'] = $this->minify($data['output']);
});

Priority Events

// Higher priority (executed first)
$this->events->on('user.login', function($data) {
    // Check if user is banned
}, 100);

// Lower priority (executed later)
$this->events->on('user.login', function($data) {
    // Log login
}, 10);

Testing

Running Tests

# Run all tests
php easy test

# Run specific test file
php easy test tests/OrmTest.php

# Run with verbose output
php easy test --verbose

Writing Tests

Create tests/UserTest.php:

<?php

class UserTest extends TestCase {
    
    public function setUp() {
        parent::setUp();
        
        // Set up test database
        $this->db->query("CREATE TABLE IF NOT EXISTS users (
            id INT PRIMARY KEY AUTO_INCREMENT,
            name VARCHAR(255),
            email VARCHAR(255),
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )");
    }
    
    public function testUserCreation() {
        // Create user
        $user = User::create([
            'name' => 'Test User',
            'email' => 'test@example.com'
        ]);
        
        // Assertions
        $this->assertNotNull($user->id);
        $this->assertEquals('Test User', $user->name);
        $this->assertEquals('test@example.com', $user->email);
    }
    
    public function testUserRelationships() {
        // Create user with posts
        $user = User::create(['name' => 'John', 'email' => 'john@example.com']);
        
        $post = Post::create([
            'user_id' => $user->id,
            'title' => 'Test Post',
            'content' => 'Content here'
        ]);
        
        // Test relationship
        $userPosts = $user->posts()->get();
        
        $this->assertCount(1, $userPosts);
        $this->assertEquals('Test Post', $userPosts[0]->title);
    }
    
    public function tearDown() {
        // Clean up
        $this->db->query("DROP TABLE IF EXISTS users");
        $this->db->query("DROP TABLE IF EXISTS posts");
        
        parent::tearDown();
    }
}

Test Examples

See the tests/ directory for comprehensive examples:

  • tests/OrmTest.php - ORM functionality tests
  • tests/OrmRelationshipsTest.php - Relationship tests
  • tests/OrmAdvancedTest.php - Advanced ORM features
  • tests/SystemIntegrationTest.php - Full system tests

Documentation

Comprehensive documentation is available in the docs/ directory:

Getting Started

Core Concepts

Components

Additional Guides

Contributing

We welcome contributions from the community! Here's how you can help:

Ways to Contribute

  • Report Bugs - Submit detailed bug reports with reproduction steps
  • Suggest Features - Propose new features or improvements
  • Improve Documentation - Fix typos, clarify explanations, add examples
  • Submit Pull Requests - Fix bugs or implement new features
  • Star the Repository - Show your support!

Development Setup

# Fork and clone the repository
git clone https://github.com/YOUR-USERNAME/EasyAPP.git
cd EasyAPP

# Create a feature branch
git checkout -b feature/your-feature-name

# Make your changes and test
php easy test

# Commit with clear message
git commit -m "Add: Brief description of your changes"

# Push to your fork
git push origin feature/your-feature-name

# Create a Pull Request

Guidelines

  • Follow PSR-12 coding standards
  • Write clear, descriptive commit messages
  • Add tests for new features
  • Update documentation as needed
  • Ensure all tests pass before submitting PR

See CONTRIBUTING.md for detailed guidelines.

Changelog

See CHANGELOG.md for version history and release notes.

Support & Community

Getting Help

  • Documentation - Check the docs/ directory
  • GitHub Issues - Report bugs or ask questions
  • Official Website - Visit script-php.ro
  • Email - Contact the maintainers

Useful Resources

Project Status

  • Stable - Version 2.0 (dev-orm branch)
  • Actively Maintained - Regular updates and bug fixes
  • Growing - New features in development

Acknowledgments

Special thanks to all contributors who have helped make EasyAPP better:

  • Community members for bug reports and feature suggestions
  • Contributors for code improvements and documentation
  • Everyone who has starred and shared the project

License

EasyAPP Framework is open-source software licensed under the GPL v3 License.

Copyright (c) 2022-2025, script-php.ro

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

See LICENSE file for full license text.

Author

YoYo

Built with care for the PHP community

Back to Top