Skip to content

Commit

Permalink
Added WeakRef support
Browse files Browse the repository at this point in the history
Added resource value support
Moved SingleValueObjectTrait validation / normalization logic to factory method (to avoid unnecessary instantiation)
Improved documentation
Fixed money example
  • Loading branch information
lhellemons-sqills committed Feb 4, 2019
1 parent e36a73c commit 118c927
Show file tree
Hide file tree
Showing 12 changed files with 366 additions and 32 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Read the full documentation [here](docs/index.md).
Usage
-----

Install the package using composer.

```
composer require lhellemons/php-value-objects
```
Expand All @@ -36,9 +38,11 @@ final class Weekday
return self::define('TUESDAY');
}

...
// ...
}
...

// ...

$monday = Weekday::MONDAY();
$tuesday = Weekday::TUESDAY();
$deliveryDay = WeekDay::MONDAY();
Expand Down Expand Up @@ -74,7 +78,9 @@ final class EmailAddress
return $this->emailAddressString;
}
}
...

// ...

$emailAddress = EmailAddress::of("[email protected]");
$sameEmailAddress = EmailAddress::of(" [email protected]");

Expand Down
10 changes: 7 additions & 3 deletions docs/examples/money.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Martin Fowler (https://martinfowler.com/eaaCatalog/money.html).
Here is a sample implementation using a ValueObject:

```php
use SolidPhp\ValueObjects\Enum\EnumInterface;
use SolidPhp\ValueObjects\Enum\EnumTrait;
use SolidPhp\ValueObjects\Value\ValueObjectTrait;

class Currency implements EnumInterface
{
use EnumTrait;
Expand Down Expand Up @@ -83,10 +87,10 @@ final class Money

$share = $this->multiply(1 / $count);
$shares = array_fill(0, $count, $share);
$remainder = $this->subtract($share->multiply($count), $result);
$results[$count-1] = $results[$count-1]->add($remainder);
$remainder = $this->subtract($share->multiply($count));
$shares[$count-1] = $shares[$count-1]->add($remainder);

return $results;
return $shares;
}

private function assertSameCurrency(Money $money): void
Expand Down
29 changes: 20 additions & 9 deletions docs/single-value-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,22 @@ The `SingleValueObjectTrait` implements these methods for you, so in
most cases you can just define your class like this and it will work:

```php
class MySimpleValueObject implements SingleValueObjectInterface
use SolidPhp\ValueObjects\Value\SingleValueObjectInterface;
use SolidPhp\ValueObjects\Value\SingleValueObjectTrait;

class EmailAddress implements SingleValueObjectInterface
{
use SingleValueObjectTrait;
}
```

or extend the `SimpleValueObject` abstract class, which is provided for
or extend the `SingleValueObject` abstract class, which is provided for
convenience and is basically just a shorthand for the above code block:

```php
class MySimpleValueObject extends SimpleValueObject
use SolidPhp\ValueObjects\Value\SingleValueObject;

class EmailAddress extends SingleValueObject
{
}
```
Expand All @@ -38,13 +43,16 @@ case, you can override the `validateRawValue` method from `SingleValueObjectTrai
add any validation you might need:

```php
class Email implements SingleValueObjectInterface
use SolidPhp\ValueObjects\Value\SingleValueObjectInterface;
use SolidPhp\ValueObjects\Value\SingleValueObjectTrait;

class EmailAddress implements SingleValueObjectInterface
{
use SingleValueObjectTrait;

protected static function validateRawValue($rawValue): void
{
if (0 === preg_match('/\w+\@\w.com/', $rawValue) {
if (0 === preg_match('/\w+\@\w.com/', $rawValue)) {
throw new DomainException('Not a valid e-mail address');
}
}
Expand All @@ -65,18 +73,22 @@ object, for example to trim any whitespace. For this, you can
override the `normalizeValidRawValue` method from `SingleValueObjectTrait`:

```php
class LastName implements SingleValueObjectInterface
use SolidPhp\ValueObjects\Value\SingleValueObjectInterface;
use SolidPhp\ValueObjects\Value\SingleValueObjectTrait;

class EmailAddress implements SingleValueObjectInterface
{
use SingleValueObjectTrait;

protected static function normalizeValidRawValue($validRawValue)
protected static function normalizeValidRawValue($validRawValue): string
{
return trim($validRawValue);
}
}
```

`normalizeValidRawValue` is called after validation, so
`normalizeValidRawValue` is called after validation, so you can assume
the parameter is valid according to the rules of your class.

Considerations
--------------
Expand All @@ -86,4 +98,3 @@ value object also apply here.

- Don't mutate instance properties
- Don't deserialize directly; always use the `of` factory method
- Use only scalar values; no objects or arrays.
24 changes: 21 additions & 3 deletions docs/value-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ their parameters, and then call `return static::getInstance()` with the paramete

Example:
```php
use SolidPhp\ValueObjects\Value\ValueObjectTrait;

class Point
{
// use ValueObjectTrait to get access to the getInstance class method to use in your factory methods
Expand Down Expand Up @@ -86,12 +88,14 @@ $myPoint === $myOtherPoint; // false
Important things to remember
----------------------------

Value objects add many advantages to your codebase, but these come with the following caveats:
Value objects add many advantages to your codebase. These flow directly
from the following usage rules:

- Never mutate the value of your instance properties! You can add non-static factory methods that
use `static::getInstance` to construct a new instance based on the current instance.
This also means no setter methods! Check out the [money example](examples/money.md).
- Don't directly `unserialize` a value object; this will always create a new instance. Value objects
This also means no setter methods! Check out the [money example](examples/money.md) for an example of how to work with immutable
value object instances.
- Value objects cannot directly be `unserialize`d; this would always create a new instance. Value objects
usually contain only scalars as properties, so serialization / unserialization should be a matter
of getting those scalars for serialization and using the unserialized scalars to produce the correct
call to a factory method for deserialization. In the future this library may add a trait that will
Expand All @@ -108,3 +112,17 @@ $entity->property = 2;
$valueObjectB = ValueClass::fromEntity($entity);
$valueObjectA === $valueObjectB; // true
```

WeakRef
-------

Value objects support the PHP [WeakRef extension](http://php.net/manual/en/book.weakref.php).
If your PHP runtime has the WeakRef extension installed, value object
instances will be kept as weak references, and will be
garbage-collected
when they are no longer referenced in code. This means it becomes
possible to instantiate enormous numbers of value objects for
quick tasks without consuming excessive memory.

If the WeakRef extension is not installed, value object instances will
be kept in memory until the script finishes.
46 changes: 46 additions & 0 deletions src/Value/Ref/Ref.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace SolidPhp\ValueObjects\Value\Ref;

define('__SOLIDPHP_VALUEOBJECTS_WEAKREF_AVAILABLE', class_exists('\WeakRef'));

if (__SOLIDPHP_VALUEOBJECTS_WEAKREF_AVAILABLE) {
function createRef(): Ref {
return new WeakRef();
}
} else {
function createRef(): Ref {
return new StrongRef();
}
}

/**
* Class Ref
*
* Provides a way of storing a reference to an object. There are two implementations:
* StrongRef, which stores a reference to the object directly; and
* WeakRef, which stores a PHP WeakRef to the object.
*
* WeakRef can only be used if the PECL extension has been installed.
*
* @internal
*/
abstract class Ref
{
public static function create(): Ref
{
return createRef();
}

/**
* @return object|null
*/
abstract public function get();

/**
* @param object|null $object
*/
abstract public function set($object): void;

abstract public function has(): bool;
}
34 changes: 34 additions & 0 deletions src/Value/Ref/StrongRef.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace SolidPhp\ValueObjects\Value\Ref;

/**
* Class StrongRef
* @internal
*/
class StrongRef extends Ref
{
/** @var object|null */
private $object;

/**
* @return object|null
*/
public function get()
{
return $this->object;
}

/**
* @param object|null $object
*/
public function set($object): void
{
$this->object = $object;
}

public function has(): bool
{
return $this->object !== null;
}
}
36 changes: 36 additions & 0 deletions src/Value/Ref/WeakRef.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace SolidPhp\ValueObjects\Value\Ref;

use \WeakRef as PhpWeakRef;

/**
* Class WeakRef
* @internal
*/
class WeakRef extends Ref
{
/** @var PhpWeakRef|null */
private $weakRef;

/**
* @return object|null
*/
public function get()
{
return $this->weakRef ? $this->weakRef->get() : null;
}

/**
* @param object|null $object
*/
public function set($object): void
{
$this->weakRef = new PhpWeakRef($object);
}

public function has(): bool
{
return $this->weakRef && $this->weakRef->valid();
}
}
14 changes: 9 additions & 5 deletions src/Value/SingleValueObjectTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,24 @@ trait SingleValueObjectTrait /* implements SingleValueObjectInterface */
{
use ValueObjectTrait;

/** @var string */
/** @var string|int|float|bool */
private $value;

final protected function __construct($rawValue)
final protected function __construct($value)
{
static::validateRawValue($rawValue);
$this->value = static::normalizeValidRawValue($rawValue);
$this->value = $value;
}

final public static function of($rawValue): self
{
return self::getInstance($rawValue);
static::validateRawValue($rawValue);

return self::getInstance(static::normalizeValidRawValue($rawValue));
}

/**
* @return bool|float|int|string
*/
final public function getValue()
{
return $this->value;
Expand Down
Loading

0 comments on commit 118c927

Please sign in to comment.