Skip to content

Commit

Permalink
Merge branch 'main' into add-cipher-exception-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ilicmiljan authored Mar 10, 2024
2 parents 2c37a58 + 55814cc commit 15ab6ee
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 156 deletions.
5 changes: 5 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,35 @@ $cipher = new AsymmetricEncryptionCipher($publicKey, $privateKey);

## Property Readers

SecureProps comes with two implementations of property readers: a runtime one and a decorator for caching.
SecureProps provides two types of property readers to handle encrypted properties within your PHP objects efficiently: `RuntimeObjectPropertiesReader` and `CachingObjectPropertiesReader`.

### RuntimeObjectPropertiesReader

This reader inspects objects at runtime to find properties marked with the `#[Encrypted]` attribute. It uses PHP's reflection capabilities to perform its duties without requiring any caching mechanism.
The `RuntimeObjectPropertiesReader` dynamically examines objects at runtime to identify properties decorated with the `#[Encrypted]` attribute. Utilizing PHP's reflection requires no additional setup for caching and offers straightforward inspection capabilities.

### CachingObjectPropertiesReader

This reader wraps another `ObjectPropertiesReader` implementation and caches the results to improve performance. It's particularly useful for applications that repeatedly process the same object types, reducing the overhead of reflection operations. The `PSR-6` caching standard provides a flexible framework for integrating various caching backends, offering developers the freedom to choose a solution that best fits their application's scaling and performance requirements.
For enhanced performance, especially in applications that frequently deal with the same types of objects, the `CachingObjectPropertiesReader` caches property reading results. This approach reduces the computational overhead associated with reflection.

It integrates seamlessly with `PSR-6` compliant caching solutions, allowing for customizable performance optimization.

#### Quick Start Example

Combining `CachingObjectPropertiesReader` with `RuntimeObjectPropertiesReader` and a `PSR-6` compliant cache implementation:

```php
// Initialize a PSR-6 cache pool
$cache = new FilesystemAdapter(...);

// Configure the caching reader
$reader = new CachingObjectPropertiesReader(
new RuntimeObjectPropertiesReader(),
new ItemPoolCompatibleCache($cache)
);

// Set up the ObjectEncryptionService with the reader
$encryptionService = new ObjectEncryptionService($cipher, $reader);
```

## Contributing

Expand Down
25 changes: 25 additions & 0 deletions src/Cache/Cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace IlicMiljan\SecureProps\Cache;

use IlicMiljan\SecureProps\Cache\Exception\InvalidCacheKey;
use Psr\Cache\CacheItemInterface;

interface Cache
{
/**
* Fetches a value from the cache or computes it if not found.
*
* @template T
*
* @param string $key The cache key.
* @param (callable(CacheItemInterface):T) $callable A callable that
* computes the value if it's not found in the
* cache.
* @param int|null $ttl The time-to-live for the cache entry in seconds.
*
* @return T The cached or computed value.
* @throws InvalidCacheKey When $key is not valid.
*/
public function get(string $key, callable $callable, ?int $ttl = null): mixed;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace IlicMiljan\SecureProps\Reader\Exception;
namespace IlicMiljan\SecureProps\Cache\Exception;

use LogicException;
use Throwable;
Expand Down
35 changes: 35 additions & 0 deletions src/Cache/ItemPoolCompatibleCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace IlicMiljan\SecureProps\Cache;

use IlicMiljan\SecureProps\Cache\Exception\InvalidCacheKey;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\InvalidArgumentException;

class ItemPoolCompatibleCache implements Cache
{
private CacheItemPoolInterface $cachePool;

public function __construct(CacheItemPoolInterface $cachePool)
{
$this->cachePool = $cachePool;
}

public function get(string $key, callable $callable, ?int $ttl = null): mixed
{
try {
$cacheItem = $this->cachePool->getItem($key);
} catch (InvalidArgumentException $e) {
throw new InvalidCacheKey($key, $e);
}

if (!$cacheItem->isHit()) {
$cacheItem->set($callable($cacheItem));
$cacheItem->expiresAfter($ttl);

$this->cachePool->save($cacheItem);
}

return $cacheItem->get();
}
}
91 changes: 20 additions & 71 deletions src/Reader/CachingObjectPropertiesReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,44 @@

namespace IlicMiljan\SecureProps\Reader;

use IlicMiljan\SecureProps\Reader\Exception\InvalidCacheKey;
use IlicMiljan\SecureProps\Reader\Exception\InvalidCacheValueDataType;
use IlicMiljan\SecureProps\Cache\Cache;
use IlicMiljan\SecureProps\Cache\Exception\InvalidCacheKey;
use IlicMiljan\SecureProps\Reader\Exception\ObjectPropertyNotFound;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\InvalidArgumentException;
use ReflectionException;
use ReflectionObject;
use ReflectionProperty;

class CachingObjectPropertiesReader implements ObjectPropertiesReader
{
private const CACHE_TTL_DEFAULT = null;

public function __construct(
private ObjectPropertiesReader $objectPropertiesReader,
private CacheItemPoolInterface $cache
private Cache $cache,
private ?int $cacheTtl = self::CACHE_TTL_DEFAULT
) {
}

/**
* @throws InvalidCacheKey
* @throws InvalidCacheValueDataType
* @throws ObjectPropertyNotFound
*/
public function getPropertiesWithAttribute(object $object, string $attributeClass): array
{
$cachedProperties = $this->getCacheItem($this->getCacheKey($object, $attributeClass));

if ($cachedProperties->isHit()) {
$this->ensureCacheItemValueIsArray($cachedProperties);

/** @var string[] $cachedPropertiesValue */
$cachedPropertiesValue = $cachedProperties->get();

return $this->loadRuntimeReflectionProperties($object, $cachedPropertiesValue);
}

$propertiesWithAttribute = $this->objectPropertiesReader->getPropertiesWithAttribute(
$object,
$attributeClass
);

$this->updateCache(
$cachedProperties,
$this->getCacheablePropertiesArray($propertiesWithAttribute)
$propertyArray = $this->cache->get(
$this->getCacheKey($object, $attributeClass),
function (CacheItemInterface $cacheItem) use ($object, $attributeClass) {
$propertiesWithAttribute = $this->objectPropertiesReader->getPropertiesWithAttribute(
$object,
$attributeClass
);

return $this->getCacheablePropertiesArray($propertiesWithAttribute);
},
$this->cacheTtl
);

return $propertiesWithAttribute;
return $this->loadRuntimeReflectionProperties($object, $propertyArray);
}

private function getCacheKey(object $object, string $attributeClass): string
Expand All @@ -66,32 +57,15 @@ private function getCacheKey(object $object, string $attributeClass): string
*/
private function loadRuntimeReflectionProperties(object $object, array $propertyNames): array
{
$reflection = new ReflectionObject($object);

try {
return array_map(function ($propertyName) use ($reflection) {
return $reflection->getProperty($propertyName);
return array_map(function ($propertyName) use ($object) {
return new ReflectionProperty($object, $propertyName);
}, $propertyNames);
} catch (ReflectionException $e) {
throw new ObjectPropertyNotFound($object::class, $e);
}
}

/**
* @param string $cacheKey
* @return CacheItemInterface
*
* @throws InvalidCacheKey
*/
private function getCacheItem(string $cacheKey): CacheItemInterface
{
try {
return $this->cache->getItem($cacheKey);
} catch (InvalidArgumentException $e) {
throw new InvalidCacheKey($cacheKey, $e);
}
}

/**
* @param ReflectionProperty[] $properties
* @return string[]
Expand All @@ -102,29 +76,4 @@ private function getCacheablePropertiesArray(array $properties): array
return $property->getName();
}, $properties);
}

/**
* @param CacheItemInterface $cacheItem
* @return void
*
* @throws InvalidCacheValueDataType
*/
private function ensureCacheItemValueIsArray(CacheItemInterface $cacheItem): void
{
$cachedValue = $cacheItem->get();

if (is_array($cachedValue)) {
return;
}

throw new InvalidCacheValueDataType(gettype($cachedValue), 'array');
}

private function updateCache(CacheItemInterface $cacheItem, mixed $data): void
{
$cacheItem->set($data);
$cacheItem->expiresAfter(3600);

$this->cache->save($cacheItem);
}
}
31 changes: 0 additions & 31 deletions src/Reader/Exception/InvalidCacheValueDataType.php

This file was deleted.

Loading

0 comments on commit 15ab6ee

Please sign in to comment.