Skip to content

Commit

Permalink
PHP 8
Browse files Browse the repository at this point in the history
- Make compatible with Composer
- Add minimal library for type checking
  • Loading branch information
thisispiers committed Jun 3, 2022
1 parent ced67e4 commit ee2c7f8
Show file tree
Hide file tree
Showing 7 changed files with 630 additions and 475 deletions.
306 changes: 306 additions & 0 deletions Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
<?php declare(strict_types=1);

/**
* Abstract Redis commands with higher-level methods
*
* @package ziogas\Redis
*/

namespace ziogas\Redis;

use \ziogas\Redis\Exception as RedisException;

class Client
{
public function __construct(
private readonly \ziogas\Redis\Socket $socket
) {
}

/**
* @param array<mixed> $fields_values
* @return array<mixed>
*/
private function processFieldsValues(array $fields_values): array
{
$data = [];
$n = count($fields_values);
for ($i = 0; $i < $n; $i += 2) {
$field = $fields_values[$i];
$value = $fields_values[$i + 1];
$data[$field] = $value;
}

return $data;
}

public function connect(
?string $socket_address = null,
?string $password = null,
?int $timeout = null,
?int $connect_timeout = null
): bool {
if ($this->socket->isConnected()) {
return true;
}

if (empty($socket_address)) {
throw new RedisException('Missing Redis socket address');
}
$this->socket->setOptions($socket_address, $timeout, $connect_timeout);
try {
$this->socket->connect();
} catch (\Exception $e) {
throw new RedisException('Could not connect to Redis socket');
}

if (!empty($password)) {
$response = $this->socket->run('AUTH', $password);
if ($response !== 'OK') {
throw new RedisException('Incorrect password');
}
}

return true;
}

public function set(
string $key,
mixed $_value,
?int $ttl = null
): bool {
$value = $_value;
$_value = ''; // hide input from logs

$this->connect();

$args = ['SET', $key, $value];
if ($ttl !== null) {
$args[] = 'EX';
$args[] = $ttl;
}
$response = $this->socket->run(...$args);
if ($response !== 'OK') {
throw new RedisException('Key was not set');
}
return true;
}

public function exists(string $key): bool
{
$this->connect();

$response = $this->socket->run('EXISTS', $key);
return $response === 1;
}

/**
* @param string ...$keys
* @return string|array<mixed>|bool|int|null
*/
public function get(...$keys): string|array|bool|int|null
{
$this->connect();

if (count($keys) === 1) {
$response = $this->socket->run('GET', $keys[0]);
} else {
$response = $this->socket->run('MGET', ...$keys);
}
return $response;
}

public function expire(string $key, int $ttl): bool
{
$this->connect();

$response = $this->socket->run('EXPIRE', $key, $ttl);
return $response === 1;
}

public function expireat(string $key, int $time): bool
{
$this->connect();

$response = $this->socket->run('EXPIREAT', $key, $time);
return $response === 1;
}

public function ttl(string $key): int
{
$this->connect();

$response = $this->socket->run('TTL', $key);
if ($response === -2) {
throw new RedisException('Key does not exist');
} else if ($response === -1) {
throw new RedisException('Key will not expire');
}
return intval($response);
}

/**
* @param string ...$keys
*/
public function del(...$keys): int
{
$this->connect();

$response = $this->socket->run('DEL', ...$keys);
if ($response !== count($keys)) {
throw new RedisException('One or more keys was not deleted');
}
return $response;
}

/**
* @param string ...$keys
*/
public function unlink(...$keys): int
{
$this->connect();

$response = $this->socket->run('UNLINK', ...$keys);
if ($response !== count($keys)) {
throw new RedisException('One or more keys was not unlinked');
}
return $response;
}

/**
* @return array<mixed>
*/
public function scan(?string $pattern = null): array
{
$this->connect();

$keys = [];
$cursor = 0;
do {
$args = ['SCAN', $cursor];
if ($pattern) {
$args[] = 'MATCH';
$args[] = $pattern;
}
$response = $this->socket->run(...$args);
if (!is_array($response)) {
break;
}
$cursor = intval($response[0]);
$keys = array_merge($keys, $response[1]);
} while ($cursor !== 0);
$keys = array_unique($keys);
return $keys;
}

/**
* @param mixed ...$_fields_values
*/
public function hset(string $key, ...$_fields_values): int
{
$fields_values = $_fields_values;
$_fields_values = []; // hide input from logs

$this->connect();

if (count($fields_values) === 1 && is_array($fields_values[0])) {
$a = [];
foreach ($fields_values[0] as $field => $value) {
$a[] = $field;
$a[] = $value;
}
$fields_values = $a;
}
$response = $this->socket->run('HSET', $key, ...$fields_values);
if ($response !== (count($fields_values) / 2)) {
throw new RedisException('One or more fields was not set');
}
return intval($response);
}

public function hexists(string $key, string $field): bool
{
$this->connect();

$response = $this->socket->run('HEXISTS', $key, $field);
return $response === 1;
}

public function hget(string $key, string $field): ?string
{
$this->connect();

$response = $this->socket->run('HGET', $key, $field);
if (is_string($response) || $response === null) {
return $response;
} else {
return null;
}
}

/**
* @return array<mixed>
*/
public function hgetall(string $key): array
{
$this->connect();

$fields_values = $this->socket->run('HGETALL', $key);
if (is_array($fields_values)) {
return $this->processFieldsValues($fields_values);
} else {
return [$fields_values];
}
}

public function hlen(string $key): int
{
$this->connect();

return intval($this->socket->run('HLEN', $key));
}

/**
* @param mixed ...$fields
*/
public function hdel(string $key, ...$fields): int
{
$this->connect();

if (count($fields) === 1 && is_array($fields[0])) {
$fields = $fields[0];
}
$response = $this->socket->run('HDEL', $key, ...$fields);
if ($response !== count($fields)) {
throw new RedisException('One or more fields was not deleted');
}
return $response;
}

/**
* @return array<mixed>
*/
public function hscan(string $key, ?string $pattern = null): array
{
$this->connect();

$keys = [];
$cursor = 0;
do {
$args = ['HSCAN', $key, $cursor];
if ($pattern) {
$args[] = 'MATCH';
$args[] = $pattern;
}
$response = $this->socket->run(...$args);
if (!is_array($response)) {
break;
}
$cursor = intval($response[0]);
$keys = array_merge($keys, $response[1]);
} while ($cursor !== 0);
$keys = array_unique($keys);
return $keys;
}

}
8 changes: 8 additions & 0 deletions Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php declare(strict_types=1);

namespace ziogas\Redis;

class Exception extends \Exception
{

}
42 changes: 7 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PHP Redis implementation V2.1
PHP Redis implementation
==============
Yet another php redis implementation.
Raw wrapper for real [Redis] fans. Main advantages:
Expand All @@ -12,49 +12,20 @@ Raw wrapper for real [Redis] fans. Main advantages:
* Dead Simple and lightweight, you're welcome to read all the 300+ lines of redis.php
* Forces you to actually learn and understand redis data structures and commands.

**This fork makes it compatible with Composer and adds a minimal library for PHP 8 type checking.**

## Download
You can checkout latest version with:

$ git clone git://github.com/ziogas/PHP-Redis-implementation
$ git clone git://github.com/thisispiers/PHP-Redis-implementation


## Install
To install PHP redis:

* Simply copy redis.php to your site and require it from external script

Here are some examples:

```php
require 'redis.php';

function redis_error($error) {
throw new error($error);
}

$redis = new redis_cli ();
$redis->set_error_function('redis_error');

$redis->cmd('SET', 'foo', 'bar')
->cmd('HSET', 'hash', 'field', 'val')
->cmd('EXPIRE', 'foo', 300)
->set();

$foo = $redis->cmd('GET', 'foo')->get();
$field = $redis->cmd('HGET', 'hash', 'field')->get();

var_dump($foo);
var_dump($field);
```

More usage examples can be found on tests folder.
For commands documentation just go directly to [https://redis.io/commands]

## Running tests
To run tests you'll need [phpunit]

Execute:
```phpunit tests/*```
composer require thisispiers/php-redis-implementation
```

## Contributing

Expand All @@ -66,6 +37,7 @@ Execute:

## Author
Arminas Zukauskas - [email protected]
thisispiers

*Based on http://redis.io/topics/protocol*

Expand Down
Loading

0 comments on commit ee2c7f8

Please sign in to comment.