tigusigalpa / dropbox-php
Modern PHP SDK for Dropbox API v2 integration with Laravel support
Fund package maintenance!
tigusigalpa
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 7
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/tigusigalpa/dropbox-php
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
- illuminate/support: ^8.0|^9.0|^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0
README
Modern, production-ready PHP SDK for Dropbox API v2 with full Laravel support (versions 8-12). This comprehensive package provides a clean, intuitive, and type-safe interface for interacting with Dropbox's powerful cloud storage, file sharing, and collaboration features. Whether you're building a file management system, backup solution, document collaboration platform, or integrating cloud storage into your PHP application, this SDK offers everything you need to work seamlessly with Dropbox.
Why Choose This Dropbox PHP SDK?
This library stands out as the most complete and modern Dropbox integration solution for PHP developers. Built with PHP 8.1+ and following industry best practices, it provides enterprise-grade reliability while maintaining simplicity for rapid development. Unlike other Dropbox libraries, this SDK offers complete API v2 coverage, native Laravel integration, comprehensive error handling, and extensive documentation with real-world examples.
Perfect for: Web applications, SaaS platforms, content management systems, backup solutions, file sharing services, document management systems, media galleries, collaborative tools, and any PHP project requiring cloud storage integration.
π Language: English | Π ΡΡΡΠΊΠΈΠΉ
Table of Contents
- Features
- Supported Endpoints
- Requirements
- Installation
- Quick Start
- Detailed Usage Examples
- OAuth 2.0 Authorization
- Laravel Usage
- Error Handling
- Advanced Examples
- Package Structure
- Testing
- Contributing
- Changelog
- License
Features
- β Full Dropbox API v2 Coverage - Complete implementation of all major endpoints
- π Laravel Integration - Seamless integration with Laravel 8, 9, 10, 11, and 12
- π― Modern PHP - Built with PHP 8.1+ features and best practices
- π¦ Standalone Usage - Works perfectly outside Laravel projects
- π OAuth 2.0 Support - Built-in OAuth flow helpers
- π Comprehensive Documentation - Detailed examples and API references
- π§ͺ Well Tested - Includes PHPUnit test suite
- π¨ Clean API - Intuitive, chainable methods
- β‘ Chunked Upload - Support for uploading large files in chunks
- π Batch Operations - Efficient batch operations
Supported Endpoints
- Files - Upload, download, move, copy, delete, search, and manage files/folders
- Sharing - Create shared links, manage folder/file sharing, collaborate with team members
- Users - Get account information, space usage, and user features
- File Requests - Create and manage file request forms
- Paper - Create, edit, and manage Dropbox Paper documents
- Check - API connectivity and health checks
Requirements
- PHP 8.1 or higher
- Guzzle HTTP client 7.0+
- Laravel 8.0+ (optional, for Laravel integration)
Installation
Install the package via Composer:
composer require tigusigalpa/dropbox-php
Laravel Installation
The package will automatically register its service provider and facade.
Publish the configuration file:
php artisan vendor:publish --tag=dropbox-config
Add your Dropbox credentials to your .env file:
DROPBOX_ACCESS_TOKEN=your_access_token_here DROPBOX_APP_KEY=your_app_key DROPBOX_APP_SECRET=your_app_secret DROPBOX_REDIRECT_URI=https://your-app.com/callback
Quick Start
Obtaining Access Token
- Create a Dropbox app at Dropbox App Console
- Choose your app type and permissions
- Generate an access token from the app settings page
For production applications, implement the OAuth 2.0 flow (see OAuth 2.0 Authorization section).
Basic Usage (Standalone PHP)
use Tigusigalpa\Dropbox\DropboxClient; $client = new DropboxClient('your_access_token'); // Get current user account info $account = $client->users->getCurrentAccount(); echo "Hello, " . $account['name']['display_name']; // Upload a file $result = $client->files->upload( '/Documents/hello.txt', 'Hello, Dropbox!', 'add' ); // List folder contents $contents = $client->files->listFolder('/Documents'); foreach ($contents['entries'] as $entry) { echo $entry['name'] . "\n"; } // Download a file $file = $client->files->download('/Documents/hello.txt'); file_put_contents('local-hello.txt', $file['content']); // Create a shared link $link = $client->sharing->createSharedLinkWithSettings('/Documents/hello.txt'); echo "Share this link: " . $link['url'];
Laravel Usage
use Tigusigalpa\Dropbox\Facades\Dropbox; // Using Facade $account = Dropbox::users()->getCurrentAccount(); // Using Dependency Injection use Tigusigalpa\Dropbox\DropboxClient; class FileController extends Controller { public function upload(Request $request, DropboxClient $dropbox) { $content = file_get_contents($request->file('document')->path()); $result = $dropbox->files->upload( '/uploads/' . $request->file('document')->getClientOriginalName(), $content ); return response()->json($result); } }
Detailed Usage Examples
Working with Files
Uploading Files
// Simple upload $client->files->upload('/path/to/file.txt', 'File content'); // Upload with options $client->files->upload( '/path/to/file.txt', $content, 'overwrite', // mode: 'add', 'overwrite', or 'update' true, // autorename if conflict false, // mute notifications false // strict conflict checking ); // Upload from local file $content = file_get_contents('/local/path/file.pdf'); $client->files->upload('/Dropbox/file.pdf', $content); // Upload with metadata $result = $client->files->upload( '/Documents/report.pdf', file_get_contents('local-report.pdf'), 'add', false, false, false ); echo "Uploaded file: {$result['name']}, size: {$result['size']} bytes";
Large File Upload (Chunked)
// Start upload session $session = $client->files->uploadSessionStart($firstChunk, false); $sessionId = $session['session_id']; // Append chunks $offset = strlen($firstChunk); $client->files->uploadSessionAppend($sessionId, $offset, $secondChunk, false); // Finish upload $offset += strlen($secondChunk); $result = $client->files->uploadSessionFinish( $sessionId, $offset, $lastChunk, ['path' => '/large-file.zip', 'mode' => 'add'] ); // Complete example for uploading a large file $filePath = '/path/to/large-video.mp4'; $chunkSize = 4 * 1024 * 1024; // 4MB chunks $file = fopen($filePath, 'rb'); // First chunk $firstChunk = fread($file, $chunkSize); $session = $client->files->uploadSessionStart($firstChunk, false); $offset = strlen($firstChunk); // Remaining chunks while (!feof($file)) { $chunk = fread($file, $chunkSize); $isLast = feof($file); if ($isLast) { // Last chunk - finish session $client->files->uploadSessionFinish( $session['session_id'], $offset, $chunk, ['path' => '/Videos/large-video.mp4', 'mode' => 'add'] ); } else { // Intermediate chunk $client->files->uploadSessionAppend( $session['session_id'], $offset, $chunk, false ); $offset += strlen($chunk); } } fclose($file);
Downloading Files
// Download file $file = $client->files->download('/Documents/report.pdf'); file_put_contents('local-report.pdf', $file['content']); echo "Downloaded file size: " . strlen($file['content']) . " bytes"; // Download specific revision $file = $client->files->download('/Documents/report.pdf', 'rev123abc'); // Download folder as ZIP $zip = $client->files->downloadZip('/Documents/Project'); file_put_contents('project.zip', $zip['content']); // Get temporary download link (valid for 4 hours) $link = $client->files->getTemporaryLink('/Documents/report.pdf'); echo "Temporary link: " . $link['link']; // Export file (e.g., Google Docs to PDF) $exported = $client->files->export('/Documents/google-doc.gdoc', 'pdf'); file_put_contents('exported.pdf', $exported['content']);
File and Folder Management
// Create folder $folder = $client->files->createFolder('/Projects/NewProject'); echo "Created folder: " . $folder['metadata']['path_display']; // Create multiple folders at once $result = $client->files->createFolderBatch([ '/Projects/Project1', '/Projects/Project2', '/Projects/Project3' ], false, false); // Move file/folder $moved = $client->files->move('/old/path.txt', '/new/path.txt'); echo "Moved to: " . $moved['metadata']['path_display']; // Copy file/folder $copied = $client->files->copy('/source.txt', '/destination.txt'); // Delete file/folder (to trash) $client->files->delete('/path/to/delete.txt'); // Permanently delete (bypass trash) $client->files->permanentlyDelete('/path/to/file.txt'); // Restore file to previous revision $restored = $client->files->restore('/path/to/file.txt', 'rev123abc'); // Get file revision history $revisions = $client->files->listRevisions('/Documents/important.docx', 'path', 100); foreach ($revisions['entries'] as $rev) { echo "Revision: {$rev['rev']}, date: {$rev['client_modified']}\n"; }
Search and Listing
// List folder contents $result = $client->files->listFolder('/Documents'); foreach ($result['entries'] as $entry) { if ($entry['.tag'] === 'folder') { echo "Folder: " . $entry['name'] . "\n"; } else { echo "File: " . $entry['name'] . " (" . $entry['size'] . " bytes)\n"; echo " Modified: " . $entry['client_modified'] . "\n"; } } // List recursively $result = $client->files->listFolder('', true); // Continue listing with cursor (pagination) if ($result['has_more']) { $more = $client->files->listFolderContinue($result['cursor']); } // Complete listing of large folder with pagination $allEntries = []; $result = $client->files->listFolder('/BigFolder'); $allEntries = array_merge($allEntries, $result['entries']); while ($result['has_more']) { $result = $client->files->listFolderContinue($result['cursor']); $allEntries = array_merge($allEntries, $result['entries']); } echo "Total items: " . count($allEntries); // Search files $results = $client->files->search( 'invoice', // query '/Documents', // path 100, // max results 'relevance', // order by 'active', // file status null, // filename only ['pdf', 'docx'], // file extensions ['documents'] // file categories ); foreach ($results['matches'] as $match) { $file = $match['metadata']['metadata']; echo "Found: {$file['name']} in {$file['path_display']}\n"; } // Continue search with cursor if ($results['has_more']) { $moreResults = $client->files->searchContinue($results['cursor']); } // Get file metadata $metadata = $client->files->getMetadata('/Documents/file.txt', true); echo "Modified: " . $metadata['client_modified']; echo "Size: " . $metadata['size'] . " bytes"; echo "ID: " . $metadata['id']; // Get metadata with media info $metadata = $client->files->getMetadata('/Photos/vacation.jpg', true); if (isset($metadata['media_info'])) { echo "Dimensions: {$metadata['media_info']['metadata']['dimensions']['width']}x"; echo "{$metadata['media_info']['metadata']['dimensions']['height']}"; }
Thumbnails and Previews
// Get thumbnail $thumb = $client->files->getThumbnail( '/Photos/vacation.jpg', 'jpeg', // format: 'jpeg' or 'png' 'w256h256', // size: w32h32, w64h64, w128h128, w256h256, w480h320, w640h480, w960h640, w1024h768, w2048h1536 'strict' // mode: 'strict', 'bestfit', 'fitone_bestfit' ); file_put_contents('thumb.jpg', $thumb['content']); // Get preview $preview = $client->files->getPreview('/Documents/presentation.pptx'); file_put_contents('preview.pdf', $preview['content']); // Get thumbnails in batch $thumbs = $client->files->getThumbnailBatch([ [ 'path' => '/Photos/img1.jpg', 'format' => 'jpeg', 'size' => 'w128h128', 'mode' => 'strict' ], [ 'path' => '/Photos/img2.jpg', 'format' => 'jpeg', 'size' => 'w128h128', 'mode' => 'strict' ], ]); foreach ($thumbs['entries'] as $entry) { if ($entry['.tag'] === 'success') { $thumbnail = $entry['thumbnail']; file_put_contents('thumb_' . basename($entry['metadata']['name']), $thumbnail); } }
Sharing
Shared Links
// Create shared link $link = $client->sharing->createSharedLinkWithSettings('/Documents/report.pdf', [ 'requested_visibility' => ['.tag' => 'public'], 'audience' => ['.tag' => 'public'], 'access' => ['.tag' => 'viewer'], ]); echo "Share URL: " . $link['url']; // Create link with password and expiration $link = $client->sharing->createSharedLinkWithSettings('/Documents/secret.pdf', [ 'link_password' => 'mypassword123', 'expires' => '2024-12-31T23:59:59Z', ]); // List all shared links $links = $client->sharing->listSharedLinks(); // Get shared link metadata $metadata = $client->sharing->getSharedLinkMetadata('https://www.dropbox.com/...'); // Modify shared link settings $updated = $client->sharing->modifySharedLinkSettings( 'https://www.dropbox.com/...', ['requested_visibility' => ['.tag' => 'password']] ); // Revoke shared link $client->sharing->revokeSharedLink('https://www.dropbox.com/...');
Folder Sharing
// Share a folder $shared = $client->sharing->shareFolder('/Projects/TeamProject', null, false); $folderId = $shared['shared_folder_id']; // Add members to shared folder $client->sharing->addFolderMember($folderId, [ [ 'member' => ['.tag' => 'email', 'email' => 'colleague@example.com'], 'access_level' => ['.tag' => 'editor'], ], ], false, 'Please review this project'); // List folder members $members = $client->sharing->listFolderMembers($folderId); // Update member permissions $client->sharing->updateFolderMember( $folderId, ['.tag' => 'email', 'email' => 'colleague@example.com'], 'viewer' ); // Remove folder member $client->sharing->removeFolderMember( $folderId, ['.tag' => 'email', 'email' => 'colleague@example.com'], true // leave a copy ); // List shared folders $folders = $client->sharing->listFolders(100); // Mount shared folder $client->sharing->mountFolder($folderId); // Unmount shared folder $client->sharing->unmountFolder($folderId); // Unshare folder $client->sharing->unshareFolder($folderId, false);
File Sharing
// Add file members $client->sharing->addFileMember( '/Documents/contract.pdf', [ [ 'member' => ['.tag' => 'email', 'email' => 'client@example.com'], 'access_level' => ['.tag' => 'viewer'], ], ], 'Please review and sign', false, 'viewer' ); // List file members $members = $client->sharing->listFileMembers('/Documents/contract.pdf'); // Remove file member $client->sharing->removeFileMember( '/Documents/contract.pdf', ['.tag' => 'email', 'email' => 'client@example.com'] );
Users and Account
// Get current account info $account = $client->users->getCurrentAccount(); echo "Account ID: " . $account['account_id']; echo "Name: " . $account['name']['display_name']; echo "Email: " . $account['email']; echo "Country: " . $account['country']; // Get another user's account info $user = $client->users->getAccount('dbid:AAH4f99T0taONIb-OurWxbNQ6ywGRopQngc'); // Get multiple users' info $users = $client->users->getAccountBatch([ 'dbid:AAH4f99T0taONIb-OurWxbNQ6ywGRopQngc', 'dbid:AAH1234567890abcdefghijklmnopqrst', ]); // Get space usage $space = $client->users->getSpaceUsage(); echo "Used: " . $space['used'] . " bytes\n"; echo "Allocated: " . $space['allocation']['allocated'] . " bytes\n"; $percentage = ($space['used'] / $space['allocation']['allocated']) * 100; echo "Usage: " . number_format($percentage, 2) . "%\n"; // Check feature availability $features = $client->users->getFeaturesValues([ 'paper_as_files', 'file_locking', ]);
File Requests
// Create file request $request = $client->fileRequests->create( 'Upload your documents', '/File Requests/Documents', '2024-12-31T23:59:59Z', // deadline true, // open 'Please upload all required documents for the application' ); echo "File request URL: " . $request['url']; // Get file request $request = $client->fileRequests->get('oaCAVmEyrqYnkZX9955Y'); // List all file requests $requests = $client->fileRequests->list(1000); // Update file request $updated = $client->fileRequests->update('oaCAVmEyrqYnkZX9955Y', [ 'title' => 'Updated Title', 'open' => false, ]); // Delete file requests $client->fileRequests->delete(['oaCAVmEyrqYnkZX9955Y']); // Delete all closed file requests $client->fileRequests->deleteAllClosed(); // Count file requests $count = $client->fileRequests->count(); echo "Total file requests: " . $count['file_request_count'];
Paper Documents
// Create Paper document $doc = $client->paper->docsCreate( '<h1>Meeting Notes</h1><p>Discussion points...</p>', 'html' ); $docId = $doc['doc_id']; // Download Paper document $content = $client->paper->docsDownload($docId, 'markdown'); file_put_contents('notes.md', $content['content']); // Update Paper document $client->paper->docsUpdate( $docId, '<h1>Updated Notes</h1><p>New content...</p>', 'html', 'append', 1 ); // Get Paper document metadata $metadata = $client->paper->docsGetMetadata($docId); // List Paper documents $docs = $client->paper->docsList('docs_accessed', 'modified', 'descending', 100); // Share Paper document $client->paper->docsUsersAdd($docId, [ [ 'member' => ['.tag' => 'email', 'email' => 'team@example.com'], 'permission_level' => ['.tag' => 'edit'], ], ]); // List users with access $users = $client->paper->docsUsersList($docId, 100); // Remove users $client->paper->docsUsersRemove($docId, [ ['.tag' => 'email', 'email' => 'team@example.com'], ]); // Delete Paper document $client->paper->docsPermanentlyDelete($docId);
Batch Operations
// Copy multiple files $job = $client->files->copyBatch([ ['from_path' => '/file1.txt', 'to_path' => '/backup/file1.txt'], ['from_path' => '/file2.txt', 'to_path' => '/backup/file2.txt'], ]); // Check batch job status $status = $client->files->copyBatchCheck($job['async_job_id']); // Move multiple files $job = $client->files->moveBatch([ ['from_path' => '/old/file1.txt', 'to_path' => '/new/file1.txt'], ['from_path' => '/old/file2.txt', 'to_path' => '/new/file2.txt'], ]); // Delete multiple files $job = $client->files->deleteBatch(['/file1.txt', '/file2.txt', '/file3.txt']);
Save Files from URL
// Save file from URL $job = $client->files->saveUrl('/Downloads/image.jpg', 'https://example.com/image.jpg'); // Check save URL job status $status = $client->files->saveUrlCheckJobStatus($job['async_job_id']); if ($status['.tag'] === 'complete') { echo "File saved successfully!"; }
OAuth 2.0 Authorization
Authorization URL
use Tigusigalpa\Dropbox\DropboxClient; // Generate authorization URL $authUrl = DropboxClient::getAuthorizationUrl( 'your_app_key', 'https://your-app.com/callback', 'random_state_string', // CSRF protection ['files.content.write', 'files.content.read'] // optional scopes ); // Redirect user to authorization URL header('Location: ' . $authUrl);
Exchange Code for Token
// In your callback route $code = $_GET['code']; $state = $_GET['state']; // Verify state parameter (CSRF protection) if ($state !== $_SESSION['oauth_state']) { die('Invalid state parameter'); } // Exchange code for access token $tokenData = DropboxClient::getAccessToken( $code, 'your_app_key', 'your_app_secret', 'https://your-app.com/callback' ); // Store tokens securely $accessToken = $tokenData['access_token']; $refreshToken = $tokenData['refresh_token'] ?? null; // Create client with new token $client = new DropboxClient($accessToken);
Laravel OAuth Example
// routes/web.php Route::get('/dropbox/auth', [DropboxController::class, 'redirectToDropbox']); Route::get('/dropbox/callback', [DropboxController::class, 'handleCallback']); // app/Http/Controllers/DropboxController.php use Tigusigalpa\Dropbox\DropboxClient; class DropboxController extends Controller { public function redirectToDropbox() { $state = Str::random(40); session(['dropbox_state' => $state]); $url = DropboxClient::getAuthorizationUrl( config('dropbox.app_key'), config('dropbox.redirect_uri'), $state ); return redirect($url); } public function handleCallback(Request $request) { if ($request->state !== session('dropbox_state')) { abort(403, 'Invalid state'); } $tokenData = DropboxClient::getAccessToken( $request->code, config('dropbox.app_key'), config('dropbox.app_secret'), config('dropbox.redirect_uri') ); // Store tokens for the user auth()->user()->update([ 'dropbox_access_token' => encrypt($tokenData['access_token']), 'dropbox_refresh_token' => encrypt($tokenData['refresh_token'] ?? null), ]); return redirect('/dashboard')->with('success', 'Dropbox connected!'); } }
Refresh Token
// Refresh access token when expired $newTokenData = DropboxClient::refreshAccessToken( $refreshToken, 'your_app_key', 'your_app_secret' ); $newAccessToken = $newTokenData['access_token']; // Update client token $client->setAccessToken($newAccessToken);
Error Handling
use Tigusigalpa\Dropbox\Exceptions\DropboxException; try { $result = $client->files->upload('/test.txt', 'content'); } catch (DropboxException $e) { echo "Error: " . $e->getMessage() . "\n"; echo "Status Code: " . $e->getCode() . "\n"; // Get detailed error information $response = $e->getResponse(); if ($response) { echo "Error Summary: " . $e->getErrorSummary() . "\n"; echo "Error Tag: " . $e->getErrorTag() . "\n"; print_r($response); } }
Advanced Examples
Custom HTTP Client Configuration
use GuzzleHttp\Client as GuzzleClient; use Tigusigalpa\Dropbox\DropboxClient; // Create custom Guzzle client $guzzle = new GuzzleClient([ 'timeout' => 60, 'verify' => true, 'proxy' => 'http://proxy.example.com:8080', ]); // Note: Currently the package creates its own Guzzle instance // For custom configuration, you may need to extend the DropboxClient class
Working with Cursors (Pagination)
// List all files in a large folder $cursor = null; $allFiles = []; do { if ($cursor === null) { $result = $client->files->listFolder('/LargeFolder'); } else { $result = $client->files->listFolderContinue($cursor); } $allFiles = array_merge($allFiles, $result['entries']); $cursor = $result['cursor']; } while ($result['has_more']); echo "Total files: " . count($allFiles);
Monitoring Folder Changes
// Get initial cursor $cursor = $client->files->listFolderGetLatestCursor('/MonitoredFolder', true); $cursorValue = $cursor['cursor']; // Later, check for changes $changes = $client->files->listFolderLongpoll($cursorValue, 30); if ($changes['changes']) { // Get the actual changes $result = $client->files->listFolderContinue($cursorValue); foreach ($result['entries'] as $entry) { echo "Changed: " . $entry['name'] . "\n"; } }
Performance and Optimization
Efficient File Operations
This SDK is optimized for performance with several key features:
Chunked Uploads: Large files are automatically handled through chunked upload sessions, preventing memory exhaustion and timeout issues. The SDK efficiently manages upload sessions with configurable chunk sizes (default 4MB) for optimal performance across different network conditions.
Batch Operations: Process multiple files simultaneously using batch endpoints, reducing API calls and improving throughput. Batch operations support up to 1000 files per request for copy, move, and delete operations.
Connection Pooling: Built on Guzzle HTTP client with persistent connections, reducing overhead for multiple API calls within the same session.
Memory Management: Stream-based file handling for downloads prevents loading entire files into memory, making it suitable for processing large media files and archives.
Caching Strategies
// Cache folder listings to reduce API calls $cacheKey = 'dropbox_folder_' . md5($path); $contents = Cache::remember($cacheKey, 3600, function() use ($client, $path) { return $client->files->listFolder($path); }); // Cache shared links $linkCache = Cache::remember('dropbox_link_' . $fileId, 86400, function() use ($client, $path) { return $client->sharing->createSharedLinkWithSettings($path); });
Rate Limiting Best Practices
Dropbox API implements rate limiting. This SDK handles rate limit responses gracefully:
try { $result = $client->files->upload($path, $content); } catch (DropboxException $e) { if ($e->getCode() === 429) { // Rate limited - wait and retry $retryAfter = $e->getResponse()['retry_after'] ?? 60; sleep($retryAfter); $result = $client->files->upload($path, $content); } }
Testing
Run the test suite:
composer test
Run tests with coverage:
composer test:coverage
API Reference
For complete API documentation, visit:
Common Use Cases
Backup System
// Backup local files to Dropbox $backupFolder = '/Backups/' . date('Y-m-d'); $client->files->createFolder($backupFolder); $files = glob('/var/www/app/storage/backups/*.sql'); foreach ($files as $file) { $content = file_get_contents($file); $client->files->upload( $backupFolder . '/' . basename($file), $content ); }
File Sync
// Sync local folder with Dropbox $localPath = '/local/documents'; $remotePath = '/Documents'; $localFiles = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($localPath) ); foreach ($localFiles as $file) { if ($file->isFile()) { $relativePath = str_replace($localPath, '', $file->getPathname()); $content = file_get_contents($file->getPathname()); $client->files->upload( $remotePath . $relativePath, $content, 'overwrite' ); } }
Image Gallery
// Create image gallery with thumbnails $photos = $client->files->listFolder('/Photos'); foreach ($photos['entries'] as $photo) { if ($photo['.tag'] === 'file') { // Get thumbnail $thumb = $client->files->getThumbnail( $photo['path_display'], 'jpeg', 'w256h256' ); // Save thumbnail file_put_contents( 'thumbs/' . $photo['name'], $thumb['content'] ); // Create shared link for full image $link = $client->sharing->createSharedLinkWithSettings( $photo['path_display'] ); echo '<img src="thumbs/' . $photo['name'] . '" data-full="' . $link['url'] . '">'; } }
Package Structure
Core Components
dropbox-php/
βββ config/
β βββ dropbox.php # Laravel configuration
βββ examples/
β βββ basic-usage.php # Standalone PHP usage examples
β βββ laravel-usage.php # Laravel integration examples
β βββ oauth-flow.php # OAuth 2.0 flow implementation
βββ src/
β βββ Endpoints/ # API endpoint implementations
β β βββ Check.php # API health checks
β β βββ FileRequests.php # File request operations
β β βββ Files.php # File/folder operations (40+ methods)
β β βββ Paper.php # Dropbox Paper operations
β β βββ Sharing.php # Sharing and collaboration (30+ methods)
β β βββ Users.php # User account operations
β βββ Exceptions/
β β βββ DropboxException.php # Custom exception class
β βββ Facades/
β β βββ Dropbox.php # Laravel facade
β βββ DropboxClient.php # Main client class
β βββ DropboxServiceProvider.php # Laravel service provider
βββ tests/ # PHPUnit tests
Usage Patterns
Standalone PHP:
$client = new DropboxClient($accessToken); $result = $client->files->upload('/path/file.txt', $content);
Laravel Facade:
use Tigusigalpa\Dropbox\Facades\Dropbox; $result = Dropbox::files()->upload('/path/file.txt', $content);
Laravel Dependency Injection:
public function upload(DropboxClient $dropbox) { $result = $dropbox->files->upload('/path/file.txt', $content); }
Testing
The package includes:
- Unit tests for core functionality
- Integration test examples
- GitHub Actions workflow for CI/CD
- PHPUnit configuration
Run tests:
composer test
Run tests with coverage:
composer test:coverage
Setup for Testing
-
Fork the repository
-
Clone your fork:
git clone https://github.com/YOUR_USERNAME/dropbox-php.git cd dropbox-php -
Install dependencies:
composer install
-
Create a
.envfile with your Dropbox credentials for testing:DROPBOX_ACCESS_TOKEN=your_test_token
Contributing
We welcome your contributions! Please follow these guidelines:
Coding Standards
- Follow PSR-12 coding standards
- Write clear, descriptive commit messages
- Add PHPDoc blocks for all public methods
- Use type hints for parameters and return types
- Keep methods focused and single-purpose
Pull Request Process
-
Create a new branch for your feature:
git checkout -b feature/your-feature-name
-
Make your changes and commit:
git commit -m "Add feature: description" -
Push to your fork:
git push origin feature/your-feature-name
-
Create a Pull Request on GitHub
-
Ensure all tests pass and code follows standards
Adding New Features
When adding new Dropbox API endpoints:
- Create or update the appropriate endpoint class in
src/Endpoints/ - Add comprehensive PHPDoc comments
- Include links to official Dropbox API documentation
- Add usage examples to README.md
- Write tests for the new functionality
Reporting Issues
- Use the GitHub issue tracker
- Include PHP version, Laravel version (if applicable)
- Provide code examples that reproduce the issue
- Include error messages and stack traces
Troubleshooting Guide
Common Issues and Solutions
Authentication Errors
Problem: "Invalid access token" error
Solutions:
- Verify your access token is correct and not expired
- For OAuth tokens, implement refresh token logic
- Check that your app has the required permissions/scopes
- Ensure the token hasn't been revoked in Dropbox App Console
// Check token validity try { $account = $client->users->getCurrentAccount(); echo "Token is valid for: " . $account['email']; } catch (DropboxException $e) { if ($e->getCode() === 401) { // Token invalid - need to refresh or re-authenticate $newToken = DropboxClient::refreshAccessToken($refreshToken, $appKey, $appSecret); } }
File Upload Failures
Problem: Upload fails for large files or times out
Solutions:
- Use chunked upload for files larger than 150MB
- Increase PHP
max_execution_timeandmemory_limit - Implement retry logic for network failures
- Check file path format (must start with /)
// Robust upload with retry $maxRetries = 3; $attempt = 0; while ($attempt < $maxRetries) { try { $result = $client->files->upload($path, $content); break; } catch (DropboxException $e) { $attempt++; if ($attempt >= $maxRetries) throw $e; sleep(2 ** $attempt); // Exponential backoff } }
Path Errors
Problem: "Path not found" or "Malformed path" errors
Solutions:
- Ensure paths start with
/(e.g.,/Documents/file.txt) - Use proper encoding for special characters
- Check that parent folders exist before creating files
- Verify case sensitivity (Dropbox paths are case-insensitive but case-preserving)
// Ensure parent folder exists $filePath = '/Documents/Reports/2024/report.pdf'; $parentPath = dirname($filePath); try { $client->files->getMetadata($parentPath); } catch (DropboxException $e) { // Parent doesn't exist - create it $client->files->createFolder($parentPath); } $client->files->upload($filePath, $content);
Laravel Integration Issues
Problem: Service provider not loading or facade not working
Solutions:
- Clear Laravel cache:
php artisan cache:clear - Clear config cache:
php artisan config:clear - Republish config:
php artisan vendor:publish --tag=dropbox-config --force - Verify
.envvariables are set correctly - Check that package is in
composer.jsonrequire section
# Complete Laravel reset
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear
composer dump-autoload
Debug Mode
Enable detailed error logging:
try { $result = $client->files->upload($path, $content); } catch (DropboxException $e) { // Log detailed error information Log::error('Dropbox API Error', [ 'message' => $e->getMessage(), 'code' => $e->getCode(), 'error_summary' => $e->getErrorSummary(), 'error_tag' => $e->getErrorTag(), 'response' => $e->getResponse(), 'trace' => $e->getTraceAsString() ]); }
Frequently Asked Questions (FAQ)
General Questions
Q: Is this library production-ready?
A: Yes, this SDK is production-ready and actively maintained. It includes comprehensive error handling, extensive test coverage, and follows PHP best practices. Many applications use it in production environments.
Q: What's the difference between this and the official Dropbox SDK?
A: This SDK provides a more modern, Laravel-friendly interface with better documentation and examples. It's built specifically for PHP 8.1+ with type safety, comprehensive error handling, and seamless Laravel integration out of the box.
Q: Can I use this without Laravel?
A: Absolutely! The SDK works perfectly as a standalone PHP library. Laravel integration is optional and provided as an additional convenience feature.
Q: Does it support Dropbox Business/Team accounts?
A: Yes, the SDK supports both personal and business Dropbox accounts. For team operations, use the appropriate team-scoped access tokens.
Technical Questions
Q: What's the maximum file size I can upload?
A: Using chunked upload, you can upload files up to 350GB. For files under 150MB, use the simple upload method. For larger files, use the upload session methods.
Q: How do I handle rate limiting?
A: The SDK throws a DropboxException with code 429 when rate limited. Implement exponential backoff retry logic as shown in the troubleshooting section.
Q: Can I upload files from URLs directly to Dropbox?
A: Yes, use the saveUrl() method which tells Dropbox to download the file from a URL directly, saving bandwidth and time.
Q: How do I get a permanent link to a file?
A: Use createSharedLinkWithSettings() to create a permanent shared link. Temporary links (4-hour expiry) can be created with getTemporaryLink().
Q: Does it support Dropbox Paper?
A: Yes, full Paper API support is included for creating, editing, sharing, and managing Dropbox Paper documents.
Q: How do I handle file conflicts?
A: Use the mode parameter in upload methods: 'add' (fail if exists), 'overwrite' (replace existing), or 'update' (update specific revision).
Security Questions
Q: How should I store access tokens?
A: Never store tokens in plain text. Use Laravel's encryption (encrypt()), environment variables, or secure key management systems. For production, implement OAuth flow with refresh tokens.
Q: Is it safe to use in multi-tenant applications?
A: Yes, create separate DropboxClient instances per user with their individual access tokens. Never share tokens between users.
Q: How do I revoke access?
A: Revoke tokens through the Dropbox App Console or implement token revocation in your application's settings.
Best Practices
Security Best Practices
- Never hardcode access tokens - Use environment variables or secure configuration management
- Implement OAuth 2.0 flow - For production applications, use proper OAuth instead of generated tokens
- Use refresh tokens - Implement token refresh logic to maintain long-term access
- Validate user input - Sanitize file paths and names before passing to API
- Implement rate limiting - Add application-level rate limiting to prevent API abuse
- Log security events - Track authentication failures and suspicious activities
- Use HTTPS only - Ensure all callbacks and webhooks use HTTPS
- Scope permissions appropriately - Request only the OAuth scopes your application needs
Development Best Practices
- Use type hints - Leverage PHP 8.1+ type system for better code quality
- Handle exceptions properly - Always wrap API calls in try-catch blocks
- Implement retry logic - Handle transient failures with exponential backoff
- Cache API responses - Reduce API calls by caching folder listings and metadata
- Use batch operations - Process multiple files efficiently with batch endpoints
- Test with real data - Create a test Dropbox account for development
- Monitor API usage - Track API call volumes to stay within rate limits
- Version your code - Use semantic versioning and maintain changelog
Performance Best Practices
- Use chunked uploads - For files over 150MB, always use upload sessions
- Implement pagination - Handle large folder listings with cursor-based pagination
- Stream large downloads - Don't load entire files into memory
- Optimize search queries - Use specific paths and filters to reduce result sets
- Leverage cursors - Use
listFolderContinue()for efficient pagination - Batch thumbnail requests - Get multiple thumbnails in one API call
- Use temporary links - For public file access, temporary links are faster than downloads
- Implement queue workers - Process large file operations asynchronously in Laravel
Laravel-Specific Best Practices
// Use queued jobs for large operations class UploadToDropboxJob implements ShouldQueue { public function handle(DropboxClient $dropbox) { $dropbox->files->upload($this->path, $this->content); } } // Use events for file operations event(new FileUploadedToDropbox($filePath, $metadata)); // Implement middleware for Dropbox webhooks Route::post('/dropbox/webhook', [DropboxWebhookController::class, 'handle']) ->middleware('verify.dropbox.signature');
Comparison with Alternatives
Why Choose This SDK Over Others?
| Feature | This SDK | Official Dropbox SDK | Other Libraries |
|---|---|---|---|
| PHP Version | 8.1+ (Modern) | 7.4+ | Varies |
| Laravel Integration | Native, Built-in | Manual | Limited/None |
| API v2 Coverage | Complete | Complete | Partial |
| Documentation | Extensive + Examples | API Reference Only | Limited |
| Type Safety | Full Type Hints | Partial | Minimal |
| Error Handling | Comprehensive | Basic | Varies |
| Chunked Upload | Built-in | Manual Implementation | Often Missing |
| Batch Operations | Full Support | Full Support | Limited |
| OAuth 2.0 Helpers | Included | Manual | Manual |
| Active Maintenance | Yes | Yes | Often Abandoned |
| Test Coverage | Comprehensive | Good | Varies |
| Code Quality | PSR-12, Modern PHP | Good | Varies |
Migration from Other Libraries
Migrating from other Dropbox libraries is straightforward:
// Old library (example) $dropbox->uploadFile('/path', $content); // This SDK $client->files->upload('/path', $content); // Old library $dropbox->getMetadata('/path'); // This SDK $client->files->getMetadata('/path');
The SDK follows intuitive naming conventions that map directly to Dropbox API documentation, making migration and learning easier.
Real-World Use Cases
E-Commerce Platform
Store product images, invoices, and customer documents:
// Upload product images with organized structure $productId = 12345; $imagePath = "/products/{$productId}/images/main.jpg"; $client->files->upload($imagePath, $imageContent); // Generate shareable link for product image $link = $client->sharing->createSharedLinkWithSettings($imagePath); $product->image_url = $link['url'];
Document Management System
Manage corporate documents with version control:
// Upload document with metadata $result = $client->files->upload( "/documents/contracts/{$contractId}.pdf", $pdfContent, 'add' ); // Track revisions $revisions = $client->files->listRevisions($result['path_display']); // Share with specific users $client->sharing->addFileMember($result['path_display'], [ ['member' => ['.tag' => 'email', 'email' => 'legal@company.com']] ]);
Automated Backup System
Create scheduled backups of critical data:
// Laravel scheduled task protected function schedule(Schedule $schedule) { $schedule->call(function (DropboxClient $dropbox) { $backupPath = '/backups/' . date('Y-m-d-H-i-s'); $dropbox->files->createFolder($backupPath); // Backup database $dbBackup = Storage::get('backups/database.sql'); $dropbox->files->upload("{$backupPath}/database.sql", $dbBackup); // Backup files $filesBackup = Storage::get('backups/files.tar.gz'); $dropbox->files->upload("{$backupPath}/files.tar.gz", $filesBackup); })->daily(); }
Media Gallery Application
Build a photo gallery with thumbnails:
// Upload photos and generate thumbnails foreach ($photos as $photo) { $path = "/gallery/{$albumId}/{$photo->name}"; $client->files->upload($path, $photo->content); // Get thumbnail $thumb = $client->files->getThumbnail($path, 'jpeg', 'w256h256'); Storage::put("thumbnails/{$photo->id}.jpg", $thumb['content']); // Create public link $link = $client->sharing->createSharedLinkWithSettings($path); $photo->public_url = $link['url']; }
Collaborative Workspace
Enable team collaboration with shared folders:
// Create project workspace $projectFolder = "/projects/{$projectName}"; $client->files->createFolder($projectFolder); // Share with team $shared = $client->sharing->shareFolder($projectFolder); // Add team members foreach ($teamMembers as $member) { $client->sharing->addFolderMember($shared['shared_folder_id'], [[ 'member' => ['.tag' => 'email', 'email' => $member->email], 'access_level' => ['.tag' => $member->role === 'admin' ? 'editor' : 'viewer'] ]]); }
Changelog
Version 1.0.0 - 2024-12-20
Added:
- Initial release
- Full Dropbox API v2 support
- Files endpoint with complete file/folder operations
- Sharing endpoint for collaboration features
- Users endpoint for account management
- File Requests endpoint
- Paper endpoint for Dropbox Paper documents
- Check endpoint for API health checks
- Laravel 8-12 integration with service provider and facade
- OAuth 2.0 flow helpers
- Comprehensive documentation and examples
- PHPUnit test suite
- GitHub Actions CI/CD workflow
Features:
- Upload/download files with chunked upload support
- File and folder management (copy, move, delete, search)
- Shared links and folder sharing
- Batch operations support
- Thumbnail generation
- File preview and export
- Space usage tracking
- Error handling with detailed exceptions
Security
If you discover any security-related issues, please email sovletig@gmail.com instead of using the issue tracker.
Credits
License
MIT License (MIT). Please see LICENSE file for more information.