{"id":18552090,"url":"https://github.com/baraja-core/session","last_synced_at":"2026-05-02T03:07:05.251Z","repository":{"id":48404094,"uuid":"225583325","full_name":"baraja-core/session","owner":"baraja-core","description":"Simple performance package for storage your sessions to database by native PDO.","archived":false,"fork":false,"pushed_at":"2022-09-08T13:39:19.000Z","size":34,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-15T11:11:22.367Z","etag":null,"topics":["database","doctrine","handler","storage"],"latest_commit_sha":null,"homepage":null,"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/baraja-core.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-03T09:41:22.000Z","updated_at":"2022-03-12T10:05:06.000Z","dependencies_parsed_at":"2022-09-21T21:02:35.360Z","dependency_job_id":null,"html_url":"https://github.com/baraja-core/session","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/baraja-core/session","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fsession","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fsession/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fsession/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fsession/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/baraja-core","download_url":"https://codeload.github.com/baraja-core/session/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fsession/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32521114,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"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":["database","doctrine","handler","storage"],"created_at":"2024-11-06T21:12:54.630Z","updated_at":"2026-05-02T03:07:05.242Z","avatar_url":"https://github.com/baraja-core.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP Native PDO Session Storage\n\nSimple, high-performance package for storing PHP sessions in a MySQL database using native `\\PDO`. This package provides a robust database-backed session handler that works seamlessly with both native PHP applications and the Nette Framework.\n\n## 🎯 Key Features\n\n- **Native PDO Implementation** - Uses PHP's native PDO extension for reliable database connections with prepared statements\n- **Framework Agnostic** - Works with native PHP applications out of the box\n- **Nette Framework Integration** - Includes DI extension for seamless Nette integration\n- **Doctrine ORM Support** - Optional entity mapping for Doctrine-based applications\n- **Automatic Garbage Collection** - Probabilistic GC with configurable session lifetime (14 days default)\n- **CLI Safe** - Automatically skips session operations in CLI mode to prevent errors\n- **Binary Data Support** - Automatic Base64 encoding fallback for problematic session data\n- **Tracy Debugger Integration** - Logs errors to Tracy when available\n- **Custom Table Names** - Configurable database table name for multi-tenant applications\n\n## 🏗️ Architecture Overview\n\nThe package consists of three main components that work together to provide database session storage:\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     Application Layer                           │\n├─────────────────────────────────────────────────────────────────┤\n│                                                                 │\n│   Native PHP                          Nette Framework           │\n│   ───────────                         ───────────────           │\n│   session_set_save_handler()          SessionExtension          │\n│           │                                  │                  │\n│           │                                  │                  │\n│           ▼                                  ▼                  │\n│   ┌───────────────────────────────────────────────────┐        │\n│   │              SessionStorage                        │        │\n│   │         implements \\SessionHandlerInterface        │        │\n│   │                                                    │        │\n│   │  • open()    - Initialize connection               │        │\n│   │  • close()   - Cleanup                             │        │\n│   │  • read()    - Load session data                   │        │\n│   │  • write()   - Persist session data                │        │\n│   │  • destroy() - Remove session                      │        │\n│   │  • gc()      - Garbage collection                  │        │\n│   └───────────────────────────────────────────────────┘        │\n│                           │                                     │\n│                           │ PDO                                 │\n│                           ▼                                     │\n│   ┌───────────────────────────────────────────────────┐        │\n│   │              MySQL Database                        │        │\n│   │         core__session_storage table                │        │\n│   │                                                    │        │\n│   │  • id (varchar 26) - Session identifier            │        │\n│   │  • haystack (longtext) - Serialized data           │        │\n│   │  • last_update (datetime) - Last modification      │        │\n│   └───────────────────────────────────────────────────┘        │\n│                                                                 │\n│   Optional: SessionEntity (Doctrine ORM mapping)                │\n│                                                                 │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## 🧩 Components\n\n### SessionStorage\n\nThe core component implementing PHP's `\\SessionHandlerInterface`. It handles all session operations:\n\n- **Connection Management**: Creates a dedicated PDO connection with proper character encoding (UTF-8) and error handling\n- **Session Reading**: Retrieves session data from database, handles Base64-encoded data transparently\n- **Session Writing**: Persists session data with automatic timestamp updates, falls back to Base64 encoding for binary data\n- **Session Destruction**: Removes session records from the database\n- **Garbage Collection**: Probabilistic cleanup (0.1% chance per request) removing sessions older than 14 days, limited to 500 records per run\n\n### SessionExtension\n\nA Nette Framework DI extension that provides:\n\n- **Automatic Service Registration**: Registers `SessionStorage` as a service\n- **Doctrine Integration**: Automatically registers entity paths when Doctrine is present\n- **DBAL Connection Reuse**: Can inherit database credentials from existing Doctrine DBAL connection\n- **Session Handler Setup**: Automatically configures Nette's Session to use the database handler\n\n### SessionEntity\n\nA Doctrine ORM entity class providing:\n\n- **Entity Mapping**: Proper ORM annotations for the session table\n- **Type Safety**: Strongly typed properties for session data\n- **Immutable Timestamps**: Uses `DateTimeImmutable` for the last update field\n\n## 📦 Installation\n\nIt's best to use [Composer](https://getcomposer.org) for installation, and you can also find the package on\n[Packagist](https://packagist.org/packages/baraja-core/session) and\n[GitHub](https://github.com/baraja-core/session).\n\nTo install, simply use the command:\n\n```shell\n$ composer require baraja-core/session\n```\n\nYou can use the package manually by creating an instance of the internal classes, or register a DIC extension to link the services directly to the Nette Framework.\n\n### Requirements\n\n- PHP 8.1 or higher\n- PDO extension (`ext-PDO`)\n- MySQL database\n\n## 🗄️ Database Setup\n\nCreate the database table `core__session_storage` (table name can be configured) or use Doctrine for automatic schema generation.\n\n### MySQL Table Schema\n\n```sql\nSET NAMES utf8;\nSET time_zone = '+00:00';\nSET foreign_key_checks = 0;\nSET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';\n\nCREATE TABLE `core__session_storage` (\n  `id` varchar(26) COLLATE utf8_unicode_ci NOT NULL,\n  `haystack` longtext COLLATE utf8_unicode_ci NOT NULL,\n  `last_update` datetime DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;\n```\n\n\u003e **Note**: The table can be used with Doctrine ORM or as a standalone MySQL table.\n\n### Using Doctrine Migrations\n\nIf you're using Doctrine ORM, the schema can be generated automatically from the `SessionEntity` class. Simply run your standard Doctrine migration commands:\n\n```shell\n$ php bin/console doctrine:schema:update --force\n```\n\n## ⚙️ Configuration\n\n### Native PHP Usage\n\nFor standalone PHP applications, create a `SessionStorage` instance and register it as the session handler:\n\n```php\n$handler = new \\Baraja\\Session\\SessionStorage(\n   '127.0.0.1',      // MySQL host\n   'my_application', // Database name\n   'root',           // Username\n   '****'            // Password\n);\n\nsession_set_save_handler($handler);\nsession_start();\n```\n\n\u003e **Warning**: The session handler must be registered **before** calling `session_start()`!\n\n### Nette Framework Usage\n\nRegister the extension in your NEON configuration file:\n\n```yaml\nextensions:\n   barajaPdoSession: Baraja\\Session\\SessionExtension\n```\n\nThe session storage will be configured automatically.\n\n#### Manual Configuration\n\nIf you need to specify database credentials explicitly:\n\n```yaml\nextensions:\n   barajaPdoSession: Baraja\\Session\\SessionExtension\n\nbarajaPdoSession:\n   host: 127.0.0.1\n   dbName: my_application\n   username: root\n   password: ****\n   table: core__session_storage  # optional, this is the default\n```\n\n#### Automatic Doctrine DBAL Integration\n\nWhen using the `baraja-core/doctrine` package, the extension automatically inherits database credentials from your existing DBAL connection. No additional configuration is required!\n\n## 🔧 Advanced Configuration\n\n### Custom Table Name\n\nYou can customize the database table name in two ways:\n\n**1. Via Constructor Parameter:**\n\n```php\n$handler = new \\Baraja\\Session\\SessionStorage(\n   'localhost',\n   'mydb',\n   'user',\n   'password',\n   'my_custom_sessions_table'  // Custom table name\n);\n```\n\n**2. Via Setter Method:**\n\n```php\n$handler = new \\Baraja\\Session\\SessionStorage(/* ... */);\n$handler-\u003esetTable('my_custom_sessions_table');\n```\n\n**3. Via NEON Configuration (Nette):**\n\n```yaml\nbarajaPdoSession:\n   table: my_custom_sessions_table\n```\n\n\u003e **Note**: The default table name is `core__session_storage`. While the table name can be changed at runtime, it is not recommended.\n\n### Garbage Collection\n\nThe garbage collector runs automatically with a 0.1% probability on each request. It:\n\n- Removes sessions that haven't been updated in 14 days\n- Processes a maximum of 500 records per run to prevent long-running queries\n- Is skipped entirely in CLI mode\n\n## 🔒 Security Considerations\n\n### Session Data Encoding\n\nWhen session data contains characters incompatible with MySQL's UTF-8 encoding, the package automatically:\n\n1. Detects the encoding failure via PDO exception\n2. Re-encodes the data using Base64 with a `_BASE:` prefix\n3. Transparently decodes on read\n\nThis ensures binary data and special characters are safely stored without data loss.\n\n### CLI Mode Protection\n\nSession operations are automatically skipped in CLI mode (detected by absence of `$_SERVER['REMOTE_ADDR']`). This prevents:\n\n- Errors when running console commands\n- Unnecessary database connections in CLI scripts\n- Accidental session manipulation from cron jobs\n\n### Error Handling\n\n- Database query failures throw `\\RuntimeException` with helpful error messages\n- Session corruption errors are logged to Tracy (if available) and display a user-friendly error message\n- Failed session writes gracefully degrade without crashing the application\n\n## 🔍 How It Works\n\n### Session Lifecycle\n\n1. **Initialization**: When a request arrives, `SessionStorage` creates a PDO connection\n2. **Reading**: The `read()` method fetches session data by ID, creating a new record if none exists\n3. **Processing**: Your application uses `$_SESSION` as normal\n4. **Writing**: At request end, `write()` updates the session record with new data and timestamp\n5. **Cleanup**: GC randomly triggers to remove old sessions (0.1% probability)\n\n### Data Flow\n\n```\n┌─────────────┐     ┌──────────────────┐     ┌──────────────┐\n│   Request   │────▶│  SessionStorage  │────▶│   Database   │\n│             │     │                  │     │              │\n│  read SID   │     │  SELECT by ID    │     │  Return row  │\n└─────────────┘     └──────────────────┘     └──────────────┘\n      │                     │                       │\n      │                     ▼                       │\n      │             ┌──────────────────┐           │\n      │             │ Decode haystack  │           │\n      │             │ (Base64 if needed)│          │\n      │             └──────────────────┘           │\n      │                     │                       │\n      ▼                     ▼                       ▼\n┌─────────────┐     ┌──────────────────┐     ┌──────────────┐\n│  Response   │◀────│  SessionStorage  │◀────│   Database   │\n│             │     │                  │     │              │\n│ write data  │     │  UPDATE record   │     │  Store row   │\n└─────────────┘     └──────────────────┘     └──────────────┘\n```\n\n### Retry Mechanism\n\nThe `loadById()` method includes a retry mechanism (up to 5 attempts) for handling race conditions when creating new session records. This ensures reliability under concurrent access.\n\n## 📚 API Reference\n\n### SessionStorage\n\n```php\nclass SessionStorage implements \\SessionHandlerInterface\n{\n    public function __construct(\n        string $host,\n        string $dbName,\n        string $username,\n        ?string $password = null,\n        ?string $table = null,\n    );\n\n    public function setTable(string $table): void;\n    public function open($savePath, $sessionName): bool;\n    public function close(): bool;\n    public function read($id): string;\n    public function write($id, $data): bool;\n    public function destroy($id): bool;\n    public function gc($maxlifetime): int|false;\n}\n```\n\n### SessionExtension (Nette)\n\nConfiguration schema:\n\n| Option     | Type     | Required | Default                | Description           |\n|------------|----------|----------|------------------------|-----------------------|\n| `host`     | `string` | No*      | -                      | MySQL host            |\n| `dbName`   | `string` | No*      | -                      | Database name         |\n| `username` | `string` | No*      | -                      | Database username     |\n| `password` | `string` | No*      | -                      | Database password     |\n| `table`    | `string` | No       | `core__session_storage`| Table name            |\n\n\\* Required unless Doctrine DBAL is available (credentials are then inherited automatically).\n\n## 🐛 Troubleshooting\n\n### \"Session was corrupted\" Error\n\nThis error appears when session data cannot be written due to encoding issues. The package attempts to re-encode using Base64, but if this also fails:\n\n1. Check your MySQL character set configuration\n2. Ensure the `haystack` column uses `utf8_unicode_ci` or `utf8mb4_unicode_ci`\n3. Clear corrupted sessions: `DELETE FROM core__session_storage WHERE id = 'problematic_id'`\n\n### \"mb_substr\" Function Not Available\n\nThe error `Function \"mb_substr\" is not available` indicates the `mbstring` extension is not installed. Install it:\n\n```shell\n# Debian/Ubuntu\nsudo apt-get install php-mbstring\n\n# CentOS/RHEL\nsudo yum install php-mbstring\n```\n\n### Session Not Persisting\n\n1. Verify the handler is registered before `session_start()`\n2. Check database credentials and connectivity\n3. Ensure the session table exists with correct schema\n4. Verify you're not in CLI mode (sessions are disabled in CLI)\n\n## 👤 Author\n\n**Jan Barášek**\n\n- Website: [https://baraja.cz](https://baraja.cz)\n- GitHub: [https://github.com/baraja-core](https://github.com/baraja-core)\n\n## 📄 License\n\n`baraja-core/session` is licensed under the MIT license. See the [LICENSE](https://github.com/baraja-core/session/blob/master/LICENSE) file for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaraja-core%2Fsession","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbaraja-core%2Fsession","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaraja-core%2Fsession/lists"}