PHPackages                             eclo/app - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. eclo/app

ActiveLibrary[Framework](/categories/framework)

eclo/app
========

A simple framework library

2.0.4(7mo ago)071[1 PRs](https://github.com/Jatbi/eclo/pulls)MITPHPPHP &gt;=8.1

Since Sep 6Pushed 7mo ago1 watchersCompare

[ Source](https://github.com/Jatbi/eclo)[ Packagist](https://packagist.org/packages/eclo/app)[ Docs](https://eclo.vn)[ RSS](/packages/eclo-app/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (6)Versions (18)Used By (0)

ECLO Framework (eclo/app)
=========================

[](#eclo-framework-ecloapp)

**ECLO App** là một micro-framework library được viết bằng PHP, cung cấp một bộ công cụ mạnh mẽ để xây dựng các ứng dụng web một cách nhanh chóng. Thư viện quản lý các tác vụ cốt lõi như định tuyến (routing), middleware, tương tác cơ sở dữ liệu, bảo mật, và nhiều tiện ích khác trong một lớp `App` duy nhất.

Yêu cầu
-------

[](#yêu-cầu)

- PHP &gt;= 8.1

Cài đặt
-------

[](#cài-đặt)

Sử dụng [Composer](https://getcomposer.org/) để cài đặt:

```
composer require eclo/app
```

Khởi tạo
--------

[](#khởi-tạo)

Đây là một ví dụ "Hello World" đơn giản để bắt đầu.

```
// index.php
require 'vendor/autoload.php';

// Khởi tạo ứng dụng
$app = new ECLO\App([
    'database_type' => 'mysql',
    'database_name' => 'ten_database',
    'server' => 'localhost',
    'username' => 'root',
    'password' => ''
]);

// Định nghĩa một route cho trang chủ
$app->router('/', 'GET', function() {
    echo 'Hello, World!';
});

// Chạy ứng dụng
$app->run();
```

---

Định tuyến (Routing)
--------------------

[](#định-tuyến-routing)

Hệ thống routing cho phép bạn ánh xạ các URL tới các hàm hoặc phương thức xử lý.

### Định tuyến cơ bản

[](#định-tuyến-cơ-bản)

Sử dụng phương thức `router()` để định nghĩa một route.

```
// Route cho phương thức GET
$app->router('/about', 'GET', function() {
    echo 'Đây là trang giới thiệu.';
});

// Route cho phương thức POST
$app->router('/contact', 'POST', function() {
    // Xử lý dữ liệu form
});

// Route cho nhiều phương thức
$app->router('/news', ['GET', 'POST'], function() {
    // ...
});
```

### Route với tham số

[](#route-với-tham-số)

Bạn có thể định nghĩa các tham số trong URL bằng cách đặt chúng trong dấu ngoặc nhọn `{}`.

```
$app->router('/users/{id}', 'GET', function($params) {
    $userId = $params['id'];
    echo "Thông tin người dùng có ID: " . $app->xss($userId);
});
```

### Nhóm Route (Group)

[](#nhóm-route-group)

Nhóm các route có cùng một tiền tố chung.

```
$app->group('/admin', function($app) {

    $app->router('', 'GET', function() {
        echo 'Trang chủ Admin'; // URL: /admin
    });

    $app->router('/products', 'GET', function() {
        echo 'Quản lý sản phẩm'; // URL: /admin/products
    });

    // Group lồng nhau
    $app->group('/settings', function($app) {
        $app->router('/general', 'GET', function() {
            echo 'Cài đặt chung'; // URL: /admin/settings/general
        });
    });
});
```

### Route xử lý lỗi

[](#route-xử-lý-lỗi)

Bạn có thể định nghĩa một route đặc biệt để xử lý các lỗi 404 hoặc lỗi truy cập trong một group.

```
// Định nghĩa trang lỗi 404 chung cho toàn bộ trang web
$app->router('::404', 'GET', function($params) use ($app) {
    echo $app->render('views/errors/404.php', ['path' => $params['path']]);
});

// Định nghĩa trang lỗi 500 chung
$app->router('::500', 'GET', function() use ($app) {
    echo $app->render('views/errors/500.php');
});

// Tạo một group cho khu vực admin
$app->group('/admin', function($app) {

    // Các route admin bình thường
    $app->router('/dashboard', 'GET', function() { echo 'Admin Dashboard'; });

    // Ghi đè lại trang lỗi 404 chỉ riêng cho khu vực /admin
    $app->router('::404', 'GET', function() use ($app) {
        echo $app->render('views/admin/error_404.php');
    });

    // Định nghĩa trang lỗi 403 (cấm truy cập) cho khu vực admin
    $app->router('::403', 'GET', function() use ($app) {
        echo 'ACCESS DENIED FOR ADMIN AREA';
    });
});
```

Kích hoạt lỗi một cách chủ động

```
$app->router('/critical-operation', 'GET', function() use ($app) {
    try {
        // Một thao tác có thể gây lỗi nghiêm trọng
        $result = $app->get('some_table', '*');
        if (!$result) {
            // Giả sử đây là một lỗi không mong muốn
            throw new Exception("Cannot get data.");
        }
        echo "Operation successful!";
    } catch (Exception $e) {
        // Ghi log lỗi
        // error_log($e->getMessage());

        // Hiển thị trang lỗi 500 cho người dùng
        $app->triggerError(500);
    }
});
$app->router('/admin/secret-data', 'GET', function() use ($app) {
    $userRole = $app->getSession('role'); // Giả sử 'guest'

    if ($userRole !== 'admin') {
        // Kích hoạt trang lỗi 403 (Forbidden)
        $app->triggerError(403);
    }

    echo "Here is the secret data.";
})->middleware('auth');
```

---

Middleware
----------

[](#middleware)

Middleware cho phép bạn thực thi một logic nào đó (ví dụ: kiểm tra xác thực) trước khi request được xử lý bởi route.

### Đăng ký Middleware

[](#đăng-ký-middleware)

Sử dụng `setMiddleware()` để đăng ký. Hàm callback của middleware phải trả về `true` để tiếp tục, hoặc `false` để dừng lại.

```
// Đăng ký middleware kiểm tra đăng nhập
$app->setMiddleware('auth', function() use ($app) {
    if (!$app->getSession('user_id')) {
        $app->redirect('/login');
        return false; // Dừng xử lý
    }
    return true; // Cho phép tiếp tục
});
```

### Áp dụng Middleware

[](#áp-dụng-middleware)

Sử dụng phương thức `middleware()` ngay sau khi định nghĩa route hoặc group.

```
// Áp dụng cho một route
$app->router('/dashboard', 'GET', function() { ... })->middleware('auth');

// Áp dụng cho cả một group
$app->group('/admin', function($app) {
    // Tất cả các route trong này sẽ được bảo vệ bởi middleware 'auth'
    $app->router('/posts', 'GET', function() { ... });
    $app->router('/users', 'GET', function() { ... });
})->middleware('auth');
```

---

Tương tác Cơ sở dữ liệu (Medoo)
-------------------------------

[](#tương-tác-cơ-sở-dữ-liệu-medoo)

ECLO App tích hợp sẵn [Medoo](https://medoo.in/) để làm việc với CSDL. Bạn có thể gọi các phương thức của Medoo trực tiếp từ đối tượng `$app`.

```
// Lấy tất cả người dùng
$users = $app->select('users', '*');

// Lấy một người dùng theo điều kiện
$user = $app->get('users', '*', ['id' => 1]);

// Chèn dữ liệu
$app->insert('users', [
    'username' => 'eclo',
    'email' => 'info@eclo.vn'
]);

// Cập nhật
$app->update('users', ['email' => 'new.email@eclo.vn'], ['id' => 1]);

// Xóa
$app->delete('users', ['id' => 1]);

// Sử dụng cú pháp tĩnh
$count = ECLO\App::table('users')->count();
```

Để biết thêm các phương thức truy vấn, vui lòng tham khảo [tài liệu của Medoo](https://medoo.in/api/where).

---

Hệ thống Hooks (Actions) 🔌
--------------------------

[](#hệ-thống-hooks-actions-)

### Khái niệm Hooks

[](#khái-niệm-hooks)

**Hooks** (hay Actions) là các điểm đánh dấu cụ thể trong vòng đời của ứng dụng. Tại các điểm này, bạn có thể đăng ký để thực thi các hàm của riêng mình. Hooks không dùng để thay đổi dữ liệu, mà dùng để **thực hiện một hành động** tại một thời điểm nhất định (ví dụ: ghi log, gửi email, cập nhật CSDL, gọi API ngoài).

### API: `addHook()`

[](#api-addhook)

Dùng để đăng ký một hàm (callback) vào một hook cụ thể.

**Chữ ký (Signature):**

```
$app->addHook(string $tag, callable $callback, int $priority = 10);
```

**Tham số:**

- `$tag` (string): Tên của hook mà bạn muốn "móc" vào.
- `$callback` (callable): Hàm sẽ được thực thi khi hook được kích hoạt.
- `$priority` (int): Thứ tự ưu tiên thực thi. **Số nhỏ hơn sẽ được chạy trước**. Mặc định là `10`.

**Ví dụ:** Ghi log mỗi khi có lỗi 404 xảy ra.

```
// Đăng ký hành động vào hook 'before_error_trigger'
$app->addHook('before_error_trigger', function($statusCode, $params) {
    if ($statusCode === 404) {
        $logMessage = date('Y-m-d H:i:s') . " - 404 Not Found at path: " . $params['path'] . "\n";
        file_put_contents('404_errors.log', $logMessage, FILE_APPEND);
    }
});
```

### API: `doHook()`

[](#api-dohook)

Dùng để **kích hoạt** một hook, thực thi tất cả các hàm đã được đăng ký với nó. Đây là công cụ để bạn có thể tạo ra các "điểm mở rộng" (extension points) trong chính logic ứng dụng của mình.

**Chữ ký (Signature):**

```
$app->doHook(string $tag, ...$args);
```

**Tham số:**

- `$tag` (string): Tên duy nhất của hook mà bạn muốn kích hoạt.
- `...$args` (mixed): Một hoặc nhiều tham số mà bạn muốn truyền vào các hàm callback.

**Ví dụ:** Tạo hook `after_user_registration` để xử lý các tác vụ sau khi người dùng đăng ký thành công.

**1. Trong Controller, tạo điểm hook:**

```
// Controller xử lý đăng ký
function handle_registration() {
    global $app;

    // ... Logic lưu người dùng vào database ...
    $newUser = ['id' => 123, 'email' => 'newuser@example.com']; // Dữ liệu người dùng mới

    if ($newUser) {
        // Kích hoạt hook và truyền dữ liệu người dùng mới vào
        $app->doHook('after_user_registration', $newUser);
        echo "Đăng ký thành công!";
    }
}
```

**2. Ở nơi khác, đăng ký các hành động:**

```
// file functions.php

// Hành động 1: Gửi email chào mừng
$app->addHook('after_user_registration', function($user) {
    // Logic gửi mail đến $user['email']
    echo "Đã gửi mail chào mừng tới " . $user['email'];
}, 10);

// Hành động 2: Thêm người dùng vào danh sách Mailchimp
$app->addHook('after_user_registration', function($user) {
    // Logic gọi API của Mailchimp
    echo "Đã thêm " . $user['email'] . " vào Mailchimp.";
}, 20);
```

### Danh sách Hooks có sẵn

[](#danh-sách-hooks-có-sẵn)

- `before_run`: Chạy ngay khi `$app->run()` được gọi.
- `after_run`: Chạy trước khi script kết thúc sau khi đã xử lý xong request.
- `before_checks`: Chạy trước khi middleware và quyền được kiểm tra.
- `after_checks`: Chạy sau khi middleware và quyền đã được kiểm tra xong.
- `before_route_callback`: Chạy ngay trước khi hàm của route được thực thi.
- `after_route_callback`: Chạy ngay sau khi hàm của route đã thực thi xong.
- `before_error_trigger`: Chạy khi một lỗi được kích hoạt, trước khi trang lỗi được hiển thị.
- `before_render`: Chạy trước khi một file template được render.
- `after_render`: Chạy sau khi HTML của template đã được tạo nhưng trước khi trả về.
- `before_component_render`: Chạy trước khi một component được render.
- `after_component_render`: Chạy sau khi HTML của component đã được tạo.

---

Hệ thống Filters 💧
------------------

[](#hệ-thống-filters-)

### Khái niệm Filters

[](#khái-niệm-filters)

**Filters** cung cấp một cách để **sửa đổi** dữ liệu trong quá trình ứng dụng xử lý. Một giá trị sẽ được truyền qua một chuỗi các hàm callback, mỗi hàm có thể thay đổi giá trị đó và **bắt buộc phải trả về** phiên bản đã sửa đổi để hàm tiếp theo trong chuỗi sử dụng.

### API: `addFilter()`

[](#api-addfilter)

Dùng để đăng ký một hàm lọc dữ liệu.

**Chữ ký (Signature):**

```
$app->addFilter(string $tag, callable $callback, int $priority = 10);
```

**Tham số:**

- `$tag`, `$priority`: Tương tự `addHook`.
- `$callback` (callable): Hàm xử lý dữ liệu. **QUAN TRỌNG**: Hàm này phải nhận giá trị cần lọc làm tham số đầu tiên và **bắt buộc phải `return` một giá trị**.

**Ví dụ:** Thêm tên website vào sau tiêu đề trang.

```
// Đăng ký filter cho 'page_title'
$app->addFilter('page_title', function($title) {
    // Nối thêm tên website và trả về
    return $title . ' | My Awesome Site';
});
```

### API: `applyFilters()`

[](#api-applyfilters)

Dùng để áp dụng tất cả các filter đã đăng ký cho một giá trị cụ thể.

**Chữ ký (Signature):**

```
$app->applyFilters(string $tag, $value, ...$args);
```

**Tham số:**

- `$tag` (string): Tên của filter cần áp dụng.
- `$value` (mixed): Giá trị ban đầu cần được lọc/sửa đổi.
- `...$args` (mixed): Các tham số bổ sung không bị sửa đổi, dùng để cung cấp thêm ngữ cảnh cho các hàm callback.

**Ví dụ:** Tạo một filter cho phép thay đổi giá sản phẩm trước khi hiển thị.

**1. Trong hàm hiển thị giá, tạo điểm filter:**

```
function display_product_price($product) {
    global $app;
    $basePrice = $product['price'];

    // Áp dụng filter 'product_price', truyền giá gốc và cả đối tượng sản phẩm làm ngữ cảnh
    $finalPrice = $app->applyFilters('product_price', $basePrice, $product);

    return 'Giá: ' . number_format($finalPrice) . ' VND';
}
```

**2. Ở nơi khác, đăng ký các hàm lọc:**

```
// file functions.php

// Hàm 1: Giảm giá 10% cho tất cả sản phẩm
$app->addFilter('product_price', function($price) {
    return $price * 0.9; // Giảm 10%
}, 10);

// Hàm 2: Giảm thêm 50,000 VND cho sản phẩm có danh mục 'sale'
$app->addFilter('product_price', function($price, $product) {
    // Tham số $product là ngữ cảnh được truyền vào từ applyFilters
    if ($product['category'] === 'sale') {
        return $price - 50000;
    }
    return $price;
}, 20); // Chạy sau khi đã giảm 10%
```

### Danh sách Filters có sẵn

[](#danh-sách-filters-có-sẵn)

- `before_render_vars`: Áp dụng cho mảng `$vars` trước khi truyền vào template.
- `after_render_output`: Áp dụng cho chuỗi HTML sau khi template đã được render xong.
- `component_vars`: Tương tự `before_render_vars` nhưng dành riêng cho component.

---

Bảo mật
-------

[](#bảo-mật)

### Lọc XSS

[](#lọc-xss)

Sử dụng phương thức `xss()` để làm sạch dữ liệu đầu vào từ người dùng.

```
/**
 * @param string      $string        Chuỗi cần làm sạch.
 * @param bool        $allowHtml     Mặc định là false (loại bỏ toàn bộ HTML). Đặt là `true` để cho phép HTML an toàn.
 * @param array|null  $customConfig  Mảng cấu hình HTMLPurifier tùy chỉnh.
 */
public function xss($string, $allowHtml = false, $customConfig = null)
```

**Ví dụ:**

```
// 1. Mặc định: Loại bỏ tất cả HTML (an toàn nhất)
$username = "Admin";
$safeUsername = $app->xss($username); // Kết quả: "&lt;h1&gt;Admin&lt;/h1&gt;"

// 2. Cho phép HTML: Sử dụng cấu hình mặc định trong __construct
$comment = "Nội dung an toàn";
$safeComment = $app->xss($comment, true); // Kết quả: "Nội dung an toàn"

// 3. Cho phép HTML với cấu hình tùy chỉnh
$articleContent = 'Bài viết';
$config = ['HTML.Allowed' => 'h2,p'];
$safeArticle = $app->xss($articleContent, true, $config); // Kết quả: "Bài viết"
```

### JSON Web Tokens (JWT)

[](#json-web-tokens-jwt)

Dễ dàng tạo và xác thực JWT.

```
// Cấu hình key bí mật một lần
$app->JWT('your-secret-key');

// Tạo token
$payload = [
    'iss' => 'https://eclo.vn',
    'aud' => 'https://eclo.vn',
    'iat' => time(),
    'exp' => time() + 3600, // Hết hạn sau 1 giờ
    'user_id' => 123
];
$token = $app->addJWT($payload);

// Giải mã và xác thực token
$decoded = $app->decodeJWT($token);
if ($decoded) {
    echo "Xin chào user " . $decoded->user_id;
} else {
    echo "Token không hợp lệ hoặc đã hết hạn.";
}
```

### Giới hạn truy cập (Rate Limiting)

[](#giới-hạn-truy-cập-rate-limiting)

Chống brute-force bằng cách giới hạn số lần request từ một IP.

```
// Giới hạn việc đăng nhập: 5 lần mỗi 60 giây
$app->rateLimit('login_attempt', 5, 60);

// Nếu vượt quá, ứng dụng sẽ tự động dừng và trả về lỗi 429
```

---

Views &amp; Templates
---------------------

[](#views--templates)

### Render một View

[](#render-một-view)

Sử dụng `render()` để hiển thị một file view và truyền dữ liệu vào nó.

```
// Trong route của bạn
$app->router('/profile', 'GET', function() use ($app) {
    $data = [
        'title' => 'Trang cá nhân',
        'user' => ['name' => 'ECLO']
    ];
    // Sẽ render file views/profile.php
    echo $app->render('views/profile.php', $data);
});

// Trong views/profile.php
//
// Xin chào,
```

### Sử dụng Layout chung

[](#sử-dụng-layout-chung)

Bạn có thể định nghĩa một file layout chung để bao bọc các view.

```
// Thiết lập file layout
$app->setGlobalFile('views/layouts/main.php');

// Trong views/layouts/main.php
//
//
//
//   ...
//
//   ...
//
//
```

### Components

[](#components)

Tạo các thành phần view có thể tái sử dụng.

```
// Đăng ký component
$app->setComponent('userCard', function($vars) {
    $user = $vars['user'];
    echo "{$user['name']}{$user['email']}";
});

// Render component trong view
// echo $app->component('userCard', ['user' => ['name' => 'ECLO', 'email' => 'info@eclo.vn']]);
```

---

Tiện ích khác
-------------

[](#tiện-ích-khác)

### Gửi Email (PHPMailer)

[](#gửi-email-phpmailer)

```
$mailConfig = [
    'host' => 'smtp.example.com',
    'username' => 'user@example.com',
    'password' => 'password',
    'encryption' => 'tls', // tls hoặc ssl
    'port' => 587,
    'from_email' => 'noreply@example.com',
    'from_name' => 'ECLO App'
];

try {
    $mailer = $app->Mail($mailConfig);
    $mailer->addAddress('recipient@example.net', 'Joe User');
    $mailer->isHTML(true);
    $mailer->Subject = 'Here is the subject';
    $mailer->Body    = 'This is the HTML message body in bold!';
    $mailer->send();
    echo 'Message has been sent';
} catch (Exception $e) {
    echo "Message could not be sent. Mailer Error: {$mailer->ErrorInfo}";
}
```

### Tải lên file (Upload)

[](#tải-lên-file-upload)

```
if (!empty($_FILES['my_file'])) {
    $handle = $app->upload($_FILES['my_file']);
    if ($handle->uploaded) {
        $handle->process('/path/to/save/');
        if ($handle->processed) {
            echo 'File uploaded: ' . $handle->file_dst_name;
            $handle->clean();
        } else {
            echo 'Error: ' . $handle->error;
        }
    }
}
```

### Nén file (Minify)

[](#nén-file-minify)

```
// Nén CSS
$app->minifyCSS('path/to/style.css', 'path/to/style.min.css');

// Nén JS
$app->minifyJS(['path/to/script1.js', 'path/to/script2.js'], 'path/to/all.min.js');
```

### Session và Cookie

[](#session-và-cookie)

```
// Session
$app->setSession('user_id', 123);
$userId = $app->getSession('user_id');
$app->deleteSession('user_id');

// Cookie
$app->setCookie('remember_me', 'some_token', time() + (86400 * 30)); // 30 ngày
$token = $app->getCookie('remember_me');
$app->deleteCookie('remember_me');
```

### Điều hướng (Redirect)

[](#điều-hướng-redirect)

```
// Chuyển hướng đến URL cụ thể
$app->redirect('/login');

// Quay lại trang trước đó
$app->back();
```

---

Bản quyền
---------

[](#bản-quyền)

Thư viện này được phát hành dưới giấy phép **MIT**. Xem file `LICENSE` để biết thêm chi tiết.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance62

Regular maintenance activity

Popularity9

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~24 days

Total

17

Last Release

232d ago

Major Versions

1.2.2 → 2.0.12025-07-12

2.0.3 → v3.x-dev2025-07-29

### Community

Maintainers

![](https://www.gravatar.com/avatar/fd45da3c6a63fde520a12f548086e4c15194158912ddec68ee95e5d73c410672?d=identicon)[Jatbi](/maintainers/Jatbi)

---

Top Contributors

[![Jatbi](https://avatars.githubusercontent.com/u/25944822?v=4)](https://github.com/Jatbi "Jatbi (65 commits)")

---

Tags

frameworkSimpleECLO

### Embed Badge

![Health badge](/badges/eclo-app/health.svg)

```
[![Health](https://phpackages.com/badges/eclo-app/health.svg)](https://phpackages.com/packages/eclo-app)
```

###  Alternatives

[chameleon-system/chameleon-base

The Chameleon System core.

1026.5k3](/packages/chameleon-system-chameleon-base)[sproutcms/cms

Enterprise content management and framework

241.6k4](/packages/sproutcms-cms)[quantum/framework

The Quantum PHP Framework

402.8k1](/packages/quantum-framework)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
