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.
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 | Русский
- 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
- ✅ 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
- 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
- PHP 8.1 or higher
- Guzzle HTTP client 7.0+
- Laravel 8.0+ (optional, for Laravel integration)
Install the package via Composer:
composer require tigusigalpa/dropbox-phpThe package will automatically register its service provider and facade.
Publish the configuration file:
php artisan vendor:publish --tag=dropbox-configAdd 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- 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).
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'];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);
}
}// 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";// 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);// 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']);// 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";
}// 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']}";
}// 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);
}
}// 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/...');// 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);// 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']
);// 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',
]);// 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'];// 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);// 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 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!";
}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);// 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);// 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 access token when expired
$newTokenData = DropboxClient::refreshAccessToken(
$refreshToken,
'your_app_key',
'your_app_secret'
);
$newAccessToken = $newTokenData['access_token'];
// Update client token
$client->setAccessToken($newAccessToken);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);
}
}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// 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);// 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";
}
}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.
// 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);
});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);
}
}Run the test suite:
composer testRun tests with coverage:
composer test:coverageFor complete API documentation, visit:
// 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
);
}// 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'
);
}
}// 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'] . '">';
}
}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
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);
}The package includes:
- Unit tests for core functionality
- Integration test examples
- GitHub Actions workflow for CI/CD
- PHPUnit configuration
Run tests:
composer testRun tests with coverage:
composer test:coverage-
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
We welcome your contributions! Please follow these guidelines:
- 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
-
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
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
- 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
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);
}
}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
}
}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);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-autoloadEnable 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()
]);
}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.
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).
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.
- 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
- 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
- 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
// 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');| 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 |
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.
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'];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']]
]);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();
}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'];
}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']
]]);
}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
If you discover any security-related issues, please email sovletig@gmail.com instead of using the issue tracker.
MIT License (MIT). Please see LICENSE file for more information.
