{"id":28894911,"url":"https://github.com/cipherstash/protectphp","last_synced_at":"2026-02-01T06:31:52.902Z","repository":{"id":299396309,"uuid":"999067151","full_name":"cipherstash/protectphp","owner":"cipherstash","description":"Field-level encryption for PHP with searchable encrypted data","archived":false,"fork":false,"pushed_at":"2025-07-26T19:54:42.000Z","size":25,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-10T17:16:15.089Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://cipherstash.com","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/cipherstash.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","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}},"created_at":"2025-06-09T17:28:24.000Z","updated_at":"2025-07-25T15:12:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"98584a99-b3db-42dd-825f-33202022246e","html_url":"https://github.com/cipherstash/protectphp","commit_stats":null,"previous_names":["cipherstash/protectphp"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cipherstash/protectphp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cipherstash%2Fprotectphp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cipherstash%2Fprotectphp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cipherstash%2Fprotectphp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cipherstash%2Fprotectphp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cipherstash","download_url":"https://codeload.github.com/cipherstash/protectphp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cipherstash%2Fprotectphp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28970526,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T05:48:53.985Z","status":"ssl_error","status_checked_at":"2026-02-01T05:47:55.855Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2025-06-21T04:15:17.065Z","updated_at":"2026-02-01T06:31:52.896Z","avatar_url":"https://github.com/cipherstash.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://cipherstash.com\"\u003e\n        \u003cimg alt=\"CipherStash Logo\" width=\"160\" height=\"160\" src=\"https://cipherstash.com/assets/cs-github.png\"\u003e\n    \u003c/a\u003e\n    \u003ch1\u003eProtect.php\u003c/h1\u003e\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n    Implement robust data security without sacrificing performance or usability\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://cipherstash.com\"\u003e\u003cimg\n        alt=\"Built by CipherStash\"\n        src=\"https://raw.githubusercontent.com/cipherstash/meta/refs/heads/main/csbadge.svg\"\n    /\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/cipherstash/protectphp/blob/main/LICENSE.md\"\u003e\u003cimg\n        alt=\"License\"\n        src=\"https://img.shields.io/github/license/cipherstash/protectphp.svg?style=for-the-badge\u0026labelColor=000000\"\n    /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nProtect.php brings field-level encryption to your PHP applications. Store encrypted data in any JSONB-compatible database while maintaining searchability on PostgreSQL. Encryption happens directly in your application using a unique key for each value, managed by CipherStash [ZeroKMS](https://cipherstash.com/products/zerokms) and backed by [AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html).\n\n## Installation\n\nInstall Protect.php via Composer:\n\n```bash\ncomposer require cipherstash/protectphp\n```\n\n## Requirements\n\n- PHP 8.1 or higher\n\n## Configuration\n\nBefore using Protect.php, you must configure your CipherStash credentials. Set these environment variables in your application:\n\n```bash\nCS_CLIENT_ID=your-client-id\nCS_CLIENT_ACCESS_KEY=your-client-access-key\nCS_CLIENT_KEY=your-client-key\nCS_WORKSPACE_CRN=your-workspace-crn\n```\n\nCredentials can be generated by logging in or signing up for CipherStash and setting up a new workspace via the [CipherStash CLI](https://cipherstash.com/docs/sdk/how-to/cli) or [CipherStash Dashboard](https://dashboard.cipherstash.com/).\n\n## Quick Start\n\nEncrypt and decrypt data with just a few lines of code:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$field = 'users.email';\n$value = 'john@example.com';\n\n$encrypted = Protect::encrypt($field, $value);\n$decrypted = Protect::decrypt($encrypted); // john@example.com\n```\n\n## Integrations\n\n- Laravel (coming soon)\n\n## Database Setup\n\nProtect.php works with any database that supports JSONB storage. The encrypted data is structured as an [Encrypt Query Language (EQL)](https://github.com/cipherstash/encrypt-query-language) JSON payload.\n\nFor advanced querying capabilities (searching, sorting, filtering), you'll need PostgreSQL with the EQL extension. EQL provides the `eql_v2_encrypted` type:\n\n```sql\nCREATE TABLE users (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    email eql_v2_encrypted,\n    name eql_v2_encrypted,\n    balance eql_v2_encrypted,\n    notes eql_v2_encrypted,\n    contact eql_v2_encrypted,\n    CONSTRAINT unique_email UNIQUE ((email-\u003e\u003e'hm')) -- Enforce unique emails\n);\n```\n\nSee the [EQL installation instructions](https://github.com/cipherstash/encrypt-query-language#installation) to get started.\n\n## Encrypting Data\n\nEncrypt values for specific table columns using the `Protect::encrypt()` method. This method accepts the field name in dot notation, value, and optional configuration:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$field = 'users.email';\n$value = 'john@example.com';\n\n$encrypted = Protect::encrypt($field, $value);\n```\n\nThe `Protect::encrypt()` method automatically handles data type conversion and applies sensible default indexes based on the PHP data type. You can customize the data type and indexing behavior by providing configuration options:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$field = 'users.email';\n$value = 'john@example.com';\n\n$options = [\n    'cast_as' =\u003e 'string',\n    'indexes' =\u003e [\n        'unique' =\u003e [],\n        'ore' =\u003e [],\n        'match' =\u003e [],\n    ],\n];\n\n$encrypted = Protect::encrypt($field, $value, $options);\n```\n\n## Decrypting Data\n\nDecrypt an encrypted envelope back to its original value using the `Protect::decrypt()` method. This method accepts the encrypted envelope array returned from encryption and optional configuration:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$field = 'users.email';\n$value = 'john@example.com';\n\n$encrypted = Protect::encrypt($field, $value);\n$decrypted = Protect::decrypt($encrypted); // john@example.com\n```\n\nThe `Protect::decrypt()` method automatically converts the decrypted data back to the original PHP data type. You can customize the data type conversion by providing configuration options:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$field = 'users.balance';\n$value = 1575000;\n\n$options = [\n    'cast_as' =\u003e 'string',\n];\n\n$encrypted = Protect::encrypt($field, $value, $options);\n$decrypted = Protect::decrypt($encrypted, $options); // \"1575000\"\n```\n\n## Bulk Operations\n\nFor improved performance when handling multiple records, use bulk encryption and decryption operations.\n\n### Bulk Encryption\n\nEncrypt multiple values using the `Protect::encryptAttributes()` method. This method accepts the table name and an associative array of column names and values:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$attributes = [\n    'email' =\u003e 'john@example.com',\n    'name' =\u003e 'John Doe',\n    'balance' =\u003e 1575000,\n    'notes' =\u003e 'Account flagged for fraud monitoring after suspicious transaction pattern detected. Customer disputed charges on 2007-07-27. Priority support required for high-value client.',\n    'contact' =\u003e [\n        'phone' =\u003e '15551234567',\n        'mailing_address' =\u003e [\n            'street' =\u003e '742 Evergreen Terrace',\n            'city' =\u003e 'Springfield',\n            'state' =\u003e 'OR',\n            'zip' =\u003e '97403',\n        ],\n    ],\n];\n\n$encrypted = Protect::encryptAttributes('users', $attributes);\n```\n\nThe `Protect::encryptAttributes()` method automatically handles data type conversion and applies sensible default indexes based on the PHP data type for each column. You can provide per-column configuration options:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$attributes = [\n    'email' =\u003e 'john@example.com',\n    'name' =\u003e 'John Doe',\n    'balance' =\u003e 1575000,\n    'notes' =\u003e 'Account flagged for fraud monitoring after suspicious transaction pattern detected. Customer disputed charges on 2007-07-27. Priority support required for high-value client.',\n    'contact' =\u003e [\n        'phone' =\u003e '15551234567',\n        'mailing_address' =\u003e [\n            'street' =\u003e '742 Evergreen Terrace',\n            'city' =\u003e 'Springfield',\n            'state' =\u003e 'OR',\n            'zip' =\u003e '97403',\n        ],\n    ],\n];\n\n$options = [\n    'email' =\u003e [\n        'indexes' =\u003e [\n            'unique' =\u003e [],\n            'ore' =\u003e [],\n            'match' =\u003e [],\n        ],\n    ],\n    'name' =\u003e [\n        'indexes' =\u003e [\n            'unique' =\u003e [],\n            'ore' =\u003e [],\n            'match' =\u003e [],\n        ],\n    ],\n    'notes' =\u003e [\n        'indexes' =\u003e [\n            'match' =\u003e [],\n        ],\n    ],\n];\n\n$encrypted = Protect::encryptAttributes('users', $attributes, $options);\n```\n\n### Bulk Decryption\n\nDecrypt multiple encrypted envelopes using the `Protect::decryptAttributes()` method. This method accepts the table name and an associative array of column names and their corresponding encrypted envelopes:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$attributes = [\n    'email' =\u003e 'john@example.com',\n    'name' =\u003e 'John Doe',\n    'balance' =\u003e 1575000,\n    'notes' =\u003e 'Account flagged for fraud monitoring after suspicious transaction pattern detected. Customer disputed charges on 2007-07-27. Priority support required for high-value client.',\n    'contact' =\u003e [\n        'phone' =\u003e '15551234567',\n        'mailing_address' =\u003e [\n            'street' =\u003e '742 Evergreen Terrace',\n            'city' =\u003e 'Springfield',\n            'state' =\u003e 'OR',\n            'zip' =\u003e '97403',\n        ],\n    ],\n];\n\n$encrypted = Protect::encryptAttributes('users', $attributes);\n$decrypted = Protect::decryptAttributes('users', $encrypted);\n```\n\nThe `Protect::decryptAttributes()` method automatically converts the decrypted data back to the original PHP data types. You can customize the data type conversion by providing per-column configuration options:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$attributes = [\n    'email' =\u003e 'john@example.com',\n    'name' =\u003e 'John Doe',\n    'balance' =\u003e 1575000,\n    'notes' =\u003e 'Account flagged for fraud monitoring after suspicious transaction pattern detected. Customer disputed charges on 2007-07-27. Priority support required for high-value client.',\n    'contact' =\u003e [\n        'phone' =\u003e '15551234567',\n        'mailing_address' =\u003e [\n            'street' =\u003e '742 Evergreen Terrace',\n            'city' =\u003e 'Springfield',\n            'state' =\u003e 'OR',\n            'zip' =\u003e '97403',\n        ],\n    ],\n];\n\n$options = [\n    'balance' =\u003e [\n        'cast_as' =\u003e 'string',\n    ],\n];\n\n$encrypted = Protect::encryptAttributes('users', $attributes, $options);\n$decrypted = Protect::decryptAttributes('users', $encrypted, $options);\n```\n\n## Searchable Encryption\n\nCreate search terms that enable querying encrypted data without decryption using the `Protect::createSearchTerms()` method. This method accepts an associative array where keys are field names in dot notation and values are the data to search:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$fields = [\n    'users.email' =\u003e 'john@example.com',\n    'users.balance' =\u003e 1575000,\n];\n\n$searchTerms = Protect::createSearchTerms($fields);\n```\n\nThe `Protect::createSearchTerms()` method automatically applies sensible default indexes based on the PHP data types, ensuring search terms match the indexes used for encrypted data with the same defaults. You can provide per-field configuration options:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$fields = [\n    'users.email' =\u003e 'john@example.com',\n    'users.balance' =\u003e 1575000,\n];\n\n$options = [\n    'users.email' =\u003e [\n        'indexes' =\u003e [\n            'unique' =\u003e [],\n            'ore' =\u003e [],\n            'match' =\u003e [],\n        ],\n    ],\n];\n\n$searchTerms = Protect::createSearchTerms($fields, $options);\n```\n\nThis feature integrates with [EQL](https://github.com/cipherstash/encrypt-query-language) and is currently only supported on PostgreSQL databases.\n\n### Querying with Search Terms\n\nThese examples demonstrate how to use search terms with PostgreSQL and EQL for querying encrypted data without decryption. Each query uses the complete search terms object, and EQL automatically selects the appropriate index for the query operation.\n\n#### Exact Equality Queries\n\nFor exact equality queries, EQL uses the `unique` index (`hm` response parameter) from your search terms:\n\n```sql\n-- Find user record by email address\n-- Using search terms (encrypted ahead of time, plaintext not loggable):\nSELECT * FROM users\nWHERE email = '{\"hm\":\"f3ca71fd39ae9d3d1d1fc25141bcb6da...\",\"ob\":[\"57e58bb3ebd195a5cdd5b77902732a6a...\"],\"bf\":[1124,2134,987,1456,743,2201],\"i\":{\"t\":\"users\",\"c\":\"email\"}}'::jsonb;\n```\n\n#### Equality, Range, and Sorting Queries\n\nFor equality, range comparisons, and sorting, EQL uses the `ore` index (`ob` response parameter) from your search terms:\n\n```sql\n-- Find users with exact balance amount\n-- Using search terms (encrypted ahead of time, plaintext not loggable):\nSELECT * FROM users\nWHERE balance = '{\"hm\":null,\"ob\":[\"99f7adadadadadadc68b2822197a849e...\"],\"bf\":null,\"i\":{\"t\":\"users\",\"c\":\"balance\"}}'::jsonb;\n\n-- Find users above specified balance\n-- Using search terms (encrypted ahead of time, plaintext not loggable):\nSELECT * FROM users\nWHERE balance \u003e= '{\"hm\":null,\"ob\":[\"99f7adadadadadadc68b2822197a849e...\"],\"bf\":null,\"i\":{\"t\":\"users\",\"c\":\"balance\"}}'::jsonb;\n\n-- Find users with balance in specified range\n-- Using search terms (encrypted ahead of time, plaintext not loggable):\nSELECT * FROM users\nWHERE balance BETWEEN\n      '{\"hm\":null,\"ob\":[\"99f7adadadadadadc68b2822197a849e...\"],\"bf\":null,\"i\":{\"t\":\"users\",\"c\":\"balance\"}}'::jsonb\n  AND '{\"hm\":null,\"ob\":[\"99f7adadadadadadc68b2822197a849e...\"],\"bf\":null,\"i\":{\"t\":\"users\",\"c\":\"balance\"}}'::jsonb;\n\n-- Order users by balance from lowest to highest\nSELECT * FROM users\nORDER BY balance ASC;\n\n-- Order users by balance from highest to lowest\nSELECT * FROM users\nORDER BY balance DESC;\n```\n\n#### Full-Text Search Queries\n\nFor searching within text content, EQL uses the `match` index (`bf` response parameter) from your search terms:\n\n```sql\n-- Find users with notes containing specified terms\n-- Using search terms (encrypted ahead of time, plaintext not loggable):\nSELECT * FROM users\nWHERE notes LIKE '{\"hm\":null,\"ob\":null,\"bf\":[1397,378,1463,1673,1474,1226],\"i\":{\"t\":\"users\",\"c\":\"notes\"}}'::jsonb;\n```\n\n#### JSONB Containment Queries\n\nFor structured data queries, EQL uses the `ste_vec` index (`sv` response parameter) from your search terms:\n\n```sql\n-- Find users where contact contains specified values\n-- Using search terms (encrypted ahead of time, plaintext not loggable):\nSELECT * FROM users\nWHERE contact @\u003e '{\"sv\":[{\"s\":\"dd4659b9c279af040dd05ce21b2a22f7...\",\"t\":\"22303061363334333330316661653633...\",\"r\":\"mBbL}QHJ\u0026a(@rwS5n)u^G+Fb+t}Soo-h...\",\"pa\":false}],\"i\":{\"t\":\"users\",\"c\":\"contact\"}}'::jsonb;\n\n-- Find users where contact is contained by specified values\n-- Using search terms (encrypted ahead of time, plaintext not loggable):\nSELECT * FROM users\nWHERE contact \u003c@ '{\"sv\":[{\"s\":\"df08a4c4157bdb5bf6fa9be89cf18d10...\",\"t\":\"22303063343133306135646334356130...\",\"r\":\"mBbL}QHJ\u0026a(@rwS5n)u^G+Fb+Ex8ofB!...\",\"pa\":false}],\"i\":{\"t\":\"users\",\"c\":\"contact\"}}'::jsonb;\n```\n\n## Options\n\nThe Protect.php library provides configuration options to customize encryption behavior, indexing strategies, and contextual security. Each method supports the following options:\n\n| Option | Type | Default | Description | `Protect::encrypt()` | `Protect::decrypt()` | `Protect::encryptAttributes()` | `Protect::decryptAttributes()` | `Protect::createSearchTerms()` |\n|--------|------|---------|-------------|-----------|-----------|---------------------|---------------------|-------------------|\n| `cast_as` | `string` | Auto-detected | Override auto-detected PHP data type | ✓ | ✓ | ✓ | ✓ | ✓ |\n| `indexes` | `array` | Type-based | Configure query capabilities | ✓ | ✗ | ✓ | ✗ | ✓ |\n| `context` | `array\\|null` | `null` | Bind encryption to specific context | ✓ | ✓ | ✓ | ✓ | ✓ |\n| `skip` | `boolean` | `false` | Skip processing for specific columns | ✗ | ✗ | ✓ | ✓ | ✗ |\n\nOptions not supported by a specific method are silently ignored, allowing you to use the same configuration across different operations.\n\n### Data Types\n\nThe `cast_as` parameter determines how data is processed and converted during encryption and decryption. In most cases, you can rely on automatic type detection, but you can specify the PHP data type when needed:\n\n| Type | Description | Example |\n|------|-------------|---------------|\n| `string` | Text data supporting exact matching and full-text search capabilities | `john@example.com` |\n| `bool` | Binary state values for exact equality queries only | `true` or `false` |\n| `int` | Whole numbers enabling range queries and mathematical ordering | `2147483647` |\n| `float` | Decimal values supporting precise range and ordering operations | `25.99` |\n| `date` | Temporal data enabling date range queries and chronological sorting | `new DateTime('2020-11-10')` |\n| `array` | Structured data supporting containment and relationship queries | `['foo', 'bar']` |\n\nBasic usage:\n\n```php\n$options = [\n    'cast_as' =\u003e 'string',\n];\n```\n\n### Index Types\n\nThe `indexes` parameter determines what queries are supported on encrypted data:\n\n| Index Type | Description | Applied by Default | Response Parameter | Supported Queries |\n|------------|-------------|-------------------|-------------------|------------------|\n| `unique` | Exact equality queries and uniqueness constraints | `string`, `bool` | `hm` | `=` |\n| `ore` | Equality, range comparisons, range queries, and ordering | `string`, `int`, `float`, `date` | `ob` | `=`, `\u003e`, `\u003c`, `BETWEEN`, `ORDER BY` |\n| `match` | Full-text search queries | - | `bf` | `LIKE` |\n| `ste_vec` | JSONB containment queries | - | `sv` | `@\u003e`, `\u003c@` |\n\n#### Unique Index (`unique`)\n\nEnables exact equality queries and database uniqueness constraints. Uses the `hm` response parameter to generate HMAC-based hashes for exact equality matching.\n\nBasic usage:\n\n```php\n$options = [\n    'indexes' =\u003e [\n        'unique' =\u003e [], // Uses defaults\n    ],\n];\n```\n\nConfiguration parameters:\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `token_filters` | `array` | ✗ | `[]` | Text processing filters applied before hashing |\n| `token_filters[].kind` | `string` | ✗ | - | Filter type: `downcase` to convert to lowercase |\n\nWith custom parameters:\n\n```php\n$options = [\n    'indexes' =\u003e [\n        'unique' =\u003e [\n            'token_filters' =\u003e [\n                ['kind' =\u003e 'downcase'],\n            ],\n        ],\n    ],\n];\n```\n\nFor database-level uniqueness constraints, add a unique constraint on the `hm` response parameter:\n\n```sql\nCONSTRAINT unique_email UNIQUE ((email-\u003e\u003e'hm'))\n```\n\n#### Order Revealing Encryption Index (`ore`)\n\nEnables equality, range operations, and ordering on encrypted data. Uses the `ob` response parameter to create order-preserving encrypted values for equality checks, range comparisons, and sorting operations.\n\nBasic usage:\n\n```php\n$options = [\n    'indexes' =\u003e [\n        'ore' =\u003e [],\n    ],\n];\n```\n\n*This index type has no configurable parameters.*\n\n#### Match Index (`match`)\n\nEnables full-text search on encrypted text data using bloom filters. Uses the `bf` response parameter to create bloom filter representations of tokenized text for probabilistic matching.\n\nBasic usage:\n\n```php\n$options = [\n    'indexes' =\u003e [\n        'match' =\u003e [], // Uses defaults\n    ],\n];\n```\n\nConfiguration parameters:\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `tokenizer` | `object` | ✗ | `['kind' =\u003e 'standard']` | Text tokenization method |\n| `tokenizer.kind` | `string` | ✗ | `standard` | Tokenizer type: `standard` or `ngram` |\n| `tokenizer.token_length` | `integer` | ✗ | `3` | Token length for ngram tokenizer |\n| `token_filters` | `array` | ✗ | `[]` | Text processing filters |\n| `token_filters[].kind` | `string` | ✗ | - | Filter type: `downcase` |\n| `k` | `integer` | ✗ | `6` | Hash function count for bloom filter |\n| `m` | `integer` | ✗ | `2048` | Bloom filter size in bits |\n| `include_original` | `boolean` | ✗ | `false` | Include original text in search results |\n\nWith custom parameters:\n\n```php\n$options = [\n    'indexes' =\u003e [\n        'match' =\u003e [\n            'tokenizer' =\u003e [\n                'kind' =\u003e 'ngram',\n                'token_length' =\u003e 3,\n            ],\n            'token_filters' =\u003e [\n                ['kind' =\u003e 'downcase'],\n            ],\n            'k' =\u003e 8,\n            'm' =\u003e 1024,\n            'include_original' =\u003e true,\n        ],\n    ],\n];\n```\n\n#### Structured Text Encryption Vector Index (`ste_vec`)\n\nEnables containment queries on encrypted JSONB data. Uses the `sv` response parameter to create structured text encryption vectors that preserve JSON path relationships for encrypted JSONB containment matching.\n\nBasic usage:\n\n```php\n$options = [\n    'indexes' =\u003e [\n        'ste_vec' =\u003e [\n            'prefix' =\u003e 'users.contact',\n        ],\n    ],\n];\n```\n\nConfiguration parameters:\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `prefix` | `string` | ✓ | - | Domain separator for cryptographic hashing that must be unique per column (recommended format is `table.column`) |\n\n### Encryption Context\n\nProvide additional encryption context for an additional layer of security by binding encrypted data to specific contextual information of your choosing. This prevents data encrypted with one context from being decrypted with a different context, even when using the same encryption keys.\n\n#### Context Types\n\nThe `context` parameter determines what contextual authentication is supported:\n\n| Context Type | Description | Supported Index Types |\n|--------------|-------------|----------------------|\n| `identity_claim` | Identity-aware encryption using JWT claims (requires CTS authentication) | `unique`, `ore`, `match` |\n| `tag` | Label-aware encryption using string tags | `unique`, `ore`, `match` |\n| `value` | Attribute-aware encryption using key-value pairs | `unique`, `ore`, `match` |\n\n\u003e [!IMPORTANT]\n\u003e Encryption context is not supported with `ste_vec` indexes and will cause decryption to fail.\n\n#### Identity Claim Context\n\nIdentity claim context binds encrypted data to specific user identities using JWT claims. This enables identity-aware encryption where data can only be decrypted by authenticated users who match the identity criteria.\n\nIdentity claim context requires [CipherStash Token Service (CTS)](https://cipherstash.com/docs/cts/about) authentication for both encryption and decryption operations.\n\n\u003e [!NOTE]\n\u003e While identity claim context is supported by passing JWT claims in the `context.identity_claim` parameter, automatic CTS authentication is not yet implemented. This means you'll need to manually obtain CTS tokens by calling the CTS API directly. If automatic CTS integration would be valuable for your use case, please open an issue and let us know. Community feedback helps us prioritize new features.\n\n#### Tag Context\n\nTag context binds encrypted data to specific string labels. This enables label-aware encryption where data can only be decrypted when the same tag context is provided:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$field = 'users.email';\n$value = 'john@example.com';\n\n$options = [\n    'context' =\u003e [\n        'tag' =\u003e ['pii'],\n    ],\n];\n\n$encrypted = Protect::encrypt($field, $value, $options);\n$decrypted = Protect::decrypt($encrypted, $options); // john@example.com\n```\n\n#### Value Context\n\nValue context binds encrypted data to specific key-value pairs. This enables attribute-aware encryption where data can only be decrypted when the same value context is provided:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$field = 'users.email';\n$value = 'john@example.com';\n\n$options = [\n    'context' =\u003e [\n        'value' =\u003e [\n            ['key' =\u003e 'tenant_id', 'value' =\u003e 'tenant_2ynTJf38e9HvuAO8jaX5kAyVaKI'],\n            ['key' =\u003e 'role', 'value' =\u003e 'admin'],\n        ],\n    ],\n];\n\n$encrypted = Protect::encrypt($field, $value, $options);\n$decrypted = Protect::decrypt($encrypted, $options); // john@example.com\n```\n\n\u003e [!WARNING]\n\u003e You must use the same context for both encryption and decryption operations. Wrong contexts will result in decryption failures.\n\n### Skip Processing\n\nThe `skip` parameter enables selective processing control for specific columns in bulk operations. When set to `true`, the specified column bypasses encryption and/or decryption while other columns are processed normally:\n\n```php\nuse CipherStash\\Protect\\Protect;\n\n$attributes = [\n    'email' =\u003e 'john@example.com',\n    'name' =\u003e 'John Doe',\n    'balance' =\u003e 1575000,\n];\n\n$options = [\n    'balance' =\u003e ['skip' =\u003e true],\n];\n\n$encrypted = Protect::encryptAttributes('users', $attributes, $options);\n$decrypted = Protect::decryptAttributes('users', $encrypted, $options);\n```\n\n## Reference\n\n### Encryption Response\n\nThe `Protect::encrypt()` method returns an encrypted envelope array. The response format depends on the configured indexes.\n\n#### Standard Indexes Response\n\nFor columns configured with the `unique`, `ore`, and/or `match` indexes:\n\n```php\n[\n    'k' =\u003e 'ct',\n    'c' =\u003e 'mBbKlk}G7QdaGiNj$dL7#+AOrA^}*VJx...',\n    'dt' =\u003e 'text',\n    'hm' =\u003e 'f3ca71fd39ae9d3d1d1fc25141bcb6da...',\n    'ob' =\u003e ['57e58bb3ebd195a5cdd5b77902732a6a...'],\n    'bf' =\u003e [1124,2134,987,1456,743,2201],\n    'i' =\u003e [\n        't' =\u003e 'users',\n        'c' =\u003e 'email',\n    ],\n    'v' =\u003e 2,\n]\n```\n\nResponse parameters:\n\n| Parameter | Type | Source | Description |\n|-----------|-----------|--------|-------------|\n| `k` | `string` | Always | Key type identifier (always `ct` for ciphertext) |\n| `c` | `string` | Always | Base85-encoded ciphertext containing the encrypted data |\n| `dt` | `string` | Always | Data type for casting (from `cast_as` configuration parameter) |\n| `hm` | `string\\|null` | `unique` | HMAC index for exact equality queries and uniqueness constraints |\n| `ob` | `array\\|null` | `ore` | Order-revealing encryption index for equality checks, range comparisons, range queries, and sorting operations |\n| `bf` | `array\\|null` | `match` | Bloom filter index for full-text search queries |\n| `i` | `object` | Always | Table and column identifier for this encrypted value: `['t' =\u003e 'table', 'c' =\u003e 'column']` |\n| `v` | `int` | Always | Schema version for backward compatibility |\n\n#### STE Vec Index Response\n\nFor columns configured with the `ste_vec` index:\n\n```php\n[\n    'k' =\u003e 'sv',\n    'c' =\u003e 'mBbLQ2^Io|1eh_K2*n^LSCVVQuGhkL\u003ew...',\n    'dt' =\u003e 'jsonb',\n    'sv' =\u003e [\n        [\n            's' =\u003e 'dd4659b9c279af040dd05ce21b2a22f7...',\n            't' =\u003e '22303061363334333330316661653633...',\n            'r' =\u003e 'mBbLQ2^Io|1eh_K2*n^LSCVVQuGhkL\u003ew...',\n            'pa' =\u003e false,\n        ],\n    ],\n    'i' =\u003e [\n        't' =\u003e 'users',\n        'c' =\u003e 'contact',\n    ],\n    'v' =\u003e 2,\n]\n```\n\nResponse parameters:\n\n| Parameter | Type | Source | Description |\n|-----------|------|--------|-------------|\n| `k` | `string` | Always | Key type identifier (always `sv` for structured vector) |\n| `c` | `string` | Always | Base85-encoded ciphertext containing the encrypted data |\n| `dt` | `string` | Always | Data type for casting (from `cast_as` configuration parameter) |\n| `sv` | `array\\|null` | `ste_vec` | Structured text encryption vector for JSONB containment queries |\n| `sv[].s` | `string` | `ste_vec` | Tokenized selector representing the encrypted JSON path to the value |\n| `sv[].t` | `string` | `ste_vec` | Encrypted term value for equality and order-preserving queries |\n| `sv[].r` | `string` | `ste_vec` | Base85-encoded ciphertext containing the encrypted record data |\n| `sv[].pa` | `boolean` | `ste_vec` | Whether the parent JSON element is an array |\n| `i` | `object` | Always | Table and column identifier for this encrypted value: `['t' =\u003e 'table', 'c' =\u003e 'column']` |\n| `v` | `int` | Always | Schema version for backward compatibility |\n\n### Search Terms Response\n\nThe `Protect::createSearchTerms()` method returns an associative array where keys are field names and values contain search terms with only the encryption indexes (without the full ciphertext). The response format depends on the configured indexes.\n\n#### Standard Indexes Response\n\nFor columns configured with `unique`, `ore`, and/or `match` indexes:\n\n```php\n[\n    'users.email' =\u003e [\n        'hm' =\u003e 'f3ca71fd39ae9d3d1d1fc25141bcb6da...',\n        'ob' =\u003e ['57e58bb3ebd195a5cdd5b77902732a6a...'],\n        'bf' =\u003e [1124,2134,987,1456,743,2201],\n        'i' =\u003e [\n            't' =\u003e 'users',\n            'c' =\u003e 'email',\n        ],\n    ],\n]\n```\n\nResponse parameters:\n\n| Parameter | Type | Source | Description |\n|-----------|------|--------|-------------|\n| `hm` | `string\\|null` | `unique` | HMAC index for exact equality queries and uniqueness constraints |\n| `ob` | `array\\|null` | `ore` | Order-revealing encryption index for equality checks, range comparisons, range queries, and sorting operations |\n| `bf` | `array\\|null` | `match` | Bloom filter index for full-text search queries |\n| `i` | `object` | Always | Table and column identifier for this encrypted value: `['t' =\u003e 'table', 'c' =\u003e 'column']` |\n\n#### STE Vec Index Response\n\nFor columns configured with `ste_vec` indexes:\n\n```php\n[\n    'users.contact' =\u003e [\n        'sv' =\u003e [\n            [\n                's' =\u003e 'dd4659b9c279af040dd05ce21b2a22f7...',\n                't' =\u003e '22303061363334333330316661653633...',\n                'r' =\u003e 'mBbLkCZcaJ2U|G333rRC\u003ef;r}uFEp7Tg...',\n                'pa' =\u003e false,\n            ],\n            [\n                's' =\u003e 'df08a4c4157bdb5bf6fa9be89cf18d10...',\n                't' =\u003e '22303063343133306135646334356130...',\n                'r' =\u003e 'mBbLkCZcaJ2U|G333rRC\u003ef;r}E\u0026d@?`;...',\n                'pa' =\u003e false,\n            ],\n        ],\n        'i' =\u003e [\n            't' =\u003e 'users',\n            'c' =\u003e 'contact',\n        ],\n    ],\n]\n```\n\nResponse parameters:\n\n| Parameter | Type | Source | Description |\n|-----------|------|--------|-------------|\n| `sv` | `array\\|null` | `ste_vec` | Structured text encryption vector for JSONB containment queries |\n| `sv[].s` | `string` | `ste_vec` | Tokenized selector representing the encrypted JSON path to the value |\n| `sv[].t` | `string` | `ste_vec` | Encrypted term value for equality and order-preserving queries |\n| `sv[].r` | `string` | `ste_vec` | Base85-encoded ciphertext containing the encrypted record data |\n| `sv[].pa` | `boolean` | `ste_vec` | Whether the parent JSON element is an array |\n| `i` | `object` | Always | Table and column identifier for this encrypted value: `['t' =\u003e 'table', 'c' =\u003e 'column']` |\n\n## Error Handling\n\nProtect.php operations may throw exceptions when errors occur during library operations. Proper error handling ensures your application can gracefully handle configuration issues, network problems, or invalid data scenarios.\n\n### Exception Types\n\nProtect.php defines several specific exception types for different error conditions. For most use cases, you can catch all exceptions using the base `Exception` or `Throwable`:\n\n```php\nuse CipherStash\\Protect\\Protect;\nuse Throwable;\n\ntry {\n    $encrypted = Protect::encrypt('users.email', 'john@example.com');\n} catch (Throwable $e) {\n    // Handle any errors\n    // ...\n}\n```\n\nFor more granular error handling, you can catch specific exception types:\n\n```php\nuse CipherStash\\Protect\\Exceptions\\EncryptException;\nuse CipherStash\\Protect\\Exceptions\\ValidationException;\nuse CipherStash\\Protect\\Protect;\n\ntry {\n    $encrypted = Protect::encrypt('users.email', 'john@example.com');\n} catch (ValidationException $e) {\n    // Handle validation errors\n    // ...\n} catch (EncryptException $e) {\n    // Handle encryption errors\n    // ...\n}\n```\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcipherstash%2Fprotectphp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcipherstash%2Fprotectphp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcipherstash%2Fprotectphp/lists"}