{"id":33135105,"url":"https://github.com/araga-dev/aux-data","last_synced_at":"2025-11-15T10:01:59.463Z","repository":{"id":324285168,"uuid":"1096701417","full_name":"araga-dev/aux-data","owner":"araga-dev","description":"PSR-16 key/value cache built on SQLite and PDO. Lightweight, single-file, perfect for local config and small apps.","archived":false,"fork":false,"pushed_at":"2025-11-14T21:13:33.000Z","size":20,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-14T22:19:45.957Z","etag":null,"topics":["cache","composer-package","key-value-store","library","pdo","php","psr-16","settings","simple-cache","sqlite"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/araga-dev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-14T20:22:03.000Z","updated_at":"2025-11-14T21:09:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/araga-dev/aux-data","commit_stats":null,"previous_names":["araga-dev/aux-data"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/araga-dev/aux-data","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/araga-dev%2Faux-data","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/araga-dev%2Faux-data/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/araga-dev%2Faux-data/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/araga-dev%2Faux-data/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/araga-dev","download_url":"https://codeload.github.com/araga-dev/aux-data/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/araga-dev%2Faux-data/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":284538091,"owners_count":27022334,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-15T02:00:06.050Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","composer-package","key-value-store","library","pdo","php","psr-16","settings","simple-cache","sqlite"],"created_at":"2025-11-15T10:01:29.685Z","updated_at":"2025-11-15T10:01:59.456Z","avatar_url":"https://github.com/araga-dev.png","language":"PHP","readme":"# AuxData\n\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![PHP Version](https://img.shields.io/badge/php-%3E%3D8.1-8892BF.svg)](https://php.net/)\n[![PSR-16](https://img.shields.io/badge/PSR--16-compliant-brightgreen.svg)](https://www.php-fig.org/psr/psr-16/)\n\nSmall, dependency-free key/value storage on top of SQLite using PDO.\n\nAuxData is designed for situations where you want a simple and persistent\nconfiguration or cache-like store without bringing a full cache system or database\nabstraction layer into your project.\n\n## Features\n\n- **PSR-16 SimpleCache compliant** - Standard interface for interoperability\n- Lightweight: a single PHP class with minimal dependencies\n- Uses SQLite via PDO with WAL mode for better concurrency\n- Simple API: `set`, `get`, `has`, `delete`, `clear`\n- PSR-16 batch operations: `setMultiple`, `getMultiple`, `deleteMultiple`\n- Optional TTL (time-to-live) per key with `DateInterval` support\n- Multiple databases and table names supported\n- Atomic increment/decrement operations (safe for concurrent access)\n- Transaction support for batch operations\n- Garbage collection of expired keys\n- Statistics and chunked iteration for large datasets\n- Automatically creates the directory and SQLite file if needed\n\n## Requirements\n\n- PHP 8.1+\n- PDO SQLite extension\n- PSR-16 Simple Cache interface (`psr/simple-cache`)\n\n## When to Use\n\n**✅ Perfect for:**\n- CLI tools and scripts\n- Single-user desktop applications\n- Local configuration storage\n- Development/testing caches\n- Low to moderate traffic web applications\n- Background job storage\n- Simple session-like data\n- Any project needing PSR-16 SimpleCache without external services\n\n**❌ Not recommended for:**\n- High-traffic APIs with heavy concurrent writes (use Redis/Memcached instead)\n- Distributed systems requiring shared cache across multiple servers\n- Applications needing sub-millisecond response times\n- Systems with hundreds of concurrent write operations per second\n\n**💡 Concurrency Notes:**\n- SQLite with WAL mode handles multiple readers + one writer well\n- Perfect for scenarios with read-heavy workloads\n- `increment/decrement` operations are atomic and safe\n- Multiple processes on the same machine can safely access the database\n- For multi-server environments, use a network-based cache instead\n\n## Installation\n\n```bash\ncomposer require araga/aux-data\n```\n\n## Basic Usage\n\n```php\n\u003c?php\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Araga\\AuxData;\n\n// Open (or create) auxdata.db inside the \"storage\" folder\n$cache = AuxData::open(__DIR__ . '/storage');\n\n// Store a simple value\n$cache-\u003eset('app.name', 'My Awesome App');\n\n// Store an array (it will be JSON-encoded)\n$cache-\u003eset('features', [\n    'dark_mode' =\u003e true,\n    'beta'      =\u003e ['new_layout' =\u003e true],\n]);\n\n// Retrieve values\n$name     = $cache-\u003eget('app.name');                    // \"My Awesome App\"\n$missing  = $cache-\u003eget('missing.key', 'default-value'); // \"default-value\"\n\n// Check existence (optimized - no JSON decode)\nif ($cache-\u003ehas('features')) {\n    $features = $cache-\u003eget('features');\n}\n\n// Remove a single key\n$cache-\u003edelete('app.name');\n\n// Remove all keys\n// $cache-\u003eclear();\n```\n\n## PSR-16 SimpleCache Interface\n\nAuxData implements the standard PSR-16 interface:\n\n```php\n\u003c?php\n\nuse Araga\\AuxData;\nuse Psr\\SimpleCache\\CacheInterface;\n\n// Type-hint with the standard interface\nfunction processWithCache(CacheInterface $cache) {\n    $cache-\u003eset('key', 'value', 60); // TTL in seconds\n    return $cache-\u003eget('key');\n}\n\n$cache = AuxData::open(__DIR__ . '/storage');\n$result = processWithCache($cache); // Works with any PSR-16 implementation!\n```\n\n## Using TTL (Time-to-Live)\n\n```php\n\u003c?php\n\nuse Araga\\AuxData;\n\n$cache = AuxData::open(__DIR__ . '/storage');\n\n// TTL as integer (seconds)\n$cache-\u003eset('api_token', 'abc123', 60); // Expires in 60 seconds\n\n// TTL as DateInterval (PSR-16 standard)\n$cache-\u003eset('session', $data, new DateInterval('PT1H')); // Expires in 1 hour\n\n// No expiration\n$cache-\u003eset('permanent', 'forever', null);\n\n// After expiration, get() returns the default value\n$token = $cache-\u003eget('api_token', null);\n\n// Clean up all expired keys manually (reclaim disk space)\n$deletedCount = $cache-\u003ecleanExpired();\n```\n\n## Multiple Databases and Tables\n\n```php\n\u003c?php\n\nuse Araga\\AuxData;\n\n// Using a custom database name\n$userStorage = AuxData::open(__DIR__ . '/storage', 'users.db');\n\n// Using the fluent API\n$logs = AuxData::database('logs.db')-\u003eat(__DIR__ . '/storage');\n\n// Using a different table name\n$config = AuxData::open(__DIR__ . '/storage', null, 'config_table');\n```\n\n## PSR-16 Batch Operations\n\n```php\n\u003c?php\n\nuse Araga\\AuxData;\n\n$cache = AuxData::open(__DIR__ . '/storage');\n\n// Set multiple values at once (atomic transaction)\n$cache-\u003esetMultiple([\n    'app.locale' =\u003e 'en',\n    'app.debug'  =\u003e true,\n    'limits'     =\u003e ['max_items' =\u003e 50],\n], 3600); // TTL for all keys\n\n// Get multiple values at once\n$values = $cache-\u003egetMultiple(['app.locale', 'app.debug', 'missing'], 'N/A');\n// Returns: ['app.locale' =\u003e 'en', 'app.debug' =\u003e true, 'missing' =\u003e 'N/A']\n\n// Delete multiple keys\n$cache-\u003edeleteMultiple(['app.locale', 'app.debug']);\n```\n\n## Atomic Increment / Decrement\n\nThese operations are thread-safe and work correctly with concurrent processes:\n\n```php\n\u003c?php\n\nuse Araga\\AuxData;\n\n$counter = AuxData::open(__DIR__ . '/storage');\n\n// Initialize (if not exists) and increment\n$views = $counter-\u003eincrement('page.views'); // 1\n\n// Increment by custom amount\n$views = $counter-\u003eincrement('page.views', 10); // 11\n\n// Decrement\n$views = $counter-\u003edecrement('page.views', 2); // 9\n```\n\n## Transactions\n\nExecute multiple operations atomically:\n\n```php\n\u003c?php\n\nuse Araga\\AuxData;\n\n$store = AuxData::open(__DIR__ . '/storage');\n\n$result = $store-\u003etransaction(function ($store) {\n    $store-\u003eset('key1', 'value1');\n    $store-\u003eset('key2', 'value2');\n    \n    // If an exception is thrown, all changes are rolled back\n    \n    return 'success';\n});\n```\n\n## Advanced Features\n\n### Pull Operation (Get + Delete)\n\n```php\n\u003c?php\n\n$cache = AuxData::open(__DIR__ . '/storage');\n\n// Get value and immediately delete it\n$token = $cache-\u003epull('one_time_token', null);\n```\n\n### Processing Large Datasets\n\nUse `chunk()` to avoid loading all data into memory:\n\n```php\n\u003c?php\n\nuse Araga\\AuxData;\n\n$store = AuxData::open(__DIR__ . '/storage');\n\n// Process 100 records at a time\n$store-\u003echunk(100, function (array $items) {\n    foreach ($items as $key =\u003e $value) {\n        // Process each item\n    }\n});\n```\n\n### Get All Keys/Values\n\n```php\n\u003c?php\n\n$cache = AuxData::open(__DIR__ . '/storage');\n\n// Get all valid keys\n$keys = $cache-\u003ekeys();\n\n// Get all valid key =\u003e value pairs (WARNING: loads all into memory)\n$all = $cache-\u003eall();\n```\n\n### Statistics\n\nGet information about your database:\n\n```php\n\u003c?php\n\nuse Araga\\AuxData;\n\n$store = AuxData::open(__DIR__ . '/storage');\n\n$stats = $store-\u003estats();\n// Returns:\n// [\n//   'total' =\u003e 150,      // Total keys (including expired)\n//   'active' =\u003e 145,     // Non-expired keys  \n//   'expired' =\u003e 5,      // Expired keys\n//   'size' =\u003e 24576,     // Database file size in bytes\n// ]\n```\n\n## Multi-Process Usage\n\nAuxData is safe for use across multiple processes on the same machine:\n\n```php\n\u003c?php\n\n// Process 1 (web server)\n$cache = AuxData::open('/var/www/storage');\n$cache-\u003eincrement('requests');\n\n// Process 2 (background worker) \n$cache = AuxData::open('/var/www/storage');\n$cache-\u003eincrement('requests');\n\n// Process 3 (CLI script)\n$cache = AuxData::open('/var/www/storage');\n$count = $cache-\u003eget('requests'); // Gets the correct total\n```\n\nSQLite's WAL mode (enabled by default) allows:\n- Multiple processes reading simultaneously\n- One writer at a time (with 5-second busy timeout)\n- Automatic locking and retry on conflicts\n\n## Performance Considerations\n\n- **Concurrency**: WAL mode allows multiple readers + one writer. Good for read-heavy workloads.\n- **Value Size**: Maximum recommended size is 10MB per value. Larger values impact performance.\n- **Expired Keys**: Call `cleanExpired()` periodically to reclaim disk space.\n- **Large Datasets**: Use `chunk()` instead of `all()` when working with thousands of keys.\n- **Busy Timeout**: Set to 5 seconds by default. Writers will retry during this period.\n\n## Error Handling\n\nAuxData throws:\n\n- `InvalidArgumentException` for invalid keys, values, or parameters\n- `RuntimeException` for I/O problems or PDO connection failures\n\nMake sure the target directory is writable by your PHP process.\n\n## API Reference\n\n### PSR-16 Methods (Standard)\n- `set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool`\n- `get(string $key, mixed $default = null): mixed`\n- `delete(string $key): bool`\n- `clear(): bool`\n- `has(string $key): bool`\n- `setMultiple(iterable $values, null|int|DateInterval $ttl = null): bool`\n- `getMultiple(iterable $keys, mixed $default = null): iterable`\n- `deleteMultiple(iterable $keys): bool`\n\n### Additional Methods (Non-standard)\n- `AuxData::open(string $rootPath, ?string $databaseName = null, string $tableName = 'settings'): self`\n- `AuxData::database(string $databaseName, string $tableName = 'settings'): self`\n- `at(string $rootPath): self`\n- `pull(string $key, mixed $default = null): mixed`\n- `all(): array`\n- `keys(): array`\n- `increment(string $key, int $by = 1): int`\n- `decrement(string $key, int $by = 1): int`\n- `transaction(callable $callback): mixed`\n- `chunk(int $size, callable $callback): void`\n- `cleanExpired(): int`\n- `stats(): array`\n\n## Examples\n\nSee the `/examples` directory for complete working examples:\n\n- `quick-start.php` - Simplest possible usage\n- `psr16-example.php` - **PSR-16 interface demonstration**\n- `basic-usage.php` - Common operations and API overview\n- `ttl-example.php` - Time-to-live with integer and DateInterval\n- `chunk-example.php` - Processing large datasets\n- `concurrent-example.php` - Atomic counter operations\n- `multi-process-example.php` - Multi-process safety demonstration\n\n## Migration from Non-PSR-16 Version\n\nIf you're migrating from an older version, here are the breaking changes:\n\n| Old Method | New Method | Notes |\n|------------|------------|-------|\n| `forget($key)` | `delete($key)` | Now returns `bool` |\n| `flush()` | `clear()` | Now returns `bool` |\n| `setMany($values, $ttl)` | `setMultiple($values, $ttl)` | Now returns `bool` |\n| `many($keys, $default)` | `getMultiple($keys, $default)` | Returns iterable |\n\nAll other methods remain unchanged.\n\n## Project Structure\n\n```\n.\n├── src\n│   └── AuxData.php (PSR-16 compliant)\n├── examples/\n│   ├── quick-start.php\n│   ├── psr16-example.php\n│   ├── basic-usage.php\n│   ├── ttl-example.php\n│   ├── chunk-example.php\n│   ├── concurrent-example.php\n│   └── multi-process-example.php\n├── composer.json\n├── CHANGELOG.md\n├── LICENSE\n└── README.md\n```\n\n## License\n\nThis library is open-sourced software licensed under the [MIT license](LICENSE).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faraga-dev%2Faux-data","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faraga-dev%2Faux-data","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faraga-dev%2Faux-data/lists"}