Skip to content

Commit 118c927

Browse files
Added WeakRef support
Added resource value support Moved SingleValueObjectTrait validation / normalization logic to factory method (to avoid unnecessary instantiation) Improved documentation Fixed money example
1 parent e36a73c commit 118c927

File tree

12 files changed

+366
-32
lines changed

12 files changed

+366
-32
lines changed

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Read the full documentation [here](docs/index.md).
1111
Usage
1212
-----
1313

14+
Install the package using composer.
15+
1416
```
1517
composer require lhellemons/php-value-objects
1618
```
@@ -36,9 +38,11 @@ final class Weekday
3638
return self::define('TUESDAY');
3739
}
3840

39-
...
41+
// ...
4042
}
41-
...
43+
44+
// ...
45+
4246
$monday = Weekday::MONDAY();
4347
$tuesday = Weekday::TUESDAY();
4448
$deliveryDay = WeekDay::MONDAY();
@@ -74,7 +78,9 @@ final class EmailAddress
7478
return $this->emailAddressString;
7579
}
7680
}
77-
...
81+
82+
// ...
83+
7884
$emailAddress = EmailAddress::of("annie@email.com");
7985
$sameEmailAddress = EmailAddress::of(" ANNIE@EMAIL.COM");
8086

docs/examples/money.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Martin Fowler (https://martinfowler.com/eaaCatalog/money.html).
66
Here is a sample implementation using a ValueObject:
77

88
```php
9+
use SolidPhp\ValueObjects\Enum\EnumInterface;
10+
use SolidPhp\ValueObjects\Enum\EnumTrait;
11+
use SolidPhp\ValueObjects\Value\ValueObjectTrait;
12+
913
class Currency implements EnumInterface
1014
{
1115
use EnumTrait;
@@ -83,10 +87,10 @@ final class Money
8387

8488
$share = $this->multiply(1 / $count);
8589
$shares = array_fill(0, $count, $share);
86-
$remainder = $this->subtract($share->multiply($count), $result);
87-
$results[$count-1] = $results[$count-1]->add($remainder);
90+
$remainder = $this->subtract($share->multiply($count));
91+
$shares[$count-1] = $shares[$count-1]->add($remainder);
8892

89-
return $results;
93+
return $shares;
9094
}
9195

9296
private function assertSameCurrency(Money $money): void

docs/single-value-objects.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,22 @@ The `SingleValueObjectTrait` implements these methods for you, so in
1515
most cases you can just define your class like this and it will work:
1616

1717
```php
18-
class MySimpleValueObject implements SingleValueObjectInterface
18+
use SolidPhp\ValueObjects\Value\SingleValueObjectInterface;
19+
use SolidPhp\ValueObjects\Value\SingleValueObjectTrait;
20+
21+
class EmailAddress implements SingleValueObjectInterface
1922
{
2023
use SingleValueObjectTrait;
2124
}
2225
```
2326

24-
or extend the `SimpleValueObject` abstract class, which is provided for
27+
or extend the `SingleValueObject` abstract class, which is provided for
2528
convenience and is basically just a shorthand for the above code block:
2629

2730
```php
28-
class MySimpleValueObject extends SimpleValueObject
31+
use SolidPhp\ValueObjects\Value\SingleValueObject;
32+
33+
class EmailAddress extends SingleValueObject
2934
{
3035
}
3136
```
@@ -38,13 +43,16 @@ case, you can override the `validateRawValue` method from `SingleValueObjectTrai
3843
add any validation you might need:
3944

4045
```php
41-
class Email implements SingleValueObjectInterface
46+
use SolidPhp\ValueObjects\Value\SingleValueObjectInterface;
47+
use SolidPhp\ValueObjects\Value\SingleValueObjectTrait;
48+
49+
class EmailAddress implements SingleValueObjectInterface
4250
{
4351
use SingleValueObjectTrait;
4452

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

6775
```php
68-
class LastName implements SingleValueObjectInterface
76+
use SolidPhp\ValueObjects\Value\SingleValueObjectInterface;
77+
use SolidPhp\ValueObjects\Value\SingleValueObjectTrait;
78+
79+
class EmailAddress implements SingleValueObjectInterface
6980
{
7081
use SingleValueObjectTrait;
7182

72-
protected static function normalizeValidRawValue($validRawValue)
83+
protected static function normalizeValidRawValue($validRawValue): string
7384
{
7485
return trim($validRawValue);
7586
}
7687
}
7788
```
7889

79-
`normalizeValidRawValue` is called after validation, so
90+
`normalizeValidRawValue` is called after validation, so you can assume
91+
the parameter is valid according to the rules of your class.
8092

8193
Considerations
8294
--------------
@@ -86,4 +98,3 @@ value object also apply here.
8698

8799
- Don't mutate instance properties
88100
- Don't deserialize directly; always use the `of` factory method
89-
- Use only scalar values; no objects or arrays.

docs/value-objects.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ their parameters, and then call `return static::getInstance()` with the paramete
2626

2727
Example:
2828
```php
29+
use SolidPhp\ValueObjects\Value\ValueObjectTrait;
30+
2931
class Point
3032
{
3133
// use ValueObjectTrait to get access to the getInstance class method to use in your factory methods
@@ -86,12 +88,14 @@ $myPoint === $myOtherPoint; // false
8688
Important things to remember
8789
----------------------------
8890

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

9194
- Never mutate the value of your instance properties! You can add non-static factory methods that
9295
use `static::getInstance` to construct a new instance based on the current instance.
93-
This also means no setter methods! Check out the [money example](examples/money.md).
94-
- Don't directly `unserialize` a value object; this will always create a new instance. Value objects
96+
This also means no setter methods! Check out the [money example](examples/money.md) for an example of how to work with immutable
97+
value object instances.
98+
- Value objects cannot directly be `unserialize`d; this would always create a new instance. Value objects
9599
usually contain only scalars as properties, so serialization / unserialization should be a matter
96100
of getting those scalars for serialization and using the unserialized scalars to produce the correct
97101
call to a factory method for deserialization. In the future this library may add a trait that will
@@ -108,3 +112,17 @@ $entity->property = 2;
108112
$valueObjectB = ValueClass::fromEntity($entity);
109113
$valueObjectA === $valueObjectB; // true
110114
```
115+
116+
WeakRef
117+
-------
118+
119+
Value objects support the PHP [WeakRef extension](http://php.net/manual/en/book.weakref.php).
120+
If your PHP runtime has the WeakRef extension installed, value object
121+
instances will be kept as weak references, and will be
122+
garbage-collected
123+
when they are no longer referenced in code. This means it becomes
124+
possible to instantiate enormous numbers of value objects for
125+
quick tasks without consuming excessive memory.
126+
127+
If the WeakRef extension is not installed, value object instances will
128+
be kept in memory until the script finishes.

src/Value/Ref/Ref.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace SolidPhp\ValueObjects\Value\Ref;
4+
5+
define('__SOLIDPHP_VALUEOBJECTS_WEAKREF_AVAILABLE', class_exists('\WeakRef'));
6+
7+
if (__SOLIDPHP_VALUEOBJECTS_WEAKREF_AVAILABLE) {
8+
function createRef(): Ref {
9+
return new WeakRef();
10+
}
11+
} else {
12+
function createRef(): Ref {
13+
return new StrongRef();
14+
}
15+
}
16+
17+
/**
18+
* Class Ref
19+
*
20+
* Provides a way of storing a reference to an object. There are two implementations:
21+
* StrongRef, which stores a reference to the object directly; and
22+
* WeakRef, which stores a PHP WeakRef to the object.
23+
*
24+
* WeakRef can only be used if the PECL extension has been installed.
25+
*
26+
* @internal
27+
*/
28+
abstract class Ref
29+
{
30+
public static function create(): Ref
31+
{
32+
return createRef();
33+
}
34+
35+
/**
36+
* @return object|null
37+
*/
38+
abstract public function get();
39+
40+
/**
41+
* @param object|null $object
42+
*/
43+
abstract public function set($object): void;
44+
45+
abstract public function has(): bool;
46+
}

src/Value/Ref/StrongRef.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace SolidPhp\ValueObjects\Value\Ref;
4+
5+
/**
6+
* Class StrongRef
7+
* @internal
8+
*/
9+
class StrongRef extends Ref
10+
{
11+
/** @var object|null */
12+
private $object;
13+
14+
/**
15+
* @return object|null
16+
*/
17+
public function get()
18+
{
19+
return $this->object;
20+
}
21+
22+
/**
23+
* @param object|null $object
24+
*/
25+
public function set($object): void
26+
{
27+
$this->object = $object;
28+
}
29+
30+
public function has(): bool
31+
{
32+
return $this->object !== null;
33+
}
34+
}

src/Value/Ref/WeakRef.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace SolidPhp\ValueObjects\Value\Ref;
4+
5+
use \WeakRef as PhpWeakRef;
6+
7+
/**
8+
* Class WeakRef
9+
* @internal
10+
*/
11+
class WeakRef extends Ref
12+
{
13+
/** @var PhpWeakRef|null */
14+
private $weakRef;
15+
16+
/**
17+
* @return object|null
18+
*/
19+
public function get()
20+
{
21+
return $this->weakRef ? $this->weakRef->get() : null;
22+
}
23+
24+
/**
25+
* @param object|null $object
26+
*/
27+
public function set($object): void
28+
{
29+
$this->weakRef = new PhpWeakRef($object);
30+
}
31+
32+
public function has(): bool
33+
{
34+
return $this->weakRef && $this->weakRef->valid();
35+
}
36+
}

src/Value/SingleValueObjectTrait.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,24 @@ trait SingleValueObjectTrait /* implements SingleValueObjectInterface */
1414
{
1515
use ValueObjectTrait;
1616

17-
/** @var string */
17+
/** @var string|int|float|bool */
1818
private $value;
1919

20-
final protected function __construct($rawValue)
20+
final protected function __construct($value)
2121
{
22-
static::validateRawValue($rawValue);
23-
$this->value = static::normalizeValidRawValue($rawValue);
22+
$this->value = $value;
2423
}
2524

2625
final public static function of($rawValue): self
2726
{
28-
return self::getInstance($rawValue);
27+
static::validateRawValue($rawValue);
28+
29+
return self::getInstance(static::normalizeValidRawValue($rawValue));
2930
}
3031

32+
/**
33+
* @return bool|float|int|string
34+
*/
3135
final public function getValue()
3236
{
3337
return $this->value;

0 commit comments

Comments
 (0)