appwrite-php
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAppwrite PHP SDK
Appwrite PHP SDK
Installation
安装
bash
composer require appwrite/appwritebash
composer require appwrite/appwriteSetting Up the Client
客户端设置
php
use Appwrite\Client;
use Appwrite\ID;
use Appwrite\Query;
use Appwrite\Services\Users;
use Appwrite\Services\TablesDB;
use Appwrite\Services\Storage;
use Appwrite\Services\Functions;
use Appwrite\InputFile;
$client = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject(getenv('APPWRITE_PROJECT_ID'))
->setKey(getenv('APPWRITE_API_KEY'));php
use Appwrite\Client;
use Appwrite\ID;
use Appwrite\Query;
use Appwrite\Services\Users;
use Appwrite\Services\TablesDB;
use Appwrite\Services\Storage;
use Appwrite\Services\Functions;
use Appwrite\InputFile;
$client = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject(getenv('APPWRITE_PROJECT_ID'))
->setKey(getenv('APPWRITE_API_KEY'));Code Examples
代码示例
User Management
用户管理
php
$users = new Users($client);
// Create user
$user = $users->create(ID::unique(), 'user@example.com', null, 'password123', 'User Name');
// List users
$list = $users->list([Query::limit(25)]);
// Get user
$fetched = $users->get('[USER_ID]');
// Delete user
$users->delete('[USER_ID]');php
$users = new Users($client);
// Create user
$user = $users->create(ID::unique(), 'user@example.com', null, 'password123', 'User Name');
// List users
$list = $users->list([Query::limit(25)]);
// Get user
$fetched = $users->get('[USER_ID]');
// Delete user
$users->delete('[USER_ID]');Database Operations
数据库操作
Note: Use(not the deprecatedTablesDBclass) for all new code. Only useDatabasesif the existing codebase already relies on it or the user explicitly requests it.DatabasesTip: Prefer named arguments (PHP 8+, e.g.,) for all SDK method calls. Only use positional arguments if the existing codebase already uses them or the user explicitly requests it.databaseId: '...'
php
$tablesDB = new TablesDB($client);
// Create database
$db = $tablesDB->create(ID::unique(), 'My Database');
// Create row
$doc = $tablesDB->createRow('[DATABASE_ID]', '[TABLE_ID]', ID::unique(), [
'title' => 'Hello World'
]);
// Query rows
$results = $tablesDB->listRows('[DATABASE_ID]', '[TABLE_ID]', [
Query::equal('title', ['Hello World']),
Query::limit(10)
]);
// Get row
$row = $tablesDB->getRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]');
// Update row
$tablesDB->updateRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]', [
'title' => 'Updated'
]);
// Delete row
$tablesDB->deleteRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]');注意: 所有新代码请使用(而非已弃用的TablesDB类)。仅当现有代码库已依赖Databases或用户明确要求时,才使用该类。Databases提示: 所有SDK方法调用优先使用命名参数(PHP 8+,例如)。仅当现有代码库已使用位置参数或用户明确要求时,才使用位置参数。databaseId: '...'
php
$tablesDB = new TablesDB($client);
// Create database
$db = $tablesDB->create(ID::unique(), 'My Database');
// Create row
$doc = $tablesDB->createRow('[DATABASE_ID]', '[TABLE_ID]', ID::unique(), [
'title' => 'Hello World'
]);
// Query rows
$results = $tablesDB->listRows('[DATABASE_ID]', '[TABLE_ID]', [
Query::equal('title', ['Hello World']),
Query::limit(10)
]);
// Get row
$row = $tablesDB->getRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]');
// Update row
$tablesDB->updateRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]', [
'title' => 'Updated'
]);
// Delete row
$tablesDB->deleteRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]');String Column Types
字符串列类型
Note: The legacytype is deprecated. Use explicit column types for all new columns.string
| Type | Max characters | Indexing | Storage |
|---|---|---|---|
| 16,383 | Full index (if size ≤ 768) | Inline in row |
| 16,383 | Prefix only | Off-page |
| 4,194,303 | Prefix only | Off-page |
| 1,073,741,823 | Prefix only | Off-page |
- is stored inline and counts towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.
varchar - ,
text, andmediumtextare stored off-page (only a 20-byte pointer lives in the row), so they don't consume the row size budget.longtextis not required for these types.size
php
// Create table with explicit string column types
$tablesDB->createTable('[DATABASE_ID]', ID::unique(), 'articles', [
['key' => 'title', 'type' => 'varchar', 'size' => 255, 'required' => true],
['key' => 'summary', 'type' => 'text', 'required' => false],
['key' => 'body', 'type' => 'mediumtext', 'required' => false],
['key' => 'raw_data', 'type' => 'longtext', 'required' => false],
]);注意: 旧版类型已被弃用。所有新列请使用明确的列类型。string
| 类型 | 最大字符数 | 索引方式 | 存储方式 |
|---|---|---|---|
| 16,383 | 全索引(若长度≤768) | 行内存储 |
| 16,383 | 仅前缀索引 | 页外存储 |
| 4,194,303 | 仅前缀索引 | 页外存储 |
| 1,073,741,823 | 仅前缀索引 | 页外存储 |
- 存储在行内,占用64KB行大小限制的配额。适合短的、需要索引的字段,如名称、别名或标识符。
varchar - 、
text和mediumtext存储在页外(行中仅保存20字节的指针),因此不会消耗行大小配额。这些类型无需指定longtext。size
php
// Create table with explicit string column types
$tablesDB->createTable('[DATABASE_ID]', ID::unique(), 'articles', [
['key' => 'title', 'type' => 'varchar', 'size' => 255, 'required' => true],
['key' => 'summary', 'type' => 'text', 'required' => false],
['key' => 'body', 'type' => 'mediumtext', 'required' => false],
['key' => 'raw_data', 'type' => 'longtext', 'required' => false],
]);Query Methods
查询方法
php
// Filtering
Query::equal('field', ['value']) // == (always pass array)
Query::notEqual('field', ['value']) // !=
Query::lessThan('field', 100) // <
Query::lessThanEqual('field', 100) // <=
Query::greaterThan('field', 100) // >
Query::greaterThanEqual('field', 100) // >=
Query::between('field', 1, 100) // 1 <= field <= 100
Query::isNull('field') // is null
Query::isNotNull('field') // is not null
Query::startsWith('field', 'prefix') // starts with
Query::endsWith('field', 'suffix') // ends with
Query::contains('field', ['sub']) // contains (string or array)
Query::search('field', 'keywords') // full-text search (requires index)
// Sorting
Query::orderAsc('field')
Query::orderDesc('field')
// Pagination
Query::limit(25) // max rows (default 25, max 100)
Query::offset(0) // skip N rows
Query::cursorAfter('[ROW_ID]') // cursor pagination (preferred)
Query::cursorBefore('[ROW_ID]')
// Selection & Logic
Query::select(['field1', 'field2']) // return only specified fields
Query::or([Query::equal('a', [1]), Query::equal('b', [2])]) // OR
Query::and([Query::greaterThan('age', 18), Query::lessThan('age', 65)]) // AND (default)php
// Filtering
Query::equal('field', ['value']) // == (always pass array)
Query::notEqual('field', ['value']) // !=
Query::lessThan('field', 100) // <
Query::lessThanEqual('field', 100) // <=
Query::greaterThan('field', 100) // >
Query::greaterThanEqual('field', 100) // >=
Query::between('field', 1, 100) // 1 <= field <= 100
Query::isNull('field') // is null
Query::isNotNull('field') // is not null
Query::startsWith('field', 'prefix') // starts with
Query::endsWith('field', 'suffix') // ends with
Query::contains('field', ['sub']) // contains (string or array)
Query::search('field', 'keywords') // full-text search (requires index)
// Sorting
Query::orderAsc('field')
Query::orderDesc('field')
// Pagination
Query::limit(25) // max rows (default 25, max 100)
Query::offset(0) // skip N rows
Query::cursorAfter('[ROW_ID]') // cursor pagination (preferred)
Query::cursorBefore('[ROW_ID]')
// Selection & Logic
Query::select(['field1', 'field2']) // return only specified fields
Query::or([Query::equal('a', [1]), Query::equal('b', [2])]) // OR
Query::and([Query::greaterThan('age', 18), Query::lessThan('age', 65)]) // AND (default)File Storage
文件存储
php
$storage = new Storage($client);
// Upload file
$file = $storage->createFile('[BUCKET_ID]', ID::unique(), InputFile::withPath('/path/to/file.png'));
// List files
$files = $storage->listFiles('[BUCKET_ID]');
// Delete file
$storage->deleteFile('[BUCKET_ID]', '[FILE_ID]');php
$storage = new Storage($client);
// Upload file
$file = $storage->createFile('[BUCKET_ID]', ID::unique(), InputFile::withPath('/path/to/file.png'));
// List files
$files = $storage->listFiles('[BUCKET_ID]');
// Delete file
$storage->deleteFile('[BUCKET_ID]', '[FILE_ID]');InputFile Factory Methods
InputFile 工厂方法
php
use Appwrite\InputFile;
InputFile::withPath('/path/to/file.png') // from filesystem path
InputFile::withData('Hello world', 'hello.txt') // from string contentphp
use Appwrite\InputFile;
InputFile::withPath('/path/to/file.png') // from filesystem path
InputFile::withData('Hello world', 'hello.txt') // from string contentTeams
团队管理
php
$teams = new Teams($client);
// Create team
$team = $teams->create(ID::unique(), 'Engineering');
// List teams
$list = $teams->list();
// Create membership (invite user by email)
$membership = $teams->createMembership('[TEAM_ID]', ['editor'], email: 'user@example.com');
// List memberships
$members = $teams->listMemberships('[TEAM_ID]');
// Update membership roles
$teams->updateMembership('[TEAM_ID]', '[MEMBERSHIP_ID]', ['admin']);
// Delete team
$teams->delete('[TEAM_ID]');Role-based access: Usefor all team members orRole::team('[TEAM_ID]')for a specific team role when setting permissions.Role::team('[TEAM_ID]', 'editor')
php
$teams = new Teams($client);
// Create team
$team = $teams->create(ID::unique(), 'Engineering');
// List teams
$list = $teams->list();
// Create membership (invite user by email)
$membership = $teams->createMembership('[TEAM_ID]', ['editor'], email: 'user@example.com');
// List memberships
$members = $teams->listMemberships('[TEAM_ID]');
// Update membership roles
$teams->updateMembership('[TEAM_ID]', '[MEMBERSHIP_ID]', ['admin']);
// Delete team
$teams->delete('[TEAM_ID]');基于角色的访问控制: 设置权限时,使用表示所有团队成员,或使用Role::team('[TEAM_ID]')表示特定团队角色。Role::team('[TEAM_ID]', 'editor')
Serverless Functions
无服务器函数
php
$functions = new Functions($client);
// Execute function
$execution = $functions->createExecution('[FUNCTION_ID]', '{"key": "value"}');
// List executions
$executions = $functions->listExecutions('[FUNCTION_ID]');php
$functions = new Functions($client);
// Execute function
$execution = $functions->createExecution('[FUNCTION_ID]', '{"key": "value"}');
// List executions
$executions = $functions->listExecutions('[FUNCTION_ID]');Writing a Function Handler (PHP runtime)
编写函数处理器(PHP 运行时)
php
// src/main.php — Appwrite Function entry point
return function ($context) {
// $context->req->body — raw body (string)
// $context->req->bodyJson — parsed JSON (array or null)
// $context->req->headers — headers (array)
// $context->req->method — HTTP method
// $context->req->path — URL path
// $context->req->query — query params (array)
$context->log('Processing: ' . $context->req->method . ' ' . $context->req->path);
if ($context->req->method === 'GET') {
return $context->res->json(['message' => 'Hello from Appwrite Function!']);
}
$data = $context->req->bodyJson ?? [];
if (!isset($data['name'])) {
$context->error('Missing name field');
return $context->res->json(['error' => 'Name is required'], 400);
}
return $context->res->json(['success' => true]); // JSON
// return $context->res->text('Hello'); // plain text
// return $context->res->empty(); // 204
// return $context->res->redirect('https://...'); // 302
};php
// src/main.php — Appwrite Function entry point
return function ($context) {
// $context->req->body — raw body (string)
// $context->req->bodyJson — parsed JSON (array or null)
// $context->req->headers — headers (array)
// $context->req->method — HTTP method
// $context->req->path — URL path
// $context->req->query — query params (array)
$context->log('Processing: ' . $context->req->method . ' ' . $context->req->path);
if ($context->req->method === 'GET') {
return $context->res->json(['message' => 'Hello from Appwrite Function!']);
}
$data = $context->req->bodyJson ?? [];
if (!isset($data['name'])) {
$context->error('Missing name field');
return $context->res->json(['error' => 'Name is required'], 400);
}
return $context->res->json(['success' => true]); // JSON
// return $context->res->text('Hello'); // plain text
// return $context->res->empty(); // 204
// return $context->res->redirect('https://...'); // 302
};Server-Side Rendering (SSR) Authentication
服务端渲染(SSR)认证
SSR apps (Laravel, Symfony, etc.) use the server SDK to handle auth. You need two clients:
- Admin client — uses an API key, creates sessions, bypasses rate limits (reusable singleton)
- Session client — uses a session cookie, acts on behalf of a user (create per-request, never share)
php
use Appwrite\Client;
use Appwrite\Services\Account;
// Admin client (reusable)
$adminClient = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject('[PROJECT_ID]')
->setKey(getenv('APPWRITE_API_KEY'));
// Session client (create per-request)
$sessionClient = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject('[PROJECT_ID]');
$session = $_COOKIE['a_session_[PROJECT_ID]'] ?? null;
if ($session) {
$sessionClient->setSession($session);
}SSR应用(如Laravel、Symfony等)使用服务端SDK处理认证。你需要两个客户端:
- 管理客户端 — 使用API密钥,可创建会话,绕过速率限制(可复用单例)
- 会话客户端 — 使用会话Cookie,代表用户执行操作(每个请求创建一个,请勿共享)
php
use Appwrite\Client;
use Appwrite\Services\Account;
// Admin client (reusable)
$adminClient = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject('[PROJECT_ID]')
->setKey(getenv('APPWRITE_API_KEY'));
// Session client (create per-request)
$sessionClient = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject('[PROJECT_ID]');
$session = $_COOKIE['a_session_[PROJECT_ID]'] ?? null;
if ($session) {
$sessionClient->setSession($session);
}Email/Password Login
邮箱/密码登录
php
$account = new Account($adminClient);
$session = $account->createEmailPasswordSession($email, $password);
// Cookie name must be a_session_<PROJECT_ID>
setcookie('a_session_[PROJECT_ID]', $session['secret'], [
'httpOnly' => true,
'secure' => true,
'sameSite' => 'strict',
'expires' => strtotime($session['expire']),
'path' => '/',
]);php
$account = new Account($adminClient);
$session = $account->createEmailPasswordSession($email, $password);
// Cookie name must be a_session_<PROJECT_ID>
setcookie('a_session_[PROJECT_ID]', $session['secret'], [
'httpOnly' => true,
'secure' => true,
'sameSite' => 'strict',
'expires' => strtotime($session['expire']),
'path' => '/',
]);Authenticated Requests
已认证请求
php
$session = $_COOKIE['a_session_[PROJECT_ID]'] ?? null;
if (!$session) {
http_response_code(401);
exit;
}
$sessionClient->setSession($session);
$account = new Account($sessionClient);
$user = $account->get();php
$session = $_COOKIE['a_session_[PROJECT_ID]'] ?? null;
if (!$session) {
http_response_code(401);
exit;
}
$sessionClient->setSession($session);
$account = new Account($sessionClient);
$user = $account->get();OAuth2 SSR Flow
OAuth2 SSR 流程
php
// Step 1: Redirect to OAuth provider
$account = new Account($adminClient);
$redirectUrl = $account->createOAuth2Token(
OAuthProvider::GITHUB(),
'https://example.com/oauth/success',
'https://example.com/oauth/failure',
);
header('Location: ' . $redirectUrl);
// Step 2: Handle callback — exchange token for session
$account = new Account($adminClient);
$session = $account->createSession($_GET['userId'], $_GET['secret']);
setcookie('a_session_[PROJECT_ID]', $session['secret'], [
'httpOnly' => true, 'secure' => true, 'sameSite' => 'strict',
'expires' => strtotime($session['expire']), 'path' => '/',
]);Cookie security: Always use,httpOnly, andsecureto prevent XSS. The cookie name must besameSite: 'strict'.a_session_<PROJECT_ID>
Forwarding user agent: Callto record the end-user's browser info for debugging and security.$sessionClient->setForwardedUserAgent($_SERVER['HTTP_USER_AGENT'])
php
// Step 1: Redirect to OAuth provider
$account = new Account($adminClient);
$redirectUrl = $account->createOAuth2Token(
OAuthProvider::GITHUB(),
'https://example.com/oauth/success',
'https://example.com/oauth/failure',
);
header('Location: ' . $redirectUrl);
// Step 2: Handle callback — exchange token for session
$account = new Account($adminClient);
$session = $account->createSession($_GET['userId'], $_GET['secret']);
setcookie('a_session_[PROJECT_ID]', $session['secret'], [
'httpOnly' => true, 'secure' => true, 'sameSite' => 'strict',
'expires' => strtotime($session['expire']), 'path' => '/',
]);Cookie 安全: 始终使用、httpOnly和secure以防止XSS攻击。Cookie名称必须为sameSite: 'strict'。a_session_<PROJECT_ID>
转发用户代理: 调用以记录终端用户的浏览器信息,用于调试和安全审计。$sessionClient->setForwardedUserAgent($_SERVER['HTTP_USER_AGENT'])
Error Handling
错误处理
php
use Appwrite\AppwriteException;
try {
$row = $tablesDB->getRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]');
} catch (AppwriteException $e) {
echo $e->getMessage(); // human-readable error message
echo $e->getCode(); // HTTP status code (int)
echo $e->getType(); // Appwrite error type string (e.g. 'document_not_found')
echo $e->getResponse(); // full response body (array)
}Common error codes:
| Code | Meaning |
|---|---|
| Unauthorized — missing or invalid session/API key |
| Forbidden — insufficient permissions for this action |
| Not found — resource does not exist |
| Conflict — duplicate ID or unique constraint violation |
| Rate limited — too many requests, retry after backoff |
php
use Appwrite\AppwriteException;
try {
$row = $tablesDB->getRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]');
} catch (AppwriteException $e) {
echo $e->getMessage(); // human-readable error message
echo $e->getCode(); // HTTP status code (int)
echo $e->getType(); // Appwrite error type string (e.g. 'document_not_found')
echo $e->getResponse(); // full response body (array)
}常见错误状态码:
| 状态码 | 说明 |
|---|---|
| 未授权 — 缺少或无效的会话/API密钥 |
| 禁止访问 — 执行该操作的权限不足 |
| 未找到 — 资源不存在 |
| 冲突 — 重复ID或唯一约束违反 |
| 请求受限 — 请求次数过多,请在退避后重试 |
Permissions & Roles (Critical)
权限与角色(重要)
Appwrite uses permission strings to control access to resources. Each permission pairs an action (, , , , or which grants create + update + delete) with a role target. By default, no user has access unless permissions are explicitly set at the document/file level or inherited from the collection/bucket settings. Permissions are arrays of strings built with the and helpers.
readupdatedeletecreatewritePermissionRolephp
use Appwrite\Permission;
use Appwrite\Role;Appwrite使用权限字符串控制资源访问。每个权限将一个操作(、、、,或代表create+update+delete的)与一个角色目标配对。默认情况下,所有用户都无访问权限,除非在文档/文件级别明确设置权限,或从集合/存储桶设置继承权限。权限是使用和助手构建的字符串数组。
readupdatedeletecreatewritePermissionRolephp
use Appwrite\Permission;
use Appwrite\Role;Database Row with Permissions
带权限的数据库行
php
$doc = $tablesDB->createRow('[DATABASE_ID]', '[TABLE_ID]', ID::unique(), [
'title' => 'Hello World'
], [
Permission::read(Role::user('[USER_ID]')), // specific user can read
Permission::update(Role::user('[USER_ID]')), // specific user can update
Permission::read(Role::team('[TEAM_ID]')), // all team members can read
Permission::read(Role::any()), // anyone (including guests) can read
]);php
$doc = $tablesDB->createRow('[DATABASE_ID]', '[TABLE_ID]', ID::unique(), [
'title' => 'Hello World'
], [
Permission::read(Role::user('[USER_ID]')), // specific user can read
Permission::update(Role::user('[USER_ID]')), // specific user can update
Permission::read(Role::team('[TEAM_ID]')), // all team members can read
Permission::read(Role::any()), // anyone (including guests) can read
]);File Upload with Permissions
带权限的文件上传
php
$file = $storage->createFile('[BUCKET_ID]', ID::unique(), InputFile::withPath('/path/to/file.png'), [
Permission::read(Role::any()),
Permission::update(Role::user('[USER_ID]')),
Permission::delete(Role::user('[USER_ID]')),
]);When to set permissions: Set document/file-level permissions when you need per-resource access control. If all documents in a collection share the same rules, configure permissions at the collection/bucket level and leave document permissions empty.
Common mistakes:
- Forgetting permissions — the resource becomes inaccessible to all users (including the creator)
withRole::any()/write/update— allows any user, including unauthenticated guests, to modify or remove the resourcedelete on sensitive data — makes the resource publicly readablePermission::read(Role::any())
php
$file = $storage->createFile('[BUCKET_ID]', ID::unique(), InputFile::withPath('/path/to/file.png'), [
Permission::read(Role::any()),
Permission::update(Role::user('[USER_ID]')),
Permission::delete(Role::user('[USER_ID]')),
]);何时设置权限: 当需要按资源进行访问控制时,设置文档/文件级权限。如果集合中的所有文档共享相同规则,请在集合/存储桶级别配置权限,并留空文档权限。
常见错误:
- 忘记设置权限 — 资源对所有用户(包括创建者)都不可访问
- 对
/write/update使用delete— 允许任何用户(包括未认证访客)修改或删除资源Role::any()- 对敏感数据使用
— 使资源可公开读取Permission::read(Role::any())