jjuanrivvera / canvas-lms-kit
The most comprehensive PHP SDK for Canvas LMS API. Production-ready with +35 APIs implemented, rate limiting, and full test coverage.
Fund package maintenance!
jjuanrivvera99
Requires
- php: ^8.1
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- guzzlehttp/guzzle: ^7.3
- psr/log: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^3.6
This package is auto-updated.
Last update: 2025-08-20 02:45:47 UTC
README

Canvas LMS Kit
The most comprehensive PHP SDK for Canvas LMS API. Production-ready with 37 APIs implemented.
โจ Why Canvas LMS Kit?
- ๐ Production Ready: Rate limiting, middleware support, battle-tested
- ๐ Comprehensive: 37 Canvas APIs fully implemented
- ๐ก๏ธ Type Safe: Full PHP 8.1+ type declarations and PHPStan level 6
- ๐ง Developer Friendly: Intuitive Active Record pattern - just pass arrays!
- ๐ Well Documented: Extensive examples, guides, and API reference
- โก Performance: Built-in pagination, caching support, and optimized queries
๐ฏ Quick Start
use CanvasLMS\Config; use CanvasLMS\Api\Courses\Course; Config::setApiKey('your-api-key'); Config::setBaseUrl('https://canvas.instructure.com'); // It's that simple! $courses = Course::get(); // Get first page foreach ($courses as $course) { echo $course->name . "\n"; }
๐ Table of Contents
- Requirements
- Installation
- Configuration
- Usage Examples
- Supported APIs
- Advanced Features
- Testing
- Contributing
- Support
๐ Requirements
- PHP 8.1 or higher
- Composer
- Canvas LMS API token
- Extensions:
json
,curl
,mbstring
๐ฆ Installation
composer require jjuanrivvera/canvas-lms-kit
โ๏ธ Configuration
API Key Authentication (Simple)
use CanvasLMS\Config; // Basic configuration Config::setApiKey('your-api-key'); Config::setBaseUrl('https://canvas.instructure.com'); // Optional: Set account ID for scoped operations Config::setAccountId(1); // Optional: Configure middleware Config::setMiddleware([ 'retry' => ['max_attempts' => 3], 'rate_limit' => ['wait_on_limit' => true], ]);
OAuth 2.0 Authentication (User-Based) ๐
Canvas LMS Kit now supports OAuth 2.0 for user-specific authentication with automatic token refresh!
use CanvasLMS\Config; use CanvasLMS\Auth\OAuth; // Configure OAuth credentials Config::setOAuthClientId('your-client-id'); Config::setOAuthClientSecret('your-client-secret'); Config::setOAuthRedirectUri('https://yourapp.com/oauth/callback'); Config::setBaseUrl('https://canvas.instructure.com'); // Step 1: Get authorization URL $authUrl = OAuth::getAuthorizationUrl(['state' => 'random-state']); // Redirect user to $authUrl // Step 2: Handle callback $tokenData = OAuth::exchangeCode($_GET['code']); // Step 3: Use OAuth mode Config::useOAuth(); // Now all API calls use OAuth with automatic token refresh! $courses = Course::get(); // User's courses (first page)
Environment Variable Configuration
Perfect for containerized deployments and 12-factor apps:
# .env file CANVAS_BASE_URL=https://canvas.instructure.com CANVAS_API_KEY=your-api-key # OR for OAuth: CANVAS_OAUTH_CLIENT_ID=your-client-id CANVAS_OAUTH_CLIENT_SECRET=your-client-secret CANVAS_AUTH_MODE=oauth
// Auto-detect from environment Config::autoDetectFromEnvironment(); // Ready to use!
Logging Configuration
The SDK supports any PSR-3 compatible logger for comprehensive debugging and monitoring:
use CanvasLMS\Config; use Monolog\Logger; use Monolog\Handler\StreamHandler; // Configure with Monolog $logger = new Logger('canvas-lms'); $logger->pushHandler(new StreamHandler('logs/canvas.log', Logger::INFO)); Config::setLogger($logger); // All API calls, OAuth operations, pagination, and file uploads are now logged!
Symfony Integration
use Psr\Log\LoggerInterface; public function __construct(LoggerInterface $logger) { Config::setLogger($logger); }
Advanced Logging Configuration
// Enable detailed request/response logging Config::setMiddleware([ 'logging' => [ 'enabled' => true, 'log_requests' => true, 'log_responses' => true, 'log_errors' => true, 'log_timing' => true, 'log_level' => \Psr\Log\LogLevel::DEBUG, 'sanitize_fields' => ['password', 'token', 'api_key', 'secret'], 'max_body_length' => 1000 ] ]);
What Gets Logged:
- โ All API requests and responses (with sensitive data sanitization)
- โ OAuth token operations (refresh, revoke, exchange)
- โ Pagination operations with performance metrics
- โ File upload progress (3-step process)
- โ Rate limit headers and API costs
- โ Error conditions with context
Security: The logging middleware automatically sanitizes sensitive fields like passwords, tokens, and API keys to prevent accidental exposure in logs.
๐ก Usage Examples
Working with Courses
use CanvasLMS\Api\Courses\Course; // Get first page of courses (memory efficient) $courses = Course::get(); // Get ALL courses across all pages automatically // โ ๏ธ Warning: Be cautious with large datasets (1000+ items) $allCourses = Course::all(); // Get paginated results with metadata (recommended for large datasets) $paginated = Course::paginate(['per_page' => 50]); echo "Total courses: " . $paginated->getTotalCount(); // Find a specific course $course = Course::find(123); // Create a new course - just pass an array! $course = Course::create([ 'name' => 'Introduction to PHP', 'course_code' => 'PHP101', 'start_at' => '2025-02-01T00:00:00Z' ]); // Update a course $course->update([ 'name' => 'Advanced PHP Programming' ]); // Save changes with fluent interface support $course->name = 'Updated Course Name'; $course->save()->enrollments(); // Save and immediately get enrollments // Delete a course (also returns self for chaining) $course->delete();
Managing Assignments
use CanvasLMS\Api\Assignments\Assignment; use CanvasLMS\Api\Submissions\Submission; use CanvasLMS\Api\Courses\Course; // Set course context for Assignment $course = Course::find(123); Assignment::setCourse($course); // Create an assignment - simple array syntax $assignment = Assignment::create([ 'name' => 'Final Project', 'description' => 'Build a web application', 'points_possible' => 100, 'due_at' => '2025-03-15T23:59:59Z', 'submission_types' => ['online_upload', 'online_url'] ]); // Grade submissions (requires both Course and Assignment context) Submission::setCourse($course); Submission::setAssignment($assignment); $submission = Submission::update($studentId, [ 'posted_grade' => 95, 'comment' => 'Excellent work!' ]);
Working with Modules and Module Items
use CanvasLMS\Api\Modules\Module; use CanvasLMS\Api\Modules\ModuleItem; // Get course and set context $course = Course::find(123); Module::setCourse($course); // Create a module $module = Module::create([ 'name' => 'Week 1: Introduction', 'position' => 1, 'published' => true ]); // Add items to the module (requires both course and module context) ModuleItem::setCourse($course); ModuleItem::setModule($module); // Add an assignment to the module $item = ModuleItem::create([ 'title' => 'Week 1 Assignment', 'type' => 'Assignment', 'content_id' => $assignment->id, 'position' => 1, 'completion_requirement' => [ 'type' => 'must_submit' ] ]); // Or use the course instance method (recommended) $modules = $course->modules();
Working with Current User
use CanvasLMS\Api\Users\User; // Get current authenticated user instance $currentUser = User::self(); // Canvas supports 'self' for these endpoints: $profile = $currentUser->getProfile(); $activityStream = $currentUser->getActivityStream(); $todos = $currentUser->getTodoItems(); $groups = $currentUser->groups(); // Other methods require explicit user ID $user = User::find(123); $enrollments = $user->enrollments(); $courses = $user->courses();
Managing Groups
use CanvasLMS\Api\Groups\Group; use CanvasLMS\Api\Groups\GroupMembership; // Create a group in your account $group = Group::create([ 'name' => 'Study Group Alpha', 'description' => 'Weekly study sessions', 'is_public' => false, 'join_level' => 'invitation_only' ]); // Add members to the group $membership = $group->createMembership([ 'user_id' => 456, 'workflow_state' => 'accepted' ]); // Get group activity stream $activities = $group->activityStream(); // Invite users by email $group->invite(['student1@example.com', 'student2@example.com']);
File Uploads
use CanvasLMS\Api\Files\File; // Upload a file to a course $file = File::upload([ 'course_id' => 123, 'file_path' => '/path/to/document.pdf', 'name' => 'Course Syllabus.pdf', 'parent_folder_path' => 'course_documents' ]);
Feature Flags
use CanvasLMS\Api\FeatureFlags\FeatureFlag; use CanvasLMS\Api\Courses\Course; // Get feature flags for the account $flags = FeatureFlag::get(); // First page $allFlags = FeatureFlag::all(); // All flags // Get a specific feature flag $flag = FeatureFlag::find('new_gradebook'); // Enable a feature for the account $flag->enable(); // Disable a feature $flag->disable(); // Feature flags for a specific course $course = Course::find(123); $courseFlags = $course->featureFlags(); // Enable a feature for a specific course $courseFlag = $course->getFeatureFlag('anonymous_marking'); $courseFlag->enable();
Conversations (Internal Messaging)
use CanvasLMS\Api\Conversations\Conversation; // Get conversations for the current user $conversations = Conversation::get(['scope' => 'unread']); // First page $allConversations = Conversation::all(['scope' => 'unread']); // All pages // Create a new conversation $conversation = Conversation::create([ 'recipients' => ['user_123', 'course_456_students'], 'subject' => 'Assignment Feedback', 'body' => 'Great work on your recent submission!', 'group_conversation' => true ]); // Add a message to existing conversation $conversation->addMessage([ 'body' => 'Following up on my previous message...', 'attachment_ids' => [789] // Attach files ]); // Manage conversation state $conversation->markAsRead(); $conversation->star(); $conversation->archive(); // Batch operations Conversation::batchUpdate([1, 2, 3], ['event' => 'mark_as_read']); Conversation::markAllAsRead(); // Get unread count $unreadCount = Conversation::getUnreadCount();
Working with Multi-Context Resources
Some Canvas resources exist in multiple contexts (Account, Course, User, Group). Canvas LMS Kit follows an Account-as-Default convention for consistency:
use CanvasLMS\Api\Rubrics\Rubric; use CanvasLMS\Api\ExternalTools\ExternalTool; use CanvasLMS\Api\CalendarEvents\CalendarEvent; use CanvasLMS\Api\Files\File; // Direct calls default to Account context $rubrics = Rubric::get(); // Account-level rubrics (first page) $tools = ExternalTool::get(); // Account-level external tools (first page) $events = CalendarEvent::get(); // Account-level calendar events (first page) $files = File::get(); // Exception: User files (no account context) // Course-specific access through Course instance $course = Course::find(123); $courseRubrics = $course->rubrics(); // Course-specific rubrics $courseTools = $course->externalTools(); // Course-specific tools $courseEvents = $course->calendarEvents(); // Course-specific events $courseFiles = $course->files(); // Course-specific files // Direct context access when needed $userEvents = CalendarEvent::fetchByContext('user', 456); $groupFiles = File::fetchByContext('groups', 789);
Multi-Context Resources:
- Rubrics (Account/Course)
- External Tools (Account/Course)
- Calendar Events (Account/Course/User/Group)
- Files (User/Course/Group) - No Account context
- Groups (Account/Course/User)
- Content Migrations (Account/Course/Group/User)
Performance Considerations & Memory Usage
When working with large Canvas instances (universities, enterprise organizations), be mindful of memory usage:
// โ GOOD: Memory-efficient for large datasets $result = User::paginate(['per_page' => 100]); while ($result) { foreach ($result->getData() as $user) { // Process batch of 100 users } $result = $result->hasNextPage() ? $result->getNextPage() : null; } // โ GOOD: Get only what you need $recentCourses = Course::get(['per_page' => 20]); // Just first 20 // โ ๏ธ CAUTION: Loads entire dataset into memory $allUsers = User::all(); // Could be 50,000+ users! $allEnrollments = Enrollment::all(); // Could be millions! // โ ๏ธ BETTER: Process in batches for large datasets $page = 1; do { $batch = Enrollment::paginate(['page' => $page++, 'per_page' => 500]); // Process batch } while ($batch->hasNextPage());
Memory Guidelines:
- Use
get()
for dashboards and quick views (1 API call) - Use
paginate()
for large datasets and UI tables (controlled memory) - Use
all()
only when you need complete data AND know it's reasonably sized - Consider your server's memory limit when using
all()
on production data
Working with Course-Scoped Resources
Some Canvas resources are strictly course-scoped and require setting the course context before use:
use CanvasLMS\Api\Pages\Page; use CanvasLMS\Api\Quizzes\Quiz; use CanvasLMS\Api\Modules\Module; use CanvasLMS\Api\DiscussionTopics\DiscussionTopic; use CanvasLMS\Api\Courses\Course; // Get your course $course = Course::find(123); // Option 1: Set context for each API (required for direct API calls) Page::setCourse($course); Quiz::setCourse($course); Module::setCourse($course); DiscussionTopic::setCourse($course); // Now you can use the APIs directly $pages = Page::get(); // Get first page $quizzes = Quiz::all(); // Get all quizzes $modules = Module::get(); // Get first page $discussions = DiscussionTopic::all(); // Get all discussions // Option 2: Use course instance methods (recommended - no context setup needed) $pages = $course->pages(); // Returns first page only $quizzes = $course->quizzes(); // Returns first page only $modules = $course->modules(); // Returns first page only $discussions = $course->discussionTopics(); // Returns first page only
Important Notes:
- These APIs will throw an exception if you try to use them without setting the course context first.
- Relationship methods return FIRST PAGE ONLY for performance. To get all items:
// Get ALL modules for a course Module::setCourse($course); $allModules = Module::all(); // Gets all pages // Or paginate for control $paginated = Module::paginate(['per_page' => 50]);
Learning Outcomes
use CanvasLMS\Api\Outcomes\Outcome; use CanvasLMS\Api\OutcomeGroups\OutcomeGroup; // Create an outcome group $group = OutcomeGroup::create([ 'title' => 'Critical Thinking Skills', 'description' => 'Core competencies for analytical thinking' ]); // Create a learning outcome $outcome = Outcome::create([ 'title' => 'Analyze Complex Problems', 'description' => 'Student can break down complex problems into manageable parts', 'mastery_points' => 3, 'ratings' => [ ['description' => 'Exceeds', 'points' => 4], ['description' => 'Mastery', 'points' => 3], ['description' => 'Near Mastery', 'points' => 2], ['description' => 'Below Mastery', 'points' => 1] ] ]); // Link outcome to a group $group->linkOutcome($outcome->id); // Align outcome with an assignment $assignment = Assignment::find(123); $assignment->alignOutcome($outcome->id, [ 'mastery_score' => 3, 'possible_score' => 4 ]);
Content Migrations
use CanvasLMS\Api\Courses\Course; use CanvasLMS\Api\ContentMigrations\ContentMigration; // Copy content between courses $course = Course::find(456); $migration = $course->copyContentFrom(123, [ 'except' => ['announcements', 'calendar_events'] ]); // Selective copy with specific items $migration = $course->selectiveCopyFrom(123, [ 'assignments' => [1, 2, 3], 'quizzes' => ['quiz-1', 'quiz-2'], 'modules' => [10, 11] ]); // Import a Common Cartridge file $migration = $course->importCommonCartridge('/path/to/course.imscc'); // Copy with date shifting $migration = $course->copyWithDateShift(123, '2024-01-01', '2025-01-01', [ 'shift_dates' => true, 'old_start_date' => '2024-01-01', 'new_start_date' => '2025-01-01' ]); // Track migration progress while (!$migration->isCompleted()) { $progress = $migration->getProgress(); echo "Migration {$progress->workflow_state}: {$progress->completion}%\n"; sleep(5); $migration->refresh(); } // Handle migration issues $issues = $migration->migrationIssues(); foreach ($issues as $issue) { if ($issue->workflow_state === 'active') { $issue->resolve(); } }
๐ Supported APIs
โ Currently Implemented (37 APIs)
๐ Core Course Management
- โ Courses - Full CRUD operations
- โ Modules - Content organization
- โ Module Items - Individual content items
- โ Sections - Course sections
- โ Tabs - Navigation customization
- โ Pages - Wiki-style content
๐ฅ Users & Enrollment
- โ Users - User management with self() pattern support
- โ Enrollments - Course enrollments
- โ Admins - Administrative roles
- โ Accounts - Account management
๐ Assessment & Grading
- โ Assignments - Assignment management
- โ Quizzes - Quiz creation and management
- โ Quiz Submissions - Student attempts
- โ Submissions - Assignment submissions
- โ Submission Comments - Feedback
- โ Rubrics - Grading criteria and assessment
- โ Rubric Associations - Link rubrics to assignments
- โ Rubric Assessments - Grade with rubrics
- โ Outcomes - Learning objectives and competencies
- โ Outcome Groups - Organize learning outcomes
- โ Outcome Imports - Import outcome data
- โ Outcome Results - Track student achievement
๐ฌ Communication & Collaboration
- โ Discussion Topics - Forums and discussions
- โ Groups - Student groups and collaboration
- โ Group Categories - Organize and manage groups
- โ Group Memberships - Group member management
- โ Conferences - Web conferencing integration
- โ Conversations - Internal messaging system
- ๐ Announcements - Course announcements (coming soon)
๐ง Tools & Integration
- โ Files - File management and uploads
- โ External Tools - LTI integrations
- โ Module Assignment Overrides - Custom dates
- โ Calendar Events - Event management
- โ Appointment Groups - Scheduling
- โ Progress - Async operation tracking
- โ Content Migrations - Import/export course content
- โ Migration Issues - Handle import problems
- โ Feature Flags - Manage Canvas feature toggles
๐ Advanced Features
Production-Ready Middleware
// The SDK automatically handles rate limiting and retries $course = Course::find(123); // Protected by middleware // Canvas API Rate Limiting (3000 requests/hour) // โ Automatic throttling when approaching limits // โ Smart backoff strategies // โ Transparent to your application
Multi-Tenant Support
// Manage multiple Canvas instances Config::setContext('production'); $prodCourse = Course::find(123); Config::setContext('test'); $testCourse = Course::find(456);
Pagination Support (Simplified API)
// Three simple methods for all your pagination needs: // 1. get() - Fetch first page only (fast, memory efficient) $courses = Course::get(['per_page' => 100]); // 2. all() - Fetch ALL items across all pages automatically $allCourses = Course::all(); // 3. paginate() - Get results with pagination metadata $result = Course::paginate(['per_page' => 50]); echo "Page {$result->getCurrentPage()} of {$result->getTotalPages()}"; echo "Total courses: {$result->getTotalCount()}"; // Access the data foreach ($result->getData() as $course) { echo $course->name; } // Navigate pages if ($result->hasNextPage()) { $nextPage = $result->getNextPage(); }
Fluent Interface & Method Chaining
All save()
and delete()
methods return the instance, enabling method chaining:
// Save and continue working with the object $course->name = 'New Name'; $enrollments = $course->save()->enrollments(); // Chain multiple operations $assignment = new Assignment(); $assignment->name = 'Final Project'; $assignment->points_possible = 100; $submissions = $assignment->save()->getSubmissions(); // Error handling with fluent interface try { $user->email = 'new@example.com'; $user->save()->enrollments(); // Save and get enrollments } catch (CanvasApiException $e) { // Handle error - no more silent failures! }
Relationship Methods
// Efficient relationship loading $course = Course::find(123); $students = $course->getStudents(); $assignments = $course->assignments(); $modules = $course->modules();
Context Management (Account-as-Default)
Canvas LMS Kit uses the Account-as-Default convention for multi-context resources:
// Direct API calls use Account context (Config::getAccountId()) $groups = Group::get(); // First page of groups in the account $allGroups = Group::all(); // All groups in the account $rubrics = Rubric::get(); // First page of rubrics in the account $migrations = ContentMigration::all(); // All migrations in the account // Course-specific access via Course instance methods $course = Course::find(123); $courseGroups = $course->groups(); // Groups in this course $courseRubrics = $course->rubrics(); // Rubrics in this course $courseMigrations = $course->contentMigrations(); // Migrations in this course // User-specific access via User instance methods $user = User::find(456); $userGroups = $user->groups(); // User's groups $userMigrations = $user->contentMigrations(); // User's migrations // Group-specific access via Group instance methods $group = Group::find(789); $groupMigrations = $group->contentMigrations(); // Group's migrations
Why Account-as-Default?
- โ Consistency across all multi-context resources
- โ Respects Canvas hierarchy (Account โ Course โ User/Group)
- โ Clean separation of concerns
- โ No confusion about which context is being used
๐ Full Context Management Guide
๐งช Testing
# Using Docker (recommended) docker compose exec php composer test docker compose exec php composer check # Run all checks # Local development composer test composer cs-fix # Fix coding standards composer phpstan # Static analysis
๐ค Contributing
We welcome contributions! Please see our Contributing Guidelines.
Quick Contribution Guide
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Write tests for your changes
- Ensure all tests pass (
composer check
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ฌ Support
|
|
|
Resources
- ๐ Wiki Documentation - Comprehensive guides
- ๐ API Reference - Detailed API docs
- ๐ฌ GitHub Discussions - Community forum
- ๐ง Email: jjuanrivvera@gmail.com
โญ Show Your Support
If you find this project helpful, please consider giving it a star on GitHub! It helps others discover the project and motivates continued development.
Built with โค๏ธ by the Canvas LMS Kit community