notwonderful / xenforo-sdk
PHP SDK for the XenForo REST API
1.0.1
2026-03-06 10:06 UTC
Requires
- php: ^8.3
- guzzlehttp/guzzle: ^7.0
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.0
README
PHP SDK for the XenForo REST API.
Installation
composer require notwonderful/xenforo-sdk
Usage
Client setup
use Notwonderful\XenforoSdk\XenForoClient; $xenforo = new XenForoClient( baseUrl: 'https://your-forum.com', apiKey: 'your-super-user-api-key', apiUser: 1, // optional: act as specific user (super user key required) );
Available APIs
| Method | API Class | Description |
|---|---|---|
$xenforo->alerts() |
Alerts | User alerts |
$xenforo->attachments() |
Attachments | File attachments |
$xenforo->auth() |
Auth | Login, session lookup, login tokens |
$xenforo->conversations() |
Conversations | Private conversations & messages |
$xenforo->featuredContent() |
FeaturedContent | Featured content |
$xenforo->forums() |
Forums | Forum info & thread listings |
$xenforo->index() |
Index | Site & API info |
$xenforo->me() |
Me | Current user profile |
$xenforo->nodes() |
Nodes | Node tree management |
$xenforo->oauth() |
OAuth | OAuth2 token management |
$xenforo->posts() |
Posts | Thread replies |
$xenforo->profilePosts() |
ProfilePosts | Profile posts & comments |
$xenforo->search() |
Search | Search operations |
$xenforo->searchForums() |
SearchForums | Search forum nodes |
$xenforo->stats() |
Stats | Site statistics |
$xenforo->threads() |
Threads | Thread CRUD & moderation |
$xenforo->users() |
Users | User CRUD & avatars |
Auth
All auth endpoints require a super user API key.
// Validate login credentials $user = $xenforo->auth()->login('username', 'password'); // Lookup user from session or remember cookie $user = $xenforo->auth()->fromSession(sessionId: 'xf_session_value'); $user = $xenforo->auth()->fromSession(rememberCookie: 'xf_user_cookie'); // Generate auto-login token $token = $xenforo->auth()->loginToken( userId: 1, remember: true, returnUrl: 'https://your-site.com/dashboard', ); // $token->loginUrl, $token->loginToken, $token->expiryDate
OAuth2
use Notwonderful\XenforoSdk\Enum\GrantType; use Notwonderful\XenforoSdk\Enum\TokenTypeHint; // Exchange authorization code for token $result = $xenforo->oauth()->token( grantType: GrantType::AuthorizationCode, clientId: 'your-client-id', clientSecret: 'your-client-secret', code: 'authorization-code', redirectUri: 'https://your-app.com/callback', ); // Refresh an expired token $result = $xenforo->oauth()->token( grantType: GrantType::RefreshToken, clientId: 'your-client-id', refreshToken: 'your-refresh-token', ); // Revoke a token $xenforo->oauth()->revokeToken('token-to-revoke'); $xenforo->oauth()->revokeToken('token', TokenTypeHint::AccessToken);
Users
// List users (paginated, alphabetically) $result = $xenforo->users()->list(page: 1); // Create a user $user = $xenforo->users()->create( username: 'johndoe', password: 's3cret', email: 'john@example.com', extra: ['custom_title' => 'New Member'], ); // Find users by email (admin only) or name prefix $result = $xenforo->users()->findByEmail('john@example.com'); $result = $xenforo->users()->findByName('joh'); // Get / Update / Delete a user $user = $xenforo->users()->find(42); $user = $xenforo->users()->update(42, ['custom_title' => 'Senior Member']); $xenforo->users()->destroy(42, renameTo: 'Former Member'); // Avatar management $xenforo->users()->uploadAvatar(42, '/path/to/avatar.png'); $xenforo->users()->deleteAvatar(42); // Profile posts $result = $xenforo->users()->profilePosts(42, page: 1);
Me (Current User)
// Get current user info $user = $xenforo->me()->find(); // Update profile $xenforo->me()->update(['custom_title' => 'VIP', 'profile[location]' => 'NYC']); // Change email / password $xenforo->me()->updateEmail('current-password', 'new@email.com'); $xenforo->me()->updatePassword('current-password', 'new-password'); // Avatar $xenforo->me()->uploadAvatar('/path/to/avatar.png'); $xenforo->me()->deleteAvatar();
Threads
// List threads with filters $result = $xenforo->threads()->list( page: 1, order: 'last_post_date', direction: 'desc', lastDays: 7, ); // Create a thread $thread = $xenforo->threads()->create( nodeId: 5, title: 'Hello World', message: 'This is my first thread!', extra: ['prefix_id' => 1, 'sticky' => true], ); // Get / Update / Delete $thread = $xenforo->threads()->find(42, withPosts: true, page: 1); $thread = $xenforo->threads()->update(42, ['title' => 'Updated Title', 'sticky' => false]); $xenforo->threads()->destroy(42, reason: 'spam'); // Move thread to another forum $xenforo->threads()->move(42, targetNodeId: 10, starterAlert: true); // Vote / Feature $xenforo->threads()->vote(42, 'up'); $xenforo->threads()->feature(42, ['title' => 'Custom feature title']); $xenforo->threads()->unfeature(42); // Get posts in thread $result = $xenforo->threads()->posts(42, page: 2);
Posts
// Reply to a thread $post = $xenforo->posts()->create(threadId: 42, message: 'My reply'); // Get / Update / Delete $post = $xenforo->posts()->find(100); $post = $xenforo->posts()->update(100, ['message' => 'Edited message', 'silent' => true]); $xenforo->posts()->destroy(100, hardDelete: true, reason: 'spam'); // React / Vote / Mark as solution $xenforo->posts()->react(100, 1); $xenforo->posts()->vote(100, 'up'); $xenforo->posts()->markSolution(100);
Forums
// Get forum info $forum = $xenforo->forums()->find(5); // $forum->nodeId, $forum->title, $forum->discussionCount, $forum->messageCount // Get forum (threads are available via $forum->raw['threads'] when requested) $forum = $xenforo->forums()->find(5, withThreads: true, page: 1, order: 'post_date'); // Get threads from forum $result = $xenforo->forums()->threads(5, page: 1, unread: true); // Mark forum as read $xenforo->forums()->markRead(5);
Nodes
// Get node tree / flattened $result = $xenforo->nodes()->list(); $result = $xenforo->nodes()->flattened(); // CRUD $node = $xenforo->nodes()->create(['node[title]' => 'New Forum', 'node[parent_node_id]' => 0, 'node_type_id' => 'Forum']); $node = $xenforo->nodes()->find(5); $node = $xenforo->nodes()->update(5, ['node[title]' => 'Renamed']); $xenforo->nodes()->destroy(5, deleteChildren: true);
Conversations
// List conversations $result = $xenforo->conversations()->list(page: 1, unread: true); // Create a conversation $convo = $xenforo->conversations()->create( recipientIds: [2, 3], title: 'Hello!', message: 'How are you?', ); // Get / Update $convo = $xenforo->conversations()->find(10, withMessages: true, page: 1); $convo = $xenforo->conversations()->update(10, title: 'New Title'); // Reply to conversation $msg = $xenforo->conversations()->reply(conversationId: 10, message: 'Thanks!'); // Management $xenforo->conversations()->invite(10, [4, 5]); $xenforo->conversations()->star(10, star: true); $xenforo->conversations()->markRead(10); $xenforo->conversations()->markUnread(10); $xenforo->conversations()->labels(10, ['important', 'work']); $xenforo->conversations()->destroy(10, ignore: true); // Message operations $msg = $xenforo->conversations()->findMessage(50); $msg = $xenforo->conversations()->updateMessage(50, message: 'Edited'); $xenforo->conversations()->reactToMessage(50, 1);
Alerts
// List alerts $result = $xenforo->alerts()->list(page: 1, unread: true); // Get alert $alert = $xenforo->alerts()->find(42); // $alert->contentType, $alert->action, $alert->alertText, $alert->viewUrl // Send alert (super user key only) $xenforo->alerts()->send(toUserId: 5, alert: 'You have a new reward!', linkUrl: '/rewards'); // Mark alerts $xenforo->alerts()->markAll(read: true); $xenforo->alerts()->mark(42, read: true);
Attachments
// Create attachment key and upload $result = $xenforo->attachments()->newKey('post', ['thread_id' => '42']); $key = $result['key']; $result = $xenforo->attachments()->upload($key, '/path/to/file.png'); // Get / List / Delete $attachment = $xenforo->attachments()->find(10); $result = $xenforo->attachments()->list($key); $xenforo->attachments()->destroy(10);
Profile Posts
// Create profile post $post = $xenforo->profilePosts()->create(userId: 5, message: 'Hello!'); // Get / Update $post = $xenforo->profilePosts()->find(10, withComments: true, direction: 'asc'); $post = $xenforo->profilePosts()->update(10, ['message' => 'Edited']); // Comments $result = $xenforo->profilePosts()->comments(10, page: 1); $comment = $xenforo->profilePosts()->createComment(profilePostId: 10, message: 'Nice post!'); $comment = $xenforo->profilePosts()->findComment(5); $comment = $xenforo->profilePosts()->updateComment(5, ['message' => 'Edited']); // React $xenforo->profilePosts()->react(10, 1); $xenforo->profilePosts()->reactToComment(5, 1); // Delete $xenforo->profilePosts()->destroy(10, hardDelete: true); $xenforo->profilePosts()->destroyComment(5);
Featured Content
$result = $xenforo->featuredContent()->list(page: 1, contentType: 'thread', userId: 5);
Search
// Create a search $result = $xenforo->search()->create(['keywords' => 'hello world', 'search_type' => 'post']); // Search a specific member's content $result = $xenforo->search()->member(['user_id' => 42]); // Get / Load older results $result = $xenforo->search()->find(searchId: 15); $result = $xenforo->search()->older(searchId: 15);
Search Forums
$result = $xenforo->searchForums()->find(5, withThreads: true); $result = $xenforo->searchForums()->threads(5, page: 2);
Index
$result = $xenforo->index()->info(); // $result['version_id'], $result['site_title'], $result['base_url'], $result['api_url']
Stats
$result = $xenforo->stats()->all();
Error handling
use Notwonderful\XenforoSdk\Exception\ApiException; try { $xenforo->auth()->login('admin', 'wrong-password'); } catch (ApiException $e) { $e->getMessage(); // "Incorrect password." $e->statusCode; // 400 $e->errors; // [{code, message, params}, ...] }
Custom HTTP client
You can inject your own Guzzle instance for proxies, timeouts, logging, etc.:
use GuzzleHttp\Client; $http = new Client([ 'base_uri' => 'https://your-forum.com/api/', 'headers' => ['XF-Api-Key' => 'your-key'], 'timeout' => 10, ]); $xenforo = new XenForoClient('https://your-forum.com', 'your-key', http: $http);
Testing
composer test