Skip to content

Commit

Permalink
Added SelfObjectAccess
Browse files Browse the repository at this point in the history
  • Loading branch information
pieterw2w committed May 13, 2020
1 parent 738b55a commit e0364f4
Show file tree
Hide file tree
Showing 5 changed files with 412 additions and 1 deletion.
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccess;
$serializer = new Serializer(
[
new DateTimeNormalizer(),
new ApieObjectAccessNormalizer(new ObjectAccess(), new CamelCaseToSnakeCaseNameConverter),
new ApieObjectAccessNormalizer(new ObjectAccess(), new CamelCaseToSnakeCaseNameConverter()),
new ArrayDenormalizer(),
],
[new JsonEncoder()]
Expand All @@ -101,6 +101,41 @@ $instance = new Example(12);
var_dump($serializer->serialize($instance, 'json'));
```

### Advanced usages
In many cases you wan to use ObjectAccess and only use a different ObjectAccessInterface implementation
for a specific class or interface. For that we created GroupedObjectAccess.

```php
<?php

use Illuminate\Database\Eloquent\Model;
use W2w\Laravel\LaravelApie\ObjectAccess\EloquentModelAccess;
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\GroupedObjectAccess;
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccess;
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\SelfObjectAccess;
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\SelfObjectAccessInterface;

$objectAccess = new GroupedObjectAccess(
new ObjectAccess,
[
// For SomeClass we can read private properties/getters
SomeClass::class => new ObjectAccess(false, true),
// for any class that implements SelfobjectAccessInterface we use SelfObjectAccess
SelfObjectAccessInterface::class => new SelfObjectAccess(),
// does not exist in this package, just an example. Eloquent models are notorious for the amount of magic.
Model::class => new EloquentModelObjectAccess(),
]

);
```

## Available object access implementations
- CachedObjectAccess: decorator to cache the results for performance reasons.
- FilteredObjectAccess: Filter the fields you can actually use. Another decorator
- GroupedObjectAccess: see Advanced usages. Can be used to use different Object Acces instances dependening on the class
- ObjectAccess: Default object access. Checks public properties and public setters and getters.
- SelfObjectAccess: Works for classes that implementSelfObjectAccessInterface, so the class can tell itself what it can access.

### in Symfony framework
If you want to use it in the Symfony framework all you need to do is register class
W2w\Lib\ApieObjectAccessNormalizer\Normalizers\ApieObjectAccessNormalizer
Expand Down
115 changes: 115 additions & 0 deletions src/ObjectAccess/SelfObjectAccess.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php


namespace W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess;

use ReflectionClass;
use ReflectionMethod;

/**
* ObjectAccess instance that only works on objects that implement SelfObjectAccessInterface
*
* @see SelfObjectAccessInterface
*/
class SelfObjectAccess implements ObjectAccessSupportedInterface
{
/**
* {@inheritDoc}
*/
public function getGetterFields(ReflectionClass $reflectionClass): array
{
$method = new ReflectionMethod($reflectionClass->getName(), __FUNCTION__);
return $method->invoke(null);
}

/**
* {@inheritDoc}
*/
public function getSetterFields(ReflectionClass $reflectionClass): array
{
$method = new ReflectionMethod($reflectionClass->getName(), __FUNCTION__);
return $method->invoke(null);
}

/**
* {@inheritDoc}
*/
public function getGetterTypes(ReflectionClass $reflectionClass, string $fieldName): array
{
$method = new ReflectionMethod($reflectionClass->getName(), __FUNCTION__);
return $method->invoke(null, $fieldName);
}

/**
* {@inheritDoc}
*/
public function getSetterTypes(ReflectionClass $reflectionClass, string $fieldName): array
{
$method = new ReflectionMethod($reflectionClass->getName(), __FUNCTION__);
return $method->invoke(null, $fieldName);
}

/**
* {@inheritDoc}
*/
public function getConstructorArguments(ReflectionClass $reflectionClass): array
{
$method = new ReflectionMethod($reflectionClass->getName(), __FUNCTION__);
return $method->invoke(null);
}

/**
* {@inheritDoc}
*/
public function getMethodArguments(ReflectionMethod $method, ?ReflectionClass $reflectionClass = null): array
{
return (new ObjectAccess())->getMethodArguments($method, $reflectionClass);
}

/**
* {@inheritDoc}
*/
public function getValue(object $instance, string $fieldName)
{
$method = new ReflectionMethod($instance, 'getFieldNameValue');
return $method->invoke($instance, $fieldName);
}

/**
* {@inheritDoc}
*/
public function setValue(object $instance, string $fieldName, $value)
{
$method = new ReflectionMethod($instance, 'setFieldNameValue');
return $method->invoke($instance, $fieldName, $value);
}

/**
* {@inheritDoc}
*/
public function instantiate(ReflectionClass $reflectionClass, array $constructorArgs): object
{
$method = new ReflectionMethod($reflectionClass->getName(), __FUNCTION__);
return $method->invoke(null, $constructorArgs);
}

/**
* {@inheritDoc}
*/
public function getDescription(ReflectionClass $reflectionClass, string $fieldName, bool $preferGetters): ?string
{
$method = new ReflectionMethod($reflectionClass->getName(), __FUNCTION__);
return $method->invoke(null, $fieldName, $preferGetters);
}

/**
* Returns true if class is supported for this ObjectAccess instance.
*
* @param ReflectionClass $reflectionClass
* @return bool
*/
public function isSupported(ReflectionClass $reflectionClass): bool
{
return $reflectionClass->implementsInterface(SelfObjectAccessInterface::class);
}
}
85 changes: 85 additions & 0 deletions src/ObjectAccess/SelfObjectAccessInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php


namespace W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess;

use Symfony\Component\PropertyInfo\Type;

/**
* A class implementing this interface can provide its own field information.
*
* @see SelfObjectAccessInterface
*/
interface SelfObjectAccessInterface
{
/**
* Returns all property names that can be retrieved.
*
* @return string[]
*/
public static function getGetterFields(): array;

/**
* Returns all property names that can be changed.
*
* @return string[]
*/
public static function getSetterFields(): array;

/**
* Returns all types that can be returned.
*
* @param string $fieldName
* @return Type[]
*/
public static function getGetterTypes(string $fieldName): array;

/**
* Returns all types that can be used for changing a value.
*
* @param string $fieldName
* @return array
*/
public static function getSetterTypes(string $fieldName): array;

/**
* Returns all types to instantiate an object.
*
* @return Type[]
*/
public static function getConstructorArguments(): array;

/**
* Gets a property value.
*
* @param string $fieldName
* @return mixed
*/
public function getFieldNameValue(string $fieldName);

/**
* Sets a property instance value.
*
* @param string $fieldName
* @param mixed $value
* @return mixed
*/
public function setFieldNameValue(string $fieldName, $value);

/**
* Instantiate the object with the arguments provided here.
*
* @param array $constructorArgs
* @return object
*/
public static function instantiate(array $constructorArgs): object;

/**
* Returns a string description of the property.
*
* @param string $fieldName
* @param bool $preferGetters
* @return string|null
*/
public static function getDescription(string $fieldName, bool $preferGetters): ?string;
}
133 changes: 133 additions & 0 deletions tests/Mocks/ClassWithSelfObjectAccess.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php


namespace W2w\Test\ApieObjectAccessNormalizer\Mocks;


use InvalidArgumentException;
use Symfony\Component\PropertyInfo\Type;
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\SelfObjectAccessInterface;

class ClassWithSelfObjectAccess implements SelfObjectAccessInterface
{
const VALID_KEYS = ['one', 'two', 'three'];

private $input = [
'one' => 1,
'two' => 2,
'three' => 3,
];

public function __construct(array $input)
{
foreach ($input as $key => $value) {
$this->$key = $value;
}
}

public function __set($key, int $value)
{
if (!in_array($key, self::VALID_KEYS)) {
throw new InvalidArgumentException('$key should be one, two, or three');
}
$this->input[$key] = $value;
}

public function __get($key): int
{
if (!in_array($key, self::VALID_KEYS)) {
throw new InvalidArgumentException('$key should be one, two, or three');
}
return $this->input[$key];
}

/**
* {@inheritDoc}
*/
public static function getGetterFields(): array
{
return self::VALID_KEYS;
}

/**
* {@inheritDoc}
*/
public static function getSetterFields(): array
{
return self::VALID_KEYS;
}

/**
* {@inheritDoc}
*/
public static function getGetterTypes(string $fieldName): array
{
if (!in_array($fieldName, self::VALID_KEYS)) {
throw new InvalidArgumentException('$key should be one, two, or three');
}
return [new Type(Type::BUILTIN_TYPE_INT, false)];
}

/**
* {@inheritDoc}
*/
public static function getSetterTypes(string $fieldName): array
{
if (!in_array($fieldName, self::VALID_KEYS)) {
throw new InvalidArgumentException('$key should be one, two, or three');
}
return [new Type(Type::BUILTIN_TYPE_INT, false)];
}

/**
* {@inheritDoc}
*/
public static function getConstructorArguments(): array
{
return [
'input' => new Type(
Type::BUILTIN_TYPE_ARRAY,
false,
null,
true,
new Type(Type::BUILTIN_TYPE_STRING, false),
new Type(Type::BUILTIN_TYPE_INT, false)
)
];
}

/**
* {@inheritDoc}
*/
public function getFieldNameValue(string $fieldName)
{
return $this->$fieldName;
}

/**
* {@inheritDoc}
*/
public function setFieldNameValue(string $fieldName, $value)
{
$this->$fieldName = $value;
}

/**
* {@inheritDoc}
*/
public static function instantiate(array $constructorArgs): object
{
return new self($constructorArgs['input'] ?? []);
}

/**
* {@inheritDoc}
*/
public static function getDescription(string $fieldName, bool $preferGetters): ?string
{
if (!in_array($fieldName, self::VALID_KEYS)) {
throw new InvalidArgumentException('$key should be one, two, or three');
}
return $fieldName;
}
}
Loading

0 comments on commit e0364f4

Please sign in to comment.