notwonderful/xenforo-sdk

PHP SDK for the XenForo REST API

Maintainers

Package info

github.com/notwonderful/xenforo-sdk

pkg:composer/notwonderful/xenforo-sdk

Statistics

Installs: 36

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.1 2026-03-06 10:06 UTC

This package is auto-updated.

Last update: 2026-03-06 11:07:32 UTC


README

PHP 8.3+ Downloads License

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