Tài liệu GracePL
Ngôn ngữ lập trình production-ready với cú pháp quen thuộc như PHP, hiệu năng cao của Go. Bytecode VM, concurrency thực sự, module system, structured logging, metrics, circuit breaker và hơn 335 hàm tích hợp.
Bytecode VM
Biên dịch sang bytecode, chạy nhanh hơn interpreted scripting.
Concurrency thực sự
spawn, channel, WaitGroup, select — dùng goroutine của Go.
Module System
import / import_as / export — isolation đầy đủ, không global leak.
Web built-in
HTTP server, WebSocket, routing, CORS, rate limiting, TLS — production-ready.
Production Observability
JSON logging, Prometheus metrics, request tracing, circuit breaker, cron.
Database + Redis
MySQL, PostgreSQL, SQLite, Redis — connection pooling, transactions, Redis sessions.
1 Cài đặt
Tải binary (Windows)
Tải binary (Linux / macOS)
Build từ source (cần Go 1.22+)
git clone https://github.com/ghoullp/gracepl-lang
cd gracepl-lang
go build -o gracepl .
./gracepl --version # GracePL v3.0
Kiểm tra cài đặt thành công
gracepl --version
# GracePL v3.0 (go1.24.0)
gracepl run hello.gp
gracepl test ./examples/
gracepl lsp # khởi động Language Server
2 Quick Start
Hello World
<gp>
echo "Hello, World!\n";
gracepl run hello.gp
# Hello, World!
Ví dụ thực tế: REST API đơn giản
<gp>
db_connect("mysql://user:pass@localhost/mydb");
http_handle("/api/users", function($req) {
if ($req["method"] == "GET") {
$users = db_query("SELECT id, name, email FROM users");
http_json(["data" => $users, "count" => count($users)]);
} elseif ($req["method"] == "POST") {
$body = json_decode($req["body"]);
db_exec("INSERT INTO users (name, email) VALUES (?, ?)",
[$body["name"], $body["email"]]);
http_json(["status" => "created"], 201);
}
});
// Production middleware
http_request_id();
http_panic_recover();
http_cors_whitelist(["https://myapp.com"]);
metrics_enable("/metrics");
log_format("json");
http_serve(":8080");
Ví dụ: Concurrency
<gp>
$results = chan_make(10);
$jobs = [1, 2, 3, 4, 5];
// Chạy song song
foreach ($jobs as $n) {
$num = $n;
spawn {
$results <- $num * $num; // tính toán và gửi kết quả
};
}
// Thu gom kết quả
$total = 0;
foreach ($jobs as $_) {
$total += <-$results;
}
echo "Tổng bình phương: $total\n"; // 55
3 VS Code Extension
GracePL có extension chính thức cho Visual Studio Code với đầy đủ tính năng hiện đại:
- Syntax highlighting (TextMate grammar)
- Autocomplete — hàm, keyword, snippet
- Hover documentation
- Diagnostics real-time (lỗi cú pháp, undefined variable)
- Go to definition / Find all references
- Format on save
- Outline view
GracePL → Install.
Extension tự động tìm gracepl lsp hoặc dùng đường dẫn binary trong settings.
4 Cấu trúc file
Mọi file GracePL phải bắt đầu bằng tag <gp>:
<gp>
// Comment một dòng
# Cũng là comment
/*
Comment
nhiều dòng
*/
echo "Code của bạn ở đây";
File không cần tag đóng. Extension chuẩn là .gp.
5 Kiểu dữ liệu
| Kiểu | Mô tả | Ví dụ |
|---|---|---|
int |
Số nguyên 64-bit | 42, -7, 0 |
float |
Số thực 64-bit IEEE 754 | 3.14, -0.5 |
string |
Chuỗi UTF-8 bất biến | "hello", 'world' |
bool |
Boolean | true, false |
null |
Giá trị null | null |
array |
Mảng có chỉ số số | [1, 2, 3] |
map |
Map string-key | ["a" => 1] |
object |
Instance của class | new User() |
closure |
Hàm ẩn danh | fn($x) => $x*2 |
resource |
Handle C (channel, DB, file…) | chan_make() |
Ép kiểu
<gp>
$i = (int)"42"; // 42
$f = (float)"3.14"; // 3.14
$s = (string)100; // "100"
$b = (bool)1; // true
$a = (array)"hello"; // ["hello"]
// Kiểm tra kiểu
is_int($i); // true
is_string($s); // true
gettype($f); // "double"
6 Biến & Scope
<gp>
// Gán biến
$name = "Alice";
$age = 30;
$items = [1, 2, 3];
// Null coalescing
$val = $maybeNull ?? "default";
$cfg["timeout"] ??= 30;
// Biến trong hàm
function demo() {
$local = "chỉ trong hàm này";
// Truy cập biến global
global $name;
echo $name; // Alice
}
// Global declaration + init
global $counter = 0;
7 Toán tử
$a + $b // cộng
$a - $b // trừ
$a * $b // nhân
$a / $b // chia
$a % $b // chia lấy dư
$a ** $b // lũy thừa
$a . $b // nối chuỗi
$x += 5; $x -= 2; $x *= 3;
$x /= 4; $x %= 7; $x .= " text";
$a == $b // bằng (loose)
$a != $b // khác (loose)
$a === $b // bằng chặt (kiểm tra cả kiểu)
$a !== $b // khác chặt
$a < $b $a > $b $a <= $b $a >= $b
// Null coalescing
$a ?? $b // $b nếu $a là null
$a ??= $b; // gán $b cho $a nếu $a là null
$a && $b // AND (short-circuit)
$a || $b // OR (short-circuit)
!$a // NOT
$a and $b // AND (precedence thấp hơn)
$a or $b // OR (precedence thấp hơn)
// Ternary
$x = $cond ? "có" : "không";
// instanceof
$obj instanceof ClassName
$a & $b // AND
$a | $b // OR
$a ^ $b // XOR
~$a // NOT
$a << $n // shift left
$a >> $n // shift right
8 String
<gp>
$name = "Alice";
// Double-quoted — hỗ trợ interpolation
echo "Xin chào, $name!"; // Xin chào, Alice!
echo "Phần tử: {$arr[0]}";
echo "Method: {$obj->getName()}";
// Single-quoted — không interpolation
echo 'Đây là $name'; // Đây là $name
// Heredoc
$text = <<<EOT
Dòng 1
Biến: $name
EOT;
// Escape sequences
"\n" "\t" "\\" "\"" "\$"
// Hàm phổ biến
strlen($s)
strtoupper($s) / strtolower($s)
substr($s, 0, 5)
str_replace("old", "new", $s)
strpos($s, "needle") // false nếu không tìm thấy
trim($s) / ltrim() / rtrim()
explode(",", $s) // split → array
implode(", ", $arr) // join ← array
str_contains($s, "sub")
str_starts_with($s, "pre")
str_ends_with($s, "suf")
sprintf("%.2f", 3.14159)
preg_match('/\d+/', $s, $m)
preg_replace('/\s+/', ' ', $s)
9 Điều khiển luồng
<gp>
// if / elseif / else
if ($score >= 90) {
echo "Xuất sắc";
} elseif ($score >= 70) {
echo "Khá";
} else {
echo "Trung bình";
}
// while
$i = 0;
while ($i < 5) { $i++; }
// for
for ($i = 0; $i < 10; $i++) {
if ($i == 5) continue;
if ($i == 9) break;
echo $i;
}
// foreach
foreach ($items as $item) { echo $item; }
foreach ($map as $key => $val) { echo "$key=$val"; }
// switch
switch ($role) {
case "admin": echo "Quản trị"; break;
case "editor": echo "Biên tập"; break;
default: echo "Khách";
}
// match (expression)
$label = match ($code) {
200 => "OK",
404 => "Not Found",
500 => "Server Error",
default => "Unknown",
};
10 Hàm
<gp>
// Hàm thông thường
function greet(string $name, string $prefix = "Chào"): string {
return "$prefix, $name!";
}
// Variadic
function sum(...$nums): int {
return array_sum($nums);
}
// Closure — capture bằng use
$mult = 3;
$triple = function($x) use ($mult) { return $x * $mult; };
// Arrow function — auto-capture scope ngoài
$double = fn($x) => $x * 2;
// Higher-order
$numbers = [1, 2, 3, 4, 5];
$doubled = array_map(fn($x) => $x * 2, $numbers);
$evens = array_filter($numbers, fn($x) => $x % 2 == 0);
$total = array_reduce($numbers, fn($c, $x) => $c + $x, 0);
// Đệ quy
function fib($n) {
if ($n <= 1) return $n;
return fib($n-1) + fib($n-2);
}
11 Mảng & Map
<gp>
// Array
$arr = [1, 2, 3];
$arr[] = 4; // thêm vào cuối
count($arr); // 4
// Destructuring
[$a, $b, $c] = $arr;
[$first, , $third] = [10, 20, 30];
// Map
$user = [
"name" => "Alice",
"age" => 30,
"roles" => ["admin", "user"]
];
echo $user["name"]; // Alice
$user["email"] = "a@b.c"; // thêm
unset($user["age"]); // xóa
// Hàm array phổ biến
array_push($arr, $val); // sửa $arr trực tiếp — KHÔNG gán lại!
array_pop($arr);
array_shift($arr); // xóa đầu
array_unshift($arr, $val); // thêm đầu
in_array($needle, $arr);
array_search($needle, $arr);
array_keys($map);
array_values($arr);
array_merge($arr1, $arr2);
array_unique($arr);
array_reverse($arr);
array_chunk($arr, 3);
sort($arr);
usort($arr, fn($a, $b) => $a - $b);
array_sum($arr);
array_product($arr);
12 Class & OOP
<gp>
class Person {
public string $name;
private int $age;
static $count = 0;
function __construct(string $name, int $age) {
$this->name = $name;
$this->age = $age;
self::$count++;
}
public function greet(): string {
return "Chào, tôi là {$this->name}!";
}
static function getCount(): int { return self::$count; }
// Magic methods
function __toString(): string { return "Person({$this->name})"; }
function __get($prop) { return null; }
function __set($prop, $val) { }
function __call($m, $args) { }
}
// Kế thừa
class Employee extends Person {
function __construct(string $name, int $age, public string $company) {
parent::__construct($name, $age);
}
}
$p = new Person("Alice", 30);
echo $p->greet();
echo Person::getCount(); // 1
echo $p instanceof Person; // true
13 Interface & Trait
<gp>
interface Serializable {
function serialize(): string;
function unserialize(string $data): void;
}
abstract class Shape {
abstract function area(): float;
function describe(): string { return "Diện tích: " . $this->area(); }
}
trait Timestamps {
public $createdAt;
function setTimestamps() { $this->createdAt = time(); }
}
class Circle extends Shape implements Serializable {
use Timestamps;
function __construct(private float $r) { $this->setTimestamps(); }
function area(): float { return M_PI * $this->r ** 2; }
function serialize(): string { return json_encode(["r" => $this->r]); }
function unserialize(string $d): void { $this->r = json_decode($d)["r"]; }
}
final class Singleton {
static $inst = null;
static function get(): self { return self::$inst ??= new self(); }
}
14 Enum
<gp>
// Integer enum (auto-increment từ 0)
enum Direction {
NORTH, // 0
SOUTH, // 1
EAST, // 2
WEST // 3
}
// Integer enum với giá trị tùy chỉnh
enum HttpStatus {
OK = 200,
NOT_FOUND = 404,
ERROR = 500
}
// String enum
enum Color {
RED = "#FF0000",
GREEN = "#00FF00",
BLUE = "#0000FF"
}
echo Direction::EAST; // 2
echo HttpStatus::OK; // 200
echo Color::RED; // #FF0000
switch ($status) {
case HttpStatus::OK: echo "Thành công"; break;
case HttpStatus::NOT_FOUND: echo "Không tìm thấy"; break;
}
15 Type Hinting
<gp>
// Tham số + return type
function add(int $a, int $b): int {
return $a + $b;
}
// Nullable
function findUser(?int $id): ?array {
if ($id === null) return null;
return db_query_row("SELECT * FROM users WHERE id = ?", [$id]);
}
// Union type
function process(int|string $value): string {
return (string)$value;
}
// void return
function log(string $msg): void {
file_put_contents("app.log", $msg . "\n", FILE_APPEND);
}
// Class type
function greet(Person $p): string {
return "Chào {$p->name}";
}
16 Exception Handling
<gp>
// Custom exception
class ValidationError extends Error {
function __construct(string $msg, private string $field) {
parent::__construct($msg);
}
function getField(): string { return $this->field; }
}
function validateEmail(string $email): void {
if (!str_contains($email, "@")) {
throw new ValidationError("Email không hợp lệ", "email");
}
}
// try / catch / finally
try {
validateEmail("not-an-email");
$result = doRiskyOp();
} catch (ValidationError $e) {
echo "Validation: " . $e->getMessage() . " [" . $e->getField() . "]";
} catch (NetworkError|TimeoutError $e) {
echo "Network: " . $e->getMessage();
} catch ($e) {
echo "Lỗi chung: " . $e->getMessage();
} finally {
cleanup(); // luôn chạy
}
// Error chaining
function fetchUser($id) {
try {
return db_query_row("SELECT * FROM users WHERE id = ?", [$id]);
} catch ($e) {
throw error_wrap($e, "fetchUser($id) failed");
}
}
try {
fetchUser(99999);
} catch ($e) {
echo error_chain_message($e);
// "fetchUser(99999) failed: no rows returned"
}
17 Module System
import nạp code vào scope hiện tại. import_as chạy file
trong VM riêng, trả về map các symbols được export.
<gp>
// Chỉ export những gì cần thiết
export $PI = 3.14159265358979;
export function add($a, $b) {
return $a + $b + _epsilon(); // có thể gọi private
}
export function circle_area($r) {
return $PI * $r * $r;
}
export class Vector2D {
function __construct(public $x, public $y) {}
function magnitude() { return sqrt($this->x**2 + $this->y**2); }
}
// Private — không export
function _epsilon() { return 1e-10; }
$_config = ["precision" => 15];
<gp>
// import_as — isolated module
$math = import_as("lib/math.gp");
echo $math["add"](3, 4); // 7.0000000001 (+ epsilon)
echo $math["PI"]; // 3.14159265358979
echo $math["circle_area"](5); // 78.539...
$v = new $math["Vector2D"](3, 4);
echo $v->magnitude(); // 5.0
// Private không accessible:
// $math["_epsilon"] → null
// import thông thường — nạp vào scope
import "utils/helpers.gp";
helper_fn(); // accessible trực tiếp
18 Concurrency
<gp>
// ── Channel ────────────────────────────────────
$ch = chan_make(); // unbuffered
$ch = chan_make(10); // buffered size=10
$ch <- "hello"; // send (blocking nếu đầy)
$val = <-$ch; // receive (blocking nếu trống)
chan_close($ch);
// Non-blocking
[$val, $ok] = chan_try_recv($ch);
// ── spawn (goroutine) ─────────────────────────
$results = chan_make(5);
for ($i = 0; $i < 5; $i++) {
$n = $i;
spawn {
$results <- $n * $n;
};
}
// ── WaitGroup ─────────────────────────────────
$wg = wg_make();
for ($i = 0; $i < 10; $i++) {
wg_add($wg, 1);
$idx = $i;
spawn {
processItem($idx);
wg_done($wg);
};
}
wg_wait($wg);
// ── select ────────────────────────────────────
select {
case $data <- $ch1:
echo "Nhận: $data";
case $ch2 <- "pong":
echo "Gửi pong";
default:
echo "Không ready";
}
19 Context & Cancel
<gp>
// Tạo context có thể cancel
$ctx = ctx_make();
spawn {
ctx_wait_done($ctx);
echo "Worker dừng\n";
$done_ch <- true;
};
sleep_ms(2000); // worker chạy 2 giây
ctx_cancel($ctx); // báo dừng
// Context với timeout
$ctx = ctx_with_timeout(5000); // 5 giây
spawn {
ctx_wait_done($ctx);
if (ctx_cancelled($ctx)) {
echo "Timeout hoặc cancel\n";
}
};
// Truyền context qua hàm
function downloadFile($ctx, $url) {
if (ctx_done($ctx)) return null;
return http_get($url);
}
20 Mutex & Sync
<gp>
global $counter = 0;
$mu = mutex_make();
$wg = wg_make();
// Race-free concurrent counter
for ($i = 0; $i < 100; $i++) {
wg_add($wg, 1);
spawn {
mutex_lock($mu);
global $counter;
$counter++;
mutex_unlock($mu);
wg_done($wg);
};
}
wg_wait($wg);
echo $counter; // đúng 100
// mutex_try_lock — non-blocking
if (mutex_try_lock($mu)) {
// critical section
mutex_unlock($mu);
} else {
echo "lock busy";
}
21a Structured Logging
<gp>
// JSON format cho ELK/Loki/Datadog
log_format("json");
log_info("request processed", ["user_id" => 42, "latency_ms" => 12]);
// → {"timestamp":"...","level":"INFO","message":"request processed","user_id":42,"latency_ms":12}
log_debug("cache hit", ["key" => "users:42"]);
log_warn("slow query", ["ms" => 1500]);
log_error("connection failed", ["host" => "db.example.com"]);
// Set log level
log_level("warn"); // chì warn + error
// Text mode (development)
log_format("text");
// → [INFO] 2026-03-05 14:30:00 request processed
21b Prometheus Metrics
<gp>
// Bật /metrics endpoint
metrics_enable("/metrics");
// Endpoint tųâ trả về Prometheus format:
// gracepl_requests_total 12345
// gracepl_request_errors_total 23
// gracepl_request_duration_ms_bucket{le="10ms"} 8000
// gracepl_request_duration_ms_bucket{le="50ms"} 10000
// gracepl_goroutines 42
// gracepl_memory_alloc_bytes 52428800
// gracepl_uptime_seconds 86400
// Xem active connections
$n = http_active_connections();
21c Circuit Breaker
<gp>
// Tąâ circuit breaker: name, max failures, reset timeout (ms)
circuit_breaker("payment-api", 5, 30000);
// Gội function qua circuit breaker
$result = circuit_breaker_call("payment-api", function() {
return http_post("https://payment.api/charge", ["amount" => 100]);
});
// Circuit mô khi open
if (isset($result["error"])) {
log_warn("payment circuit open, fallback");
}
// State: "closed" → "open" → "half-open"
$state = circuit_breaker_state("payment-api");
// Reset manual
circuit_breaker_reset("payment-api");
21d Event Bus
<gp>
// Ðâng ký handler
event_on("user.created", function($data) {
log_info("new user", ["email" => $data["email"]]);
email_send(["to" => $data["email"], "subject" => "Welcome!"]);
});
// Emit event (trâ về số handlers Ðąâ gội)
$count = event_emit("user.created", ["email" => "alice@example.com"]);
// Hųy handlers
event_off("user.created");
21e Cron Scheduler
<gp>
// Schedule vói @every syntax
$cleanup = cron("@every 5m", function() {
db_exec("DELETE FROM sessions WHERE expired_at < NOW()");
});
$health = cron("@every 30s", function() {
$res = http_get("https://api.example.com/health");
if (!$res) { log_error("upstream down"); }
});
// Dùng job cų thể
cron_stop($cleanup);
// Dùng tất cẩ
cron_stop_all();
21f Session & Redis
<gp>
// Chuyển session sang Redis (scale multi-instance)
session_redis("redis.cluster:6379", "password", "0");
// Sų̉ dų̣ng nhų bình thųồng
session_start();
session_set("user_id", 42);
$uid = session_get("user_id"); // 42
session_flash("message", "Đã lưu thành công"); // one-time message (sau redirect)
$msg = session_flash_get("message"); // đọc và xóa flash
session_destroy();
Database Pool Configuration
<gp>
db_connect("user:pass@tcp(host:3306)/mydb");
db_set_timeout(5000);
Redis Configuration
<gp>
redis_connect("redis.cluster:6379", "password", "0");
redis_set("key", "value");
$val = redis_get("key");
21g Config Reload
<gp>
// Ðâng ký callback khi config reload
config_on_reload(function() {
global $config;
$config = json_decode(file_get_contents("config.json"));
log_info("config reloaded");
});
// Trigger reload
config_reload();
// Unix: kill -SIGHUP <pid> cùng trigger
21 CLI Reference
| Lệnh | Mô tả |
|---|---|
gracepl run <file> |
Compile & chạy file .gp |
gracepl test <dir> |
Chạy tất cả *_test.gp trong thư mục |
gracepl repl |
Interactive REPL |
gracepl lsp |
Language Server (stdio, VS Code) |
gracepl debug <file> |
DAP debugger (VS Code breakpoints) |
gracepl fmt -w <file> |
Format code (ghi đè file) |
gracepl lint <file> |
Static analysis |
gracepl profile <file> |
Profiler sampling |
gracepl build <file> |
Build standalone binary |
gracepl emit <file> |
Disassemble bytecode |
gracepl tokens <file> |
Dump lexer tokens |
gracepl init [name] |
Tạo grace.json |
gracepl install [pkg] |
Cài package |
gracepl remove pkg |
Gỡ package |
gracepl list |
Danh sách packages |
gracepl stop [port] |
Dừng HTTP server theo port |
gracepl restart [port] |
Restart HTTP server |
gracepl --version |
In phiên bản |
22 Testing
<gp>
// File có tên *_test.gp được test runner phát hiện tự động
import "../lib/test.gp";
describe("Math", function() {
it("cộng hai số", function() {
assert_eq(1 + 1, 2);
assert_eq(10 + 20, 30);
});
it("handles edge cases", function() {
assert_eq(0 + 0, 0);
assert_eq(-1 + 1, 0);
});
});
describe("String", function() {
it("converts to uppercase", function() {
assert_eq(strtoupper("hello"), "HELLO");
});
it("contains substring", function() {
assert_true(str_contains("gracepl", "grace"));
});
});
describe("Exceptions", function() {
it("catches thrown errors", function() {
assert_throws(function() {
throw new Error("test error");
});
});
});
gracepl test ./examples/
# [PASS] cộng hai số (0.12ms)
# [PASS] string operations (0.08ms)
# [PASS] exception được ném (0.05ms)
# [PASS] array operations (0.09ms)
# ─────────────────────────────────────
# 4/4 passed in 0.34ms
23 Package Manager
gracepl pkg install gracepl/http-utils
gracepl pkg install gracepl/validator
gracepl pkg install gracepl/jwt
gracepl pkg list
# gracepl/http-utils v1.2.0
# gracepl/validator v2.0.1
# gracepl/jwt v1.0.0
<gp>
// Package được cài trong vendor/
$validator = import_as("vendor/gracepl/validator/main.gp");
$errors = $validator["validate"]($data, [
"email" => "required|email",
"password" => "required|min:8",
"age" => "integer|min:18"
]);
if (count($errors) > 0) {
http_json($res, ["errors" => $errors], 422);
return;
}
23b Debugger (DAP — VS Code)
GracePL hỗ trợ Debug Adapter Protocol (DAP), tích hợp trực tiếp với VS Code debugger.
Khởi động
gracepl debug myapp.gp
# Lắng nghe DAP trên stdin/stdout — VS Code kết nối tự động
Cấu hình VS Code (launch.json)
{
"version": "0.2.0",
"configurations": [
{
"type": "gracepl",
"request": "launch",
"name": "Debug GracePL",
"program": "${file}"
}
]
}
Tính năng
- Breakpoints (line breakpoints)
- Step Over / Step Into / Step Out
- Xem locals, globals, upvalues
- Call stack
- Debug Console (eval expression)
24 HTTP & Web
<gp>
// ── HTTP Server ────────────────────────────────
http_handle("/", function($req) {
http_json(["message" => "Hello from GracePL!"]);
});
// Route động: $req["params"]["id"] — lấy path segment
http_handle("/users/:id", function($req) {
$id = $req["params"]["id"];
$user = db_query_row("SELECT * FROM users WHERE id = ?", [$id]);
if (!$user) { return http_json(["error" => "Not found"], 404); }
http_json($user);
});
// Query string: $req["query"]["name"] — lấy ?name=Alice
http_handle("/search", function($req) {
$q = isset($req["query"]["q"]) ? $req["query"]["q"] : "";
http_json(["query" => $q]);
});
// Middleware stack — gọi trước http_serve()
http_request_id(); // inject X-Request-Id header
http_panic_recover(); // catch panic → 500 JSON
http_cors_whitelist(["https://myapp.com"]); // CORS cho các origin được phép
http_rate_limit(100, 60); // 100 req/phút mỗi IP
http_security_headers(); // X-Frame-Options, CSP, HSTS…
http_gzip(); // nén gzip tự động
metrics_enable("/metrics"); // Prometheus /metrics endpoint
http_health("/health"); // GET /health → {"status":"ok"}
log_format("json"); // log dưới dạng JSON
http_static("public", "/static"); // serve file tĩnh
http_use(function($req) { // custom middleware
return true; // false để chặn request
});
http_serve(":8080");
Request object ($req)
| Key | Mô tả | Ví dụ |
|---|---|---|
$req["params"] | Path segments dynamic (/users/:id) | $req["params"]["id"] |
$req["query"] | Query string (?name=Alice&page=2) | $req["query"]["name"] |
$req["body"] | Request body (string). Form/multipart → body đã parse | json_decode($req["body"]) |
$req["files"] | Upload: $req["files"][fieldName] = array of {name, size, temp_path, content_type} | — |
$req["method"] | HTTP method | "GET", "POST"… |
$req["headers"] | Request headers (map) | $req["headers"]["authorization"] |
$req["ip"] | Client IP | "127.0.0.1" |
/* là path literal, không phải wildcard. Dùng :param hoặc {param} cho segment động.✅
/news/:slug — đúng❌
/news/* — không match được gì
null != false trả về true — không dùng $params["x"] != false để kiểm tra key có tồn tại. Dùng isset($params["x"]).
HTTP Middleware & Utility
| Hàm | Mô tả |
|---|---|
http_cors($origin) | Bật CORS — nhận string, "*" hoặc một origin cụ thể |
http_cors_whitelist($origins) | CORS whitelist — nhận array các origin được phép |
http_rate_limit($reqs, $window_sec) | Giới hạn $reqs request/IP trong $window_sec giây |
http_security_headers([$csp]) | Thêm X-Frame-Options, X-Content-Type-Options, CSP, HSTS, Referrer-Policy |
http_gzip() | Bật nén gzip tự động cho response |
http_health($path) | Tạo health-check endpoint: GET $path → {"status":"ok"} |
http_static($dir [, $prefix]) | Serve file tĩnh từ thư mục $dir tại URL $prefix |
http_use($fn) | Custom GracePL middleware — $fn($req) trả về true (tiếp tục) hoặc false (chặn) |
http_redirect($url [, $code]) | Redirect → mặc định 302 |
http_redirect_back([$fallback]) | Redirect về Referer; không có thì dùng $fallback (mặc định /) |
http_request_id([$header]) | Inject X-Request-Id vào request & response (tạo mới nếu không có) |
http_panic_recover() | Bắt panic trong handler → trả về 500 JSON thay vì crash server |
http_active_connections() | Số kết nối HTTP đang active → int |
<gp>
// ── WebSocket ──────────────────────────────────
// Giới hạn dung lượng nhận tối đa mỗi tin nhắn/frame (vd: 10MB)
ws_set_read_limit(10485760);
listen("/chat") {
on("connect", function($conn) {
ws_room_join("general");
});
on("message", function($data) {
// Broadcast string hoặc binary raw
ws_room_broadcast("general", $data["text"]);
});
}
// ── HTTP Client ────────────────────────────────
$res = http_get("https://api.example.com/data");
$post = http_post("https://api.example.com/users", [
"headers" => ["Content-Type" => "application/json"],
"body" => json_encode(["name" => "Alice"])
]);
WebSocket Room Helpers
| Hàm | Mô tả |
|---|---|
ws_room_join($room) | Thêm kết nối WS hiện tại vào room |
ws_room_leave($room) | Rời room |
ws_room_broadcast($room, $msg) | Gửi text tới tất cả member trong room |
ws_room_broadcast_binary($room, $bytes) | Gửi binary tới tất cả member trong room |
ws_room_members($room) | Trả về array conn ID của các member |
ws_rooms() | Trả về danh sách tất cả room đang active |
ws_conn_id() | Lấy ID của kết nối WS hiện tại (trong handler) |
ws_ping() | Gửi ping frame tới kết nối hiện tại |
ws_set_read_limit($bytes) | Giới hạn kích thước frame nhận tối đa |
24b Template Engine
GracePL có template engine tích hợp hỗ trợ variable substitution, conditionals, loops và includes.
Cú pháp template
| Cú pháp | Mô tả |
|---|---|
{{key}} |
Thay bằng giá trị biến — tự động HTML-escape (hỗ trợ cả {{$key}}) |
{{key | raw}} |
Output HTML thô không escape — dùng khi biến chứa HTML markup (card list, content block…) |
{{if key}} ... {{endif}} |
Conditional block (truthy); hỗ trợ lồng nhau |
{{if !key}} ... {{endif}} |
Negated conditional |
{{if key}} ... {{else}} ... {{endif}} |
Conditional với else branch |
{{key | slice:start:end}} / {{key | upper}} / {{key | lower}} |
Filter: cắt chuỗi, chuyển hoa/thường |
{{key | default:"fallback"}} / {{key | length}} |
Filter: giá trị mặc định, độ dài |
{{foreach items as item}} ... {{end}} |
Loop qua array/map |
{{include "path"}} |
Inline file template khác (dùng chung data) |
Hàm render
| Hàm | Mô tả |
|---|---|
render($file, $vars) |
Render template từ file với biến |
render_string($tmpl, $vars) |
Render template từ chuỗi |
include($file) |
Đọc file thuần (không thay biến) |
Ví dụ: Layout với include
<!DOCTYPE html>
<html>
<head><title>{{title}} — MyApp</title></head>
<body>
{{include "templates/header.html"}}
<main>{{content}}</main>
{{include "templates/footer.html"}}
</body>
</html>
Ví dụ: Conditional + Loop
<h1>{{page_title}}</h1>
{{if has_items}}
<ul>
{{foreach items as item}}
<li>{{item}}</li>
{{end}}
</ul>
{{else}}
<p>Không có item nào.</p>
{{endif}}
Pattern: Render nested objects
<gp>
$posts = [
["title" => "Post 1", "date" => "2026-01-15"],
["title" => "Post 2", "date" => "2026-01-20"]
];
$cards = "";
foreach ($posts as $post) {
$cards .= render("templates/card.html", $post);
}
$html = render("templates/list.html", [
"page_title" => "Blog",
"cards" => $cards
]);
{{var}} sẽ escape HTML (<, >, &…). Nếu biến chứa HTML markup (ví dụ: card list, content bài viết), phải dùng {{var | raw}} — nếu không HTML sẽ hiển thị dưới dạng text thay vì được render.
24c Biến Môi Trường
GracePL cung cấp env_load() và env() để quản lý cấu hình qua file .env — không hardcode secret trong source code.
env_load(".env") trước khi dùng env(). Nếu không, env() chỉ đọc từ OS environment variables.
File .env
# Cổng phải có dấu ":" ở đầu
APP_PORT=:3001
DB_HOST=localhost
DB_NAME=mydb
DB_USER=root
DB_PASS=secret
ALLOWED_ORIGIN=https://yourdomain.com
Đọc biến môi trường
<gp>
// Nạp file .env (relative tới thư mục của script — không phải cwd)
env_load(".env");
// Đọc biến — tham số 2 là giá trị mặc định nếu key không tồn tại
$port = env("APP_PORT", ":8000");
$dbHost = env("DB_HOST", "localhost");
$debug = env("DEBUG", "false");
// Khởi động server
http_serve($port);
Lưu ý về cổng (port)
| Giá trị APP_PORT | Kết quả |
|---|---|
APP_PORT=:3001 | ✅ Đúng — listen trên port 3001 |
APP_PORT=3001 | ❌ Sai — thiếu :, server không start được |
Bảo mật
- Luôn thêm
.envvào.gitignore— không commit secret lên repo - Dùng
.env.example(không có giá trị thật) để hướng dẫn team - Không hardcode API key, database password trong source code
24d HTTPS / TLS
Chạy server với SSL/TLS, hỗ trợ TLS 1.2+ và các cipher suite an toàn. Cần file certificate.
<gp>
// http_serve_tls(addr, cert_file, key_file)
// TLS 1.2+ với AES-GCM và ChaCha20-Poly1305
http_handle("/", function($req) {
return http_json(["status" => "ok"]);
});
// Tự ký (development)
// openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
http_serve_tls(":443", "cert.pem", "key.pem");
// hoặc port khác:
// http_serve_tls(":8443", "certs/cert.pem", "certs/key.pem");
| Hàm | Tham số | Mô tả |
|---|---|---|
http_serve_tls($addr, $cert, $key) | addr=":443", cert/key=PEM file path | Khởi động HTTPS server với graceful shutdown |
http_serve($addr) | addr=":8080" | HTTP thường — dùng cho development hoặc sau reverse proxy |
24e HTTP Server Config
Tinh chỉnh timeout, request body limit và access log của HTTP server. Phải gọi trước http_serve().
<gp>
// http_server_config(key, value)
http_server_config("read_timeout", 30); // giây — mặc định 30s
http_server_config("write_timeout", 30); // giây — mặc định 30s
http_server_config("idle_timeout", 120); // giây — mặc định 120s
http_server_config("body_limit", 5242880); // bytes — mặc định 10MB (10<<20)
http_server_config("access_log", false); // tắt access log
http_serve(":8080");
| Key | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
read_timeout | int (giây) | 30 | Thời gian tối đa đọc toàn bộ request |
write_timeout | int (giây) | 30 | Thời gian tối đa ghi response |
idle_timeout | int (giây) | 120 | Thời gian keep-alive connection chờ request tiếp |
body_limit | int (bytes) | 10485760 | Kích thước tối đa request body |
access_log | bool | true | Bật/tắt access log cho mỗi request |
24f CSRF Protection
Bảo vệ form POST khỏi Cross-Site Request Forgery. Token có chữ ký HMAC-SHA256, hết hạn sau 2 giờ.
<gp>
// Bước 1: Khởi tạo CSRF (trước khi serve)
csrf_init("secret-key-của-bạn"); // nếu bỏ qua → tự sinh random secret
// Bước 2: Sinh token cho form GET
http_handle("/form", function($req) {
$token = csrf_token();
return render("templates/form.html", ["csrf_token" => $token]);
});
// Bước 3: Kiểm tra token khi POST
http_handle("POST", "/submit", function($req) {
$token = $req["body"]["_csrf"] ?? $req["headers"]["x-csrf-token"] ?? "";
if (!csrf_verify($token)) {
return http_status(403, "CSRF token không hợp lệ");
}
// xử lý form...
return http_redirect("/success");
});
<form method="POST" action="/submit">
<input type="hidden" name="_csrf" value="{{csrf_token | raw}}">
<input type="text" name="name" placeholder="Tên">
<button type="submit">Gửi</button>
</form>
| Hàm | Mô tả |
|---|---|
csrf_init($secret?) | Khởi tạo CSRF; nếu bỏ secret → tự sinh an toàn |
csrf_token() | Sinh token mới (ký HMAC-SHA256, hiệu lực 2 giờ) |
csrf_verify($token) | Kiểm tra token hợp lệ và chưa hết hạn → bool |
24g HTTP Route Helpers
Sugar functions để đăng ký route theo method cụ thể — ngắn hơn http_handle().
<gp>
// Tương đương http_handle("GET", "/path", $fn)
http_route_get("/api/users", function($req) {
return http_json(db_query("SELECT id, name FROM users"));
});
http_route_post("/api/users", function($req) {
$data = json_decode($req["body"]);
db_exec("INSERT INTO users (name) VALUES (?)", [$data["name"]]);
return http_json(["status" => "created"], 201);
});
http_route_put("/api/users/:id", function($req) {
$id = $req["params"]["id"];
$data = json_decode($req["body"]);
db_exec("UPDATE users SET name = ? WHERE id = ?", [$data["name"], $id]);
return http_json(["updated" => true]);
});
http_route_delete("/api/users/:id", function($req) {
$id = $req["params"]["id"];
db_exec("DELETE FROM users WHERE id = ?", [$id]);
return http_json(["deleted" => true]);
});
http_serve(":8080");
| Hàm | Tương đương |
|---|---|
http_route_get($path, $fn) | http_handle("GET", $path, $fn) |
http_route_post($path, $fn) | http_handle("POST", $path, $fn) |
http_route_put($path, $fn) | http_handle("PUT", $path, $fn) |
http_route_delete($path, $fn) | http_handle("DELETE", $path, $fn) |
25 Thư viện: String
| Hàm | Mô tả |
|---|---|
strlen($s) |
Độ dài chuỗi (bytes) |
strtoupper/lower($s) |
Chuyển hoa/thường |
substr($s, $start, $len) |
Lấy chuỗi con |
str_replace($s, $r, $h) |
Thay thế chuỗi |
strpos($h, $n) |
Vị trí chuỗi (false nếu không có) |
trim($s) |
Xóa khoảng trắng đầu/cuối |
explode($d, $s) |
Split thành array |
implode($d, $a) |
Join array thành string |
str_contains($s, $n) |
Kiểm tra chứa chuỗi con |
str_pad($s, $n, $p, $t) |
Pad chuỗi |
sprintf($fmt, ...) |
Format string |
preg_match($p, $s, &$m) |
Regex match |
preg_replace($p, $r, $s) |
Regex replace |
md5/sha1/sha256($s) |
Hash chuỗi |
base64_encode/decode($s) |
Base64 |
urlencode/decode($s) |
URL encode/decode |
htmlspecialchars($s) / htmlentities($s) / html_decode($s) |
Escape / decode HTML |
addslashes($s) / strip_tags($s) |
Escape cho SQL / xóa thẻ HTML |
markdown_to_html($s) |
Chuyển Markdown sang HTML |
slugify($s [, $lang]) |
Chuỗi URL-friendly (Unicode) |
26 Thư viện: Array
| Hàm | Mô tả |
|---|---|
count($a) |
Số phần tử |
array_push($a, $v) |
Thêm vào cuối — trả về số phần tử mới, không trả về mảng. Không viết $a = array_push($a, $v) |
array_pop($a) |
Xóa và trả về phần tử cuối |
array_shift($a) |
Xóa và trả về phần tử đầu |
array_unshift($a, $v) |
Thêm vào đầu |
array_map($fn, $a) |
Map qua mỗi phần tử |
array_filter($a, $fn) |
Lọc phần tử |
array_reduce($a, $fn, $init) |
Reduce |
array_merge($a, $b) |
Gộp hai mảng |
array_unique($a) |
Xóa trùng |
array_keys($m) |
Lấy tất cả keys |
array_values($a) |
Lấy tất cả values |
array_chunk($a, $n) |
Chia thành chunks |
in_array($v, $a) |
Kiểm tra phần tử tồn tại |
sort/rsort($a) |
Sắp xếp tăng/giảm |
usort($a, $fn) |
Sắp xếp theo hàm so sánh |
array_sum($a) |
Tổng các phần tử |
array_combine($keys, $vals) |
Tạo map từ 2 arrays |
array_flip($a) |
Đổi key↔value |
array_slice($a, $off, $len) |
Cắt mảng |
array_splice($a, $off, $n, $r) |
Xóa và thay thế |
27 Thư viện: Math
| Hàm / Hằng | Mô tả |
|---|---|
M_PI |
π ≈ 3.14159… |
M_E |
e ≈ 2.71828… |
abs($n) |
Giá trị tuyệt đối |
ceil($n) / floor($n) |
Làm tròn lên/xuống |
round($n, $p) |
Làm tròn p chữ số thập phân |
sqrt($n) |
Căn bậc hai |
pow($b, $e) |
Lũy thừa |
log($n) / log10($n) |
Logarithm tự nhiên / cơ số 10 |
sin/cos/tan($r) |
Lượng giác (radian) |
asin/acos/atan($n) |
Lượng giác ngược |
atan2($y, $x) |
Arctangent 2 chiều |
min($a, $b, …) |
Giá trị nhỏ nhất |
max($a, $b, …) |
Giá trị lớn nhất |
rand($min, $max) |
Số ngẫu nhiên nguyên |
intdiv($a, $b) |
Chia nguyên |
fmod($a, $b) |
Chia lấy dư float |
number_format($n, $d) |
Format số với dấu phẩy |
is_nan($n) / is_inf($n) |
Kiểm tra NaN / Infinity |
28 Thư viện: File I/O
<gp>
// Đọc / ghi
$content = file_get_contents("data.txt");
file_put_contents("out.txt", $content);
file_put_contents("log.txt", "Line\n", FILE_APPEND);
// Dòng
$lines = file("data.txt"); // array các dòng
foreach ($lines as $line) { echo trim($line); }
// Kiểm tra
file_exists("path")
is_file("path")
is_dir("path")
// Thư mục
mkdir("new_dir", 0755, true); // recursive
rmdir("dir");
scandir("dir"); // [".", "..", "file.txt", ...]
// Copy / Move / Delete
copy("src", "dst");
rename("old", "new");
unlink("file.txt");
// Đường dẫn
basename("/path/to/file.txt"); // "file.txt"
dirname("/path/to/file.txt"); // "/path/to"
realpath("../relative");
pathinfo("file.txt.gz"); // ["extension" => "gz", ...]
// File size / permissions
filesize("file.txt");
filemtime("file.txt"); // timestamp sửa đổi
chmod("file.txt", 0644);
29 Thư viện: JSON
<gp>
$data = ["name" => "Alice", "age" => 30, "tags" => ["go", "php"]];
// Encode
$json = json_encode($data);
// {"name":"Alice","age":30,"tags":["go","php"]}
$pretty = json_encode($data, JSON_PRETTY_PRINT);
// Decode → map/array
$map = json_decode($json);
echo $map["name"]; // Alice
echo $map["tags"][0]; // go
// Từ file
$config = json_decode(file_get_contents("config.json"));
file_put_contents("output.json", json_encode($result, JSON_PRETTY_PRINT));
30 Thư viện: HTTP Client
Gọi API bên ngoài bằng các hàm HTTP client tích hợp sẵn.
| Hàm | Mô tả |
|---|---|
http_get($url, $headers?) | GET request, trả về response body |
http_post($url, $body, $headers?) | POST request |
http_put($url, $body, $headers?) | PUT request |
http_delete($url, $headers?) | DELETE request |
http_request($method, $url, $body?, $headers?) | Generic HTTP request tùy chỉnh |
http_download($url, $path) | Tải file về đĩa |
<gp>
// GET request
$body = http_get("https://api.example.com/users");
$data = json_decode($body);
echo $data[0]["name"];
// POST với JSON body
$token = "secret";
$resp = http_post(
"https://api.example.com/users",
json_encode(["name" => "Alice", "email" => "alice@example.com"]),
["Content-Type" => "application/json", "Authorization" => "Bearer $token"]
);
$created = json_decode($resp);
echo "Created user #" . $created["id"];
// PUT — cập nhật resource
http_put("https://api.example.com/users/1",
json_encode(["name" => "Alice Updated"]),
["Content-Type" => "application/json"]);
// DELETE
http_delete("https://api.example.com/users/1");
// Generic request
$resp = http_request("PATCH", "https://api.example.com/users/1",
json_encode(["active" => true]),
["Content-Type" => "application/json"]);
// Download file về đĩa
http_download("https://example.com/report.pdf", "downloads/report.pdf");
echo "Tải xong!";
31 Thư viện: Database
<gp>
// Kết nối
// Kết nối (MySQL, PostgreSQL, SQLite)
db_connect("user:pass@tcp(localhost:3306)/mydb");
// db_connect("postgres://user:pass@localhost/mydb");
// db_connect("sqlite://./data.db");
// Query — trả về array of maps
$rows = db_query("SELECT id, name FROM users WHERE active = ?", [1]);
foreach ($rows as $row) {
echo "{$row['id']}: {$row['name']}\n";
}
// Single row
$user = db_query_row("SELECT * FROM users WHERE id = ?", [42]);
// Execute (INSERT, UPDATE, DELETE, DDL)
db_exec("INSERT INTO users (name, email) VALUES (?, ?)", ["Alice", "a@b.c"]);
db_exec("UPDATE users SET name = ? WHERE id = ?", ["Alice Updated", 42]);
db_exec("DELETE FROM users WHERE id = ?", [42]);
// Transaction
$tx = db_begin();
db_tx_exec($tx, "UPDATE accounts SET balance = balance - 100 WHERE id = ?", [1]);
db_tx_exec($tx, "UPDATE accounts SET balance = balance + 100 WHERE id = ?", [2]);
db_commit($tx);
// Prepared statements
$stmt = db_prepare("INSERT INTO logs (msg) VALUES (?)");
for ($i = 0; $i < 1000; $i++) {
db_stmt_exec($stmt, ["log message $i"]);
}
db_stmt_close($stmt);
// Timeout + close
db_set_timeout(5000);
db_close();
32 Thư viện: Channel & Sync
| Hàm | Mô tả |
|---|---|
chan_make($size?) |
Tạo channel (size=64 nếu bỏ) |
chan_send($ch, $v) |
Gửi giá trị |
chan_recv($ch) |
Nhận giá trị (blocking) |
chan_try_recv($ch) |
Nhận không blocking → [$v, $ok] |
chan_close($ch) |
Đóng channel |
chan_len($ch) |
Số items đang đợi trong buffer |
wg_make() |
Tạo WaitGroup |
wg_add($wg, $n) |
Thêm n vào counter |
wg_done($wg) |
Trừ 1 từ counter |
wg_wait($wg) |
Chờ counter về 0 |
mutex_make() |
Tạo Mutex |
mutex_lock($m) |
Lock (blocking) |
mutex_unlock($m) |
Unlock |
mutex_try_lock($m) |
Try lock → bool |
ctx_make() |
Tạo context |
ctx_cancel($ctx) |
Cancel context |
ctx_with_timeout($ms) |
Context với timeout (ms) |
ctx_done($ctx) |
Kiểm tra đã done? |
ctx_wait_done($ctx) |
Block tới khi done |
sleep_ms($ms) |
Ngủ $ms mili-giây |
go_run($fn, $args) |
Chạy callable trong goroutine |
33 Thư viện: Crypto & Security
Password
| Hàm | Mô tả |
|---|---|
password_hash($pwd [, $algo, $cost]) | Bcrypt hash — lưu DB, không reversible. cost mặc định 10 |
password_verify($pwd, $hash) | Kiểm tra password vs bcrypt hash → bool |
random_bytes($n) | Hex string $n bytes ngẫu nhiên crypto/rand (mặc định 32, max 4096) |
Hashing
| Hàm | Mô tả |
|---|---|
md5($s) | MD5 hex — chỉ dùng cho checksum, không dùng cho auth |
sha1($s) | SHA1 hex |
sha256($s) | SHA-256 hex |
hash_hmac($algo, $data, $key) | HMAC-SHA256 hex — algo: "sha256" |
AES Encryption (AES-256-GCM)
<gp>
$key = "secret-key-của-bạn"; // tự SHA-256 thành 32 bytes nội bộ
// Mã hóa → base64(nonce[12] + ciphertext + tag[16])
$cipher = aes_encrypt("thông tin nhạy cảm", $key);
echo $cipher; // base64 string
// Giải mã
$plain = aes_decrypt($cipher, $key);
echo $plain; // "thông tin nhạy cảm"
// Dùng key 32-byte raw (không qua SHA-256 nội bộ)
$rawKey = bytes_from_hex("0123456789abcdef...");
$cipher2 = aes_encrypt_key("data", $rawKey);
$plain2 = aes_decrypt_key($cipher2, $rawKey);
| Hàm | Mô tả |
|---|---|
aes_encrypt($plain, $key [, $raw]) | AES-256-GCM encrypt → base64. $raw=true → KBytes |
aes_decrypt($cipher_b64, $key [, $raw]) | Giải mã base64 → string plaintext |
aes_encrypt_key($plain, $bytes_key) | Encrypt với KBytes key (phải đúng 16/24/32 bytes) |
aes_decrypt_key($cipher, $bytes_key) | Decrypt với KBytes key |
RSA Asymmetric Encryption
<gp>
// Sinh cặp key RSA
$keys = rsa_generate(2048); // {"private": "-----BEGIN...", "public": "-----BEGIN..."}
$priv = $keys["private"];
$pub = $keys["public"];
// Ký dữ liệu (SHA-256 hoặc SHA-512)
$sig = rsa_sign("payload data", $priv, "sha256");
// Xác minh chữ ký
$ok = rsa_verify("payload data", $sig, $pub, "sha256");
// Mã hóa bằng public key (RSA-OAEP)
$enc = rsa_encrypt("secret msg", $pub);
$dec = rsa_decrypt($enc, $priv);
| Hàm | Mô tả |
|---|---|
rsa_generate($bits?) | Sinh RSA key pair → {private, public} PEM strings. bits mặc định 2048 |
rsa_sign($data, $priv_pem [, $algo]) | Ký → base64 signature. algo: "sha256" (mặc định), "sha512" |
rsa_verify($data, $sig_b64, $pub_pem [, $algo]) | Xác minh chữ ký → bool |
rsa_encrypt($plain, $pub_pem) | RSA-OAEP encrypt → base64 |
rsa_decrypt($cipher_b64, $priv_pem) | RSA-OAEP decrypt → plaintext |
34 Thư viện: Process Management
Chạy lệnh hệ thống, script ngoài, hoặc tool deploy từ GracePL.
<gp>
// proc_exec(cmd [, args, cwd, stdin]) → {stdout, exit_code}
// Blocking — chờ process kết thúc
$result = proc_exec("git", ["log", "--oneline", "-5"], "/app");
echo $result["stdout"]; // output của git
echo $result["exit_code"]; // 0 = thành công
// shell_exec(cmd) — chạy qua shell (sh -c / cmd /C), trả về output string
$files = shell_exec("ls -la /var/log");
// proc_exec_async — non-blocking, trả về resource handle
$handle = proc_exec_async("npm", ["run", "build"], "/app/frontend");
// ... làm việc khác trong lúc build chạy ...
$res = proc_wait($handle); // chờ kết thúc
echo "Build exit: " . $res["exit_code"];
// Với stdin (pipe input)
$out = proc_exec("grep", ["error"], "", "line1\nERROR: fail\nline3");
echo $out["stdout"]; // "ERROR: fail"
| Hàm | Mô tả |
|---|---|
proc_exec($cmd [, $args, $cwd, $stdin]) | Chạy process blocking → {stdout, exit_code} |
proc_exec_async($cmd [, $args, $cwd, $stdin]) | Chạy async → resource handle |
proc_wait($handle) | Chờ async process → {stdout, exit_code} |
shell_exec($cmd) | Chạy qua shell → output string (như `cmd` trong PHP) |
shell_exec() — nguy cơ command injection. Dùng proc_exec() với args array để tham số được escape tự động.
35 Thư viện: TCP / UDP / DNS
Raw network programming — xây dựng proxy, custom protocol server, health check, DNS lookup.
TCP Client
<gp>
// Kết nối TCP
$conn = tcp_connect("example.com", 80, 5000); // timeout 5000ms
if ($conn == false) { echo "Kết nối thất bại\n"; }
// Gửi / nhận
tcp_write($conn, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
$resp = tcp_read($conn, 4096);
echo $resp;
echo tcp_remote_addr($conn); // "93.184.216.34:80"
echo tcp_local_addr($conn); // "192.168.1.5:54321"
tcp_set_deadline($conn, 3000); // timeout 3s cho lần đọc/ghi tiếp
tcp_close($conn);
TCP Server
<gp>
// Cách 1: tcp_serve (blocking, xử lý từng conn trong goroutine)
tcp_serve(9000, function($conn) {
$data = tcp_read($conn, 1024);
tcp_write($conn, "Echo: " . $data);
tcp_close($conn);
});
// Cách 2: Manual accept loop
$server = tcp_listen(9000);
echo "Listening: " . tcp_server_addr($server) . "\n";
while (true) {
$conn = tcp_accept($server);
spawn {
$msg = tcp_read($conn, 1024);
tcp_write($conn, strtoupper($msg));
tcp_close($conn);
}
}
tcp_server_close($server);
UDP
<gp>
// UDP gửi (fire-and-forget)
udp_send("logger.internal", 5140, "log message");
// UDP server
$sock = udp_listen(8125);
while (true) {
[$data, $addr] = udp_recv($sock, 512);
echo "From $addr: $data\n";
udp_reply($sock, "ACK", $addr); // phản hồi về địa chỉ gốc
}
udp_server_close($sock);
DNS
<gp>
$ips = dns_lookup("github.com");
// ["140.82.121.4", "140.82.121.3"]
$mxs = dns_lookup_mx("gmail.com");
// [{"host": "gmail-smtp-in.l.google.com.", "pref": 5}, ...]
foreach ($mxs as $mx) {
echo $mx["host"] . " (priority: " . $mx["pref"] . ")\n";
}
| Hàm | Mô tả |
|---|---|
tcp_connect($host, $port [, $timeout_ms]) | Kết nối TCP → conn resource hoặc false |
tcp_write($conn, $data) | Gửi data → số bytes đã ghi hoặc false |
tcp_read($conn [, $size]) | Đọc data → string hoặc false khi đóng |
tcp_close($conn) | Đóng TCP connection |
tcp_set_deadline($conn, $ms) | Đặt deadline cho đọc/ghi tiếp theo |
tcp_remote_addr($conn) | "host:port" của remote |
tcp_local_addr($conn) | "host:port" của local |
tcp_listen($port [, $host]) | Tạo TCP server → server resource |
tcp_accept($server) | Chờ và chấp nhận conn mới (blocking) |
tcp_serve($port, $fn [, $host]) | TCP server vòng lặp — gọi $fn($conn) trong goroutine |
tcp_server_close($server) | Đóng server |
tcp_server_addr($server) | "host:port" server đang lắng nghe |
udp_send($host, $port, $data) | Gửi UDP datagram |
udp_listen($port [, $host]) | Tạo UDP server → sock resource |
udp_recv($sock [, $size]) | Nhận datagram → [$data, $addr] |
udp_reply($sock, $data, $addr) | Gửi về địa chỉ đã nhận ($addr từ udp_recv) |
udp_server_close($sock) | Đóng UDP server |
dns_lookup($host) | Resolve hostname → array IP strings hoặc false |
dns_lookup_mx($domain) | Lấy MX records → array {host, pref} hoặc false |
36 Thư viện: File Watch
Theo dõi thay đổi file/thư mục theo thời gian thực — dùng để hot reload config, tự build, phát hiện upload.
<gp>
// Theo dõi 1 file
$w = file_watch("config.json", function($event) {
echo "File thay đổi: " . $event["path"] . " op: " . $event["op"] . "\n";
// op: "create" | "write" | "remove" | "rename" | "chmod"
});
// Theo dõi thư mục đệ quy
$w2 = file_watch("templates/", function($event) {
if ($event["op"] == "write") {
echo "Template " . $event["path"] . " đã cập nhật\n";
}
}, ["recursive" => true]);
// Thêm/bỏ path từ watcher đang chạy
file_watch_add($w2, "static/");
file_watch_remove($w2, "templates/old/");
// Dừng theo dõi
sleep(60);
file_watch_stop($w);
file_watch_stop($w2);
| Hàm | Mô tả |
|---|---|
file_watch($path, $fn [, $opts]) | Bắt đầu theo dõi. opts: ["recursive" => true] → watcher resource |
file_watch_stop($watcher) | Dừng và giải phóng watcher |
file_watch_add($watcher, $path) | Thêm path mới vào watcher đang chạy |
file_watch_remove($watcher, $path) | Bỏ path khỏi watcher |
Callback nhận map: {"path": "...", "op": "create|write|remove|rename|chmod"}
37 Thư viện: In-memory Cache
Cache nhanh trong bộ nhớ với TTL tự động. Không cần Redis — phù hợp cho single-process app.
<gp>
// Lưu với TTL 60 giây
cache_set("user:42", ["name" => "Alice", "role" => "admin"], 60);
// Lấy — null nếu hết hạn hoặc không có
$user = cache_get("user:42");
if ($user != null) {
echo "Cache hit: " . $user["name"];
} else {
// load từ DB và cache lại
$user = db_query_row("SELECT * FROM users WHERE id = 42");
cache_set("user:42", $user, 300); // cache 5 phút
}
// Xóa key
cache_delete("user:42");
// Xóa toàn bộ cache
cache_flush();
| Hàm | Mô tả |
|---|---|
cache_set($key, $value [, $ttl_sec]) | Lưu vào cache. TTL tính bằng giây, mặc định không hết hạn |
cache_get($key) | Lấy giá trị → null nếu không có hoặc hết hạn |
cache_delete($key) | Xóa key khỏi cache |
cache_flush() | Xóa toàn bộ cache |
38 Thư viện: Profiling
Đo thời gian thực thi từng function trong VM — giúp tìm bottleneck.
<gp>
prof_start(); // bật profiler
// ... chạy code cần đo ...
$data = db_query("SELECT * FROM products");
foreach ($data as $item) {
process_item($item);
}
prof_stop(); // tắt profiler
// In báo cáo: function name, call count, total time
echo prof_report();
| Hàm | Mô tả |
|---|---|
prof_start() | Bật profiling VM — theo dõi tất cả function calls |
prof_stop() | Tắt profiling |
prof_report() | Trả về string báo cáo: function, call count, thời gian tổng |
39 Thư viện: Error Chaining
Tạo và bọc lỗi có nguyên nhân (cause chain) — giống Go errors.Wrap hoặc PHP's previous exception.
<gp>
// Tạo lỗi gốc
$dbErr = error_create("Kết nối DB thất bại", 500);
// Bọc lỗi với context
$svcErr = error_wrap($dbErr, "Không thể nạp dữ liệu người dùng", 503);
$apiErr = error_wrap($svcErr, "Request thất bại");
// Kiểm tra lỗi
if (is_error($apiErr)) {
// Lấy toàn bộ chuỗi thông báo
echo error_chain_message($apiErr);
// → "Request thất bại: caused by: Không thể nạp dữ liệu người dùng: caused by: Kết nối DB thất bại"
// Lấy nguyên nhân gốc
$root = error_cause(error_cause($apiErr));
echo $root["message"]; // "Kết nối DB thất bại"
echo $root["code"]; // 500
}
| Hàm | Mô tả |
|---|---|
error_create($msg [, $code]) | Tạo error map: {error:true, message, code} |
error_wrap($cause, $msg [, $code]) | Bọc lỗi với context mới — lưu nguyên nhân gốc vào cause |
error_cause($err) | Lấy nguyên nhân trực tiếp của lỗi đã wrap |
error_chain_message($err) | Chuỗi thông báo toàn bộ chain: "msg1: caused by: msg2: ..." |
is_error($v) | Kiểm tra $v có phải error map không → bool |
40 Thư viện: Binary / Bytes
Xử lý dữ liệu nhị phân — binary protocol, mã hóa/giải mã, file binary, pack/unpack.
Bytes (KBytes type)
<gp>
// Tạo bytes
$b = bytes_from_string("Hello"); // string → KBytes
$b2 = bytes_from_hex("deadbeef"); // hex string → KBytes
// Thao tác
echo bytes_length($b); // 5
echo bytes_get($b, 0); // 72 (ASCII 'H')
bytes_set($b, 0, 104); // sửa byte đầu thành 'h'
$slice = bytes_slice($b, 1, 3); // bytes[1..3)
$joined = bytes_concat($b, $b2);
// Chuyển đổi
echo bytes_to_string($b); // "hello"
echo bytes_to_hex($b); // "68656c6c6f"
// File binary
$img = file_read_bytes("photo.jpg");
echo bytes_length($img); // kích thước file
file_write_bytes("copy.jpg", $img);
Pack / Unpack (PHP-compatible)
<gp>
// pack(format, ...values) → binary string
// Format codes: N=uint32 big-endian, n=uint16 big-endian, C=uint8, A=string
$header = pack("NnC", 1000, 42, 7);
// unpack — positional
$vals = unpack("NnC", $header);
echo $vals[0]; // 1000 (uint32)
echo $vals[1]; // 42 (uint16)
echo $vals[2]; // 7 (uint8)
// unpack — named (PHP style: "N1len/n1flags")
$data = unpack("N1length/n1flags/C1version", $header);
echo $data["length"]; // 1000
echo $data["flags"]; // 42
| Hàm | Mô tả |
|---|---|
bytes_from_string($s) | string → KBytes |
bytes_to_string($b) | KBytes → string |
bytes_length($b) | Số bytes |
bytes_get($b, $i) | Lấy byte tại index $i → int |
bytes_set($b, $i, $v) | Sửa byte tại index |
bytes_slice($b, $start, $end) | Cắt [start, end) |
bytes_concat($a, $b) | Nối hai KBytes |
bytes_to_hex($b) | KBytes → hex string |
bytes_from_hex($hex) | Hex string → KBytes |
file_read_bytes($path) | Đọc file → KBytes |
file_write_bytes($path, $b) | Ghi KBytes vào file |
pack($fmt, ...) | Đóng gói values thành binary string (PHP format) |
unpack($fmt, $data) | Giải gói binary → array hoặc map (named format) |