Skip to content

Commit

Permalink
Adding Support\Fluent
Browse files Browse the repository at this point in the history
  • Loading branch information
lukewatts committed Mar 11, 2024
1 parent d455f44 commit f0a6344
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 1 deletion.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"Affinity4\\SlimSupport\\": "src/"
},
"files": [
"./src/Support/helpers.php"
"./src/Support/helpers.php",
"./src/Collections/helpers.php"
]
},
"autoload-dev": {
Expand Down
15 changes: 15 additions & 0 deletions src/Collections/helpers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php declare(strict_types=1);

if (! function_exists('value')) {
/**
* Return the default value of the given value.
*
* @param mixed $value
* @param mixed ...$args
* @return mixed
*/
function value($value, ...$args)
{
return $value instanceof \Closure ? $value(...$args) : $value;
}
}
17 changes: 17 additions & 0 deletions src/Contracts/Support/Arrayable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php declare(strict_types=1);

namespace Affinity4\SlimSupport\Contracts\Support;

/**
* @template TKey of array-key
* @template TValue
*/
interface Arrayable
{
/**
* Get the instance as an array.
*
* @return array<TKey, TValue>
*/
public function toArray();
}
14 changes: 14 additions & 0 deletions src/Contracts/Support/Jsonable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Affinity4\SlimSupport\Contracts\Support;

interface Jsonable
{
/**
* Convert the object to its JSON representation.
*
* @param int $options
* @return string
*/
public function toJson($options = 0);
}
199 changes: 199 additions & 0 deletions src/Support/Fluent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<?php

namespace Affinity4\SlimSupport\Support;

use Affinity4\SlimSupport\Contracts\Support\Arrayable;
use Affinity4\SlimSupport\Contracts\Support\Jsonable;

/**
* @template TKey of array-key
* @template TValue
*
* @implements \Affinity4\SlimSupport\Contracts\Support\Arrayable<TKey, TValue>
* @implements \ArrayAccess<TKey, TValue>
*/
class Fluent implements Arrayable, \ArrayAccess, Jsonable, \JsonSerializable
{
/**
* All of the attributes set on the fluent instance.
*
* @var array<TKey, TValue>
*/
protected $attributes = [];

/**
* Create a new fluent instance.
*
* @param iterable<TKey, TValue> $attributes
* @return void
*/
public function __construct($attributes = [])
{
foreach ($attributes as $key => $value) {
$this->attributes[$key] = $value;
}
}

/**
* Get an attribute from the fluent instance.
*
* @template TGetDefault
*
* @param TKey $key
* @param TGetDefault|(\Closure(): TGetDefault) $default
* @return TValue|TGetDefault
*/
public function get($key, $default = null)
{
if (array_key_exists($key, $this->attributes)) {
return $this->attributes[$key];
}

return value($default);
}

/**
* Get the attributes from the fluent instance.
*
* @return array<TKey, TValue>
*/
public function getAttributes()
{
return $this->attributes;
}

/**
* Convert the fluent instance to an array.
*
* @return array<TKey, TValue>
*/
public function toArray()
{
return $this->attributes;
}

/**
* Convert the object into something JSON serializable.
*
* @return array<TKey, TValue>
*/
public function jsonSerialize(): array
{
return $this->toArray();
}

/**
* Convert the fluent instance to JSON.
*
* @param int $options
* @return string
*/
public function toJson($options = 0)
{
return json_encode($this->jsonSerialize(), $options);
}

/**
* Determine if the given offset exists.
*
* @param TKey $offset
* @return bool
*/
public function offsetExists($offset): bool
{
return isset($this->attributes[$offset]);
}

/**
* Get the value for a given offset.
*
* @param TKey $offset
* @return TValue|null
*/
public function offsetGet($offset): mixed
{
return $this->get($offset);
}

/**
* Set the value at the given offset.
*
* @param TKey $offset
* @param TValue $value
* @return void
*/
public function offsetSet($offset, $value): void
{
$this->attributes[$offset] = $value;
}

/**
* Unset the value at the given offset.
*
* @param TKey $offset
* @return void
*/
public function offsetUnset($offset): void
{
unset($this->attributes[$offset]);
}

/**
* Handle dynamic calls to the fluent instance to set attributes.
*
* @param TKey $method
* @param array{0: ?TValue} $parameters
* @return $this
*/
public function __call($method, $parameters)
{
$this->attributes[$method] = count($parameters) > 0 ? reset($parameters) : true;

return $this;
}

/**
* Dynamically retrieve the value of an attribute.
*
* @param TKey $key
* @return TValue|null
*/
public function __get($key)
{
return $this->get($key);
}

/**
* Dynamically set the value of an attribute.
*
* @param TKey $key
* @param TValue $value
* @return void
*/
public function __set($key, $value)
{
$this->offsetSet($key, $value);
}

/**
* Dynamically check if an attribute is set.
*
* @param TKey $key
* @return bool
*/
public function __isset($key)
{
return $this->offsetExists($key);
}

/**
* Dynamically unset an attribute.
*
* @param TKey $key
* @return void
*/
public function __unset($key)
{
$this->offsetUnset($key);
}
}
17 changes: 17 additions & 0 deletions tests/Collections/HelperFunctionsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php declare(strict_types=1);

namespace Affinity4\SlimSupport\Tests\Collections;

use PHPUnit\Framework\TestCase;

final class HelperFunctionsTest extends TestCase
{
public function testValue(): void
{
$this->assertSame(1, value(function () {
return 1;
}));
$this->assertSame(1, value(1));
$this->assertSame(1, value(1, 2));
}
}
84 changes: 84 additions & 0 deletions tests/Support/FluentTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php declare(strict_types=1);

namespace Affinity4\SlimSupport\Tests\Support;

use Affinity4\SlimSupport\Support\Fluent;
use PHPUnit\Framework\TestCase;

class FluentTest extends TestCase
{
public function testToArray()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame(['foo' => 'bar'], $fluent->toArray());
}

public function testJsonSerialize()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame(['foo' => 'bar'], $fluent->jsonSerialize());
}

public function testToJson()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame('{"foo":"bar"}', $fluent->toJson());
}

public function testGetAttributes()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame(['foo' => 'bar'], $fluent->getAttributes());
}

public function testGet()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame('bar', $fluent->get('foo'));
}

public function testGetWithDefault()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame('bar', $fluent->get('foo', 'default'));
$this->assertSame('default', $fluent->get('bar', 'default'));
}

public function testGetWithClosure()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame('bar', $fluent->get('foo', function () {
return 'default';
}));
}

public function testGetWithClosureAndDefault()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame('default', $fluent->get('bar', function () {
return 'default';
}));
}

public function testGetWithCallable()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame('bar', $fluent->get('foo', function () {
return 'default';
}));
}

public function testGetWithCallableAndDefault()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame('default', $fluent->get('bar', function () {
return 'default';
}));
}

public function testGetViaMagicCall()
{
$fluent = new Fluent(['foo' => 'bar']);
$this->assertSame('bar', $fluent->foo);
}
}

0 comments on commit f0a6344

Please sign in to comment.