freeloapp / php-sdk
Modern PHP SDK for Freelo API - Project and task management
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/freeloapp/php-sdk
Requires
- php: ^8.1
- php-http/discovery: ^1.19
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0|^2.0
- psr/simple-cache: ^2.0|^3.0
Requires (Dev)
- guzzlehttp/guzzle: ^7.8
- nyholm/psr7: ^1.8
- php-http/guzzle7-adapter: ^1.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^3.8
- symfony/cache: ^6.4|^7.0
Suggests
- guzzlehttp/guzzle: For HTTP client implementation
- nyholm/psr7: For PSR-7 message implementation
- symfony/cache: For caching support
This package is not auto-updated.
Last update: 2026-02-28 18:08:50 UTC
README
Modern, lightweight PHP SDK for Freelo API.
Features
- PHP 8.1+ with strict types
- PSR-18/17/7/16 compliant (bring your own HTTP client)
- Full API coverage (projects, tasks, files, comments, time tracking, etc.)
- Dynamic credentials with server-safe per-request switching
- Low-level
call()method for arbitrary API endpoints - Automatic pagination support
- Rate limiting detection
- Typed exceptions for error handling
Installation
composer require freeloapp/php-sdk
Install an HTTP client (e.g., Guzzle):
composer require guzzlehttp/guzzle php-http/guzzle7-adapter nyholm/psr7
Quick Start
use Freelo\Sdk\Freelo;
use Freelo\Sdk\Auth\ApiKeyCredentials;
$freelo = new Freelo(
new ApiKeyCredentials('your-api-key', 'your-email@example.com'),
userAgent: 'MyApp/1.0',
);
// List projects
foreach ($freelo->projects()->list() as $project) {
echo $project->name . "\n";
}
// Create a task
$task = $freelo->tasks()->create(
projectId: 123,
tasklistId: 456,
data: ['name' => 'New task', 'priority_enum' => 'h']
);
Authentication
Get your API key from Freelo Settings (scroll to API section).
use Freelo\Sdk\Auth\ApiKeyCredentials;
// Using environment variables (recommended)
$credentials = new ApiKeyCredentials(
apiKey: getenv('FREELO_API_KEY'),
email: getenv('FREELO_EMAIL')
);
$freelo = new Freelo($credentials, userAgent: 'MyApp/1.0');
Usage Examples
Projects
$projects = $freelo->projects()->list(); // List active projects
$project = $freelo->projects()->get(123); // Get project by ID
$freelo->projects()->archive(123); // Archive project
$freelo->projects()->activate(123); // Activate archived project
// Paginated results
$result = $freelo->projects()->getAll(['p' => 0]);
echo "Total: " . $result->getTotal();
Tasks
$tasks = $freelo->tasks()->listInTasklist(123, 456); // List tasks in tasklist
$task = $freelo->tasks()->get(789); // Get task by ID
$freelo->tasks()->finish(789); // Mark as complete
$freelo->tasks()->activate(789); // Reopen task
$freelo->tasks()->move(789, 999); // Move to another tasklist
$freelo->tasks()->addComment(789, 'Comment text'); // Add comment
Time Tracking
$freelo->timeTracking()->start(taskId: 123, note: 'Working');
$report = $freelo->timeTracking()->stop();
// Manual work report
$freelo->workReports()->create(123, [
'minutes' => 120,
'date_reported' => '2024-01-15',
'note' => 'Development work',
]);
Files
$freelo->files()->uploadToTask(taskId: 123, filePath: '/path/to/file.pdf');
$freelo->files()->download(fileId: 456, destination: '/path/to/save.pdf');
Dynamic Credentials
Per-request credentials (server-safe)
Use withCredentials() to create an isolated instance with different credentials. Each derived instance has its own client and state — safe for concurrent requests in multi-tenant server applications.
// Shared base instance (e.g. in a service provider)
$freelo = new Freelo(new ApiKeyCredentials(
apiKey: 'default-key',
email: 'default@example.com'
));
// Per-request — fully isolated, concurrency-safe
$userFreelo = $freelo->withCredentials(new ApiKeyCredentials(
apiKey: $user->apiKey,
email: $user->email
));
// Optionally override userAgent per tenant
$userFreelo = $freelo->withCredentials(
new ApiKeyCredentials($user->apiKey, $user->email),
userAgent: 'TenantApp/1.0',
);
$projects = $userFreelo->projects()->list();
For simple single-user scripts, you can swap credentials in place with setCredentials():
$freelo->setCredentials(new ApiKeyCredentials(
apiKey: 'new-key',
email: 'new@example.com'
));
// With userAgent override
$freelo->setCredentials(
new ApiKeyCredentials('new-key', 'new@example.com'),
userAgent: 'MyApp/2.0',
);
Note:
setCredentials()mutates the shared instance and is not safe for concurrent requests.
Lazy initialization
The SDK supports creating a client without credentials upfront. Credentials can be provided later via setCredentials() — validation is deferred to the first API request.
$freelo = new Freelo();
// ... later, when credentials are available
$freelo->setCredentials(new ApiKeyCredentials(
apiKey: $apiKey,
email: $email
));
$freelo->projects()->list(); // works
Low-level API Calls
Use call() to hit arbitrary API endpoints not yet covered by typed resource classes:
// GET request with query parameters
$response = $freelo->call('/projects', 'GET', params: ['p' => 0]);
$data = $response->json();
// POST request with body
$response = $freelo->call('/projects/123/tasklists/456/tasks', 'POST', data: [
'name' => 'New task',
'priority_enum' => 'h',
]);
// Also available on derived instances
$userFreelo = $freelo->withCredentials($credentials);
$response = $userFreelo->call('/users/me', 'GET');
Available Resources
| Resource | Method | Description |
|---|---|---|
| Projects | projects() | Project management |
| Tasks | tasks() | Task management |
| Tasklists | tasklists() | Tasklist management |
| Comments | comments() | Comment management |
| Files | files() | File upload/download |
| Task Labels | taskLabels() | Task label management |
| Project Labels | projectLabels() | Project label management |
| Subtasks | subtasks() | Subtask management |
| Time Tracking | timeTracking() | Time tracking |
| Work Reports | workReports() | Work report management |
| Users | users() | User management |
| Notifications | notifications() | Notifications |
| Events | events() | Activity events |
| Notes | notes() | Note management |
| Search | search() | Full-text search |
| Custom Fields | customFields() | Custom fields |
| Invoices | invoices() | Invoice management |
Pagination
use Freelo\Sdk\Http\Paginator;
// Auto-paginate through all results
foreach (Paginator::fetchAll(fn($p) => $freelo->projects()->getAll(['p' => $p])) as $project) {
echo $project->name . "\n";
}
// Manual pagination
$page = $freelo->tasks()->getAll(['p' => 0]);
echo "Page {$page->getPage()} of " . ceil($page->getTotal() / 20);
if ($page->hasNextPage()) {
$nextPage = $freelo->tasks()->getAll(['p' => $page->getNextPage()]);
}
Error Handling
use Freelo\Sdk\Exception\{
ApiException,
AuthenticationException,
NotFoundException,
RateLimitException,
ValidationException
};
try {
$project = $freelo->projects()->get(999);
} catch (NotFoundException $e) {
// Resource not found (404)
} catch (AuthenticationException $e) {
// Invalid credentials (401)
} catch (RateLimitException $e) {
// Rate limited (429) - retry after $e->getRetryAfter() seconds
} catch (ValidationException $e) {
// Invalid input (422)
} catch (ApiException $e) {
// Other API errors
}
Rate Limiting
The API allows 25 requests/minute. The SDK tracks limits automatically:
$limiter = $freelo->getClient()->getRateLimiter();
if ($limiter->isLimitExceeded()) {
sleep($limiter->getSecondsUntilReset());
}
Custom HTTP Client
use GuzzleHttp\Client;
use Http\Adapter\Guzzle7\Client as GuzzleAdapter;
$httpClient = new GuzzleAdapter(new Client(['timeout' => 30]));
$freelo = new Freelo($credentials, httpClient: $httpClient);
Caching (Optional)
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
$cache = new Psr16Cache(new FilesystemAdapter('freelo', 3600));
$freelo = new Freelo($credentials, cache: $cache);
Development
composer install # Install dependencies
composer test # Run tests
composer phpstan # Static analysis
composer check # Run all checks
Requirements
- PHP 8.1+
- PSR-18 HTTP Client (e.g., Guzzle)
- PSR-17 HTTP Factories (e.g., nyholm/psr7)
License
MIT License. See LICENSE.