Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ jobs:
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
redis:
image: redis:6.2
ports:
- 6379:6379
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3

strategy:
fail-fast: true
Expand Down Expand Up @@ -65,4 +70,4 @@ jobs:
- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: npx semantic-release
run: npx semantic-release
115 changes: 106 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,45 @@ Via [Composer](https://getcomposer.org/).
composer require casbin/dbal-adapter
```

### Usage
### Basic Usage (Without Redis Caching)

```php
This section describes how to use the adapter with a direct database connection, without leveraging Redis for caching.

You can initialize the adapter by passing either a Doctrine DBAL connection parameter array or an existing `Doctrine\DBAL\Connection` instance to the `Adapter::newAdapter()` method or the `Adapter` constructor.

**Example:**

```php
require_once './vendor/autoload.php';

use Casbin\Enforcer;
use CasbinAdapter\DBAL\Adapter as DatabaseAdapter;
use Doctrine\DBAL\DriverManager; // Required if creating a new connection object

$config = [
// Either 'driver' with one of the following values:
// pdo_mysql,pdo_sqlite,pdo_pgsql,pdo_oci (unstable),pdo_sqlsrv
// mysqli,sqlanywhere,sqlsrv,ibm_db2 (unstable),drizzle_pdo_mysql
// Option 1: Using DBAL connection parameters array
$dbConnectionParams = [
// Supported drivers: pdo_mysql, pdo_sqlite, pdo_pgsql, pdo_oci, pdo_sqlsrv,
// mysqli, sqlanywhere, sqlsrv, ibm_db2, drizzle_pdo_mysql
'driver' => 'pdo_mysql',
'host' => '127.0.0.1',
'dbname' => 'test',
'dbname' => 'casbin_db', // Your database name
'user' => 'root',
'password' => '',
'port' => '3306',
'port' => '3306', // Optional, defaults to driver's standard port
// 'policy_table_name' => 'casbin_rules', // Optional, defaults to 'casbin_rule'
];

$adapter = DatabaseAdapter::newAdapter($config);
// Initialize the Adapter with the DBAL parameters array (without Redis)
$adapter = DatabaseAdapter::newAdapter($dbConnectionParams);
// Alternatively, using the constructor:
// $adapter = new DatabaseAdapter($dbConnectionParams);

// Option 2: Using an existing Doctrine DBAL Connection instance
// $dbalConnection = DriverManager::getConnection($dbConnectionParams);
// $adapter = DatabaseAdapter::newAdapter($dbalConnection);
// Or using the constructor:
// $adapter = new DatabaseAdapter($dbalConnection);


$e = new Enforcer('path/to/model.conf', $adapter);

Expand All @@ -62,6 +79,86 @@ if ($e->enforce($sub, $obj, $act) === true) {
}
```

### Usage with Redis Caching

To improve performance and reduce database load, the adapter supports caching policy data using [Redis](https://redis.io/). When enabled, Casbin policies will be fetched from Redis if available, falling back to the database if the cache is empty.

To enable Redis caching, provide a Redis configuration array as the second argument when initializing the adapter. The first argument remains your Doctrine DBAL connection (either a parameters array or a `Connection` object).

**Redis Configuration Options:**

* `host` (string): Hostname or IP address of the Redis server. Default: `'127.0.0.1'`.
* `port` (int): Port number of the Redis server. Default: `6379`.
* `password` (string, nullable): Password for Redis authentication. Default: `null`.
* `database` (int): Redis database index. Default: `0`.
* `ttl` (int): Cache Time-To-Live in seconds. Policies stored in Redis will expire after this duration. Default: `3600` (1 hour).
* `prefix` (string): Prefix for all Redis keys created by this adapter. Default: `'casbin_policies:'`.

**Example:**

```php
require_once './vendor/autoload.php';

use Casbin\Enforcer;
use CasbinAdapter\DBAL\Adapter as DatabaseAdapter;
use Doctrine\DBAL\DriverManager; // Required if creating a new connection object

// Database connection parameters (can be an array or a Connection object)
$dbConnectionParams = [
'driver' => 'pdo_mysql',
'host' => '127.0.0.1',
'dbname' => 'casbin_db',
'user' => 'root',
'password' => '',
'port' => '3306',
];
// Example with DBAL connection object:
// $dbalConnection = DriverManager::getConnection($dbConnectionParams);

// Redis configuration
$redisConfig = [
'host' => '127.0.0.1', // Optional, defaults to '127.0.0.1'
'port' => 6379, // Optional, defaults to 6379
'password' => null, // Optional, defaults to null
'database' => 0, // Optional, defaults to 0
'ttl' => 7200, // Optional, Cache policies for 2 hours (default is 3600)
'prefix' => 'myapp_casbin:' // Optional, Custom prefix (default is 'casbin_policies:')
];

// Initialize adapter with DB parameters array and Redis configuration
$adapter = DatabaseAdapter::newAdapter($dbConnectionParams, $redisConfig);
// Or, using a DBAL Connection object:
// $adapter = DatabaseAdapter::newAdapter($dbalConnection, $redisConfig);
// Alternatively, using the constructor:
// $adapter = new DatabaseAdapter($dbConnectionParams, $redisConfig);

$e = new Enforcer('path/to/model.conf', $adapter);

// ... rest of your Casbin usage
```

#### Cache Preheating

The adapter provides a `preheatCache()` method to proactively load all policies from the database and store them in the Redis cache. This can be useful during application startup or as part of a scheduled task to ensure the cache is warm, reducing latency on initial policy checks.

**Example:**

```php
if ($adapter->preheatCache()) {
// Cache preheating was successful
echo "Casbin policy cache preheated successfully.\n";
} else {
// Cache preheating failed (e.g., Redis not available or DB error)
echo "Casbin policy cache preheating failed.\n";
}
```

#### Cache Invalidation

The cache is designed to be automatically invalidated when policy-modifying methods are called on the adapter (e.g., `addPolicy()`, `removePolicy()`, `savePolicy()`, etc.). Currently, this primarily clears the cache key for all policies (`{$prefix}all_policies`).

**Important Note:** The automatic invalidation for *filtered policies* (policies loaded via `loadFilteredPolicy()`) is limited. Due to the way `predis/predis` client works and to avoid using performance-detrimental commands like `KEYS *` in production environments, the adapter does not automatically delete cache entries for specific filters by pattern. If you rely heavily on `loadFilteredPolicy` and make frequent policy changes, consider a lower TTL for your Redis cache or implement a more sophisticated cache invalidation strategy for filtered results outside of this adapter if needed. The main `{$prefix}all_policies` cache is cleared on any policy change, which means subsequent calls to `loadPolicy()` will refresh from the database and update this general cache.

### Getting Help

- [php-casbin](https://github.com/php-casbin/php-casbin)
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"require": {
"php": ">=8.0",
"casbin/casbin": "^4.0",
"doctrine/dbal": "^3.9|^4.0"
"doctrine/dbal": "^3.9|^4.0",
"predis/predis": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "~9.0",
Expand All @@ -36,4 +37,4 @@
"CasbinAdapter\\DBAL\\Tests\\": "tests/"
}
}
}
}
Loading