Skip to content

Commit 94da325

Browse files
committed
Expand ArrayOfStrings functionality to support Undefined elements, refine tryFromArray method, and update JSON serialization logic.
1 parent 128267f commit 94da325

File tree

10 files changed

+51
-58
lines changed

10 files changed

+51
-58
lines changed

.junie/guidelines.md

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,22 @@
1-
# Main project instructions
2-
3-
## Events
4-
5-
### On new type generation
6-
7-
- add new type to `src/"type name"` folder
8-
- add new type assertions to `src/Code/Assert/Assert.php` if needed
9-
- add new type tests to `tests/Unit/"type name"/"TypeName"Test.php`
10-
- add new Exception class to `src/Exception` folder and use it
11-
12-
## Coding style
13-
14-
- PHP 8.2 used
15-
- use strict typing
16-
- follow PSR-12 coding standards
17-
- use meaningful variable and function names
18-
- keep code clean and readable
19-
- PSALM v6 static analysis is used
20-
21-
## Folder structure
22-
23-
- `src/Abstract` folder contains the framework internal code
24-
- other folders like `src/"type"` contains specific types
25-
- `src/psalmTest.php` contains types usage to avoid Psalm issues like `unused method`
26-
27-
## Tests
28-
29-
- are in `tests` folder
30-
- used PhpUnit v11 and PEST v3
31-
- use PEST syntax `it` instead of `test`
32-
- keep the folder structure as tested src files
33-
- test coverage should be 100%
34-
- mutation tests used, avoid fails
35-
36-
## Documentation
37-
38-
- keep it simple
39-
- use Markdown format
40-
- include project overview in README.md, short examples of how to install, use, and extend
41-
- installation instructions, usage examples, develop instructions in `docs` folder
1+
### Project Standards & Workflow
2+
3+
**Tech Stack & Style**
4+
* **Core:** PHP 8.2 with `declare(strict_types=1);`.
5+
* **Standards:** Follow PSR-12. Use clean, meaningful naming conventions.
6+
* **Static Analysis:** Psalm v6 (Level 1).
7+
8+
**Testing Strategy**
9+
* **Frameworks:** PEST v3 (use `it` syntax) & PHPUnit v11.
10+
* **Structure:** Test files must mirror the `src` directory structure.
11+
* **Requirements:** Maintain **100%** code coverage, type coverage, and mutation score.
12+
13+
**Workflow: Adding a New Type**
14+
* **Implementation:** Add the new type class to `src/{TypeName}/`.
15+
* **Exception:** Create a corresponding exception in `src/Exception/`.
16+
* **Testing:** Add unit tests to `tests/Unit/{TypeName}/{TypeName}Test.php`.
17+
18+
**Directory Structure**
19+
* `src/Abstract`: Internal framework base classes.
20+
* `src/{Type}`: Concrete type implementations (e.g., `src/Integer`).
21+
* `src/{Type}/Alias`: Concrete type aliases, just a new name (e.g., `src/Integer/Positive`).
22+
* `src/Usage`: Usage examples (prevents "unused code" false positives in Psalm).

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ $profile->getFirstName()->value(); // throws an exception on access the Undefine
108108

109109
##### Optional fail (only fail if the optional value is provided and invalid)
110110

111+
Ideal for partial data handling (e.g., requests where only specific fields, like ID, are required), allowing access to valid fields without failing on missing ones.
112+
111113
```php
112114
Profile::fromScalars(id: 101, firstName: 'Alice', height: -1); // invalid provided value -> early fail
113115

rector/toPhp7.4.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@
77
return RectorConfig::configure()
88
->withPaths([
99
__DIR__ . '/../src',
10-
// __DIR__ . '/../tests',
1110
])
1211
->withDowngradeSets(php74: true);

src/Abstract/Array/ArrayType.php

100644100755
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use IteratorAggregate;
88
use JsonSerializable;
9-
use PhpTypedValues\Abstract\AbstractType;
109

1110
/**
1211
* Base implementation for array typed values.

src/Abstract/Array/ArrayTypeInterface.php

100644100755
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,4 @@ public function value(): array;
1818
public static function fromArray(array $value): static;
1919

2020
public static function tryFromArray(array $value): static|Undefined;
21-
2221
}

src/Usage/Composite/Composite.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
require_once 'vendor/autoload.php';
66

7+
use const JSON_THROW_ON_ERROR;
78
use const PHP_EOL;
89

10+
use PhpTypedValues\Usage\Example\ArrayOfStrings;
911
use PhpTypedValues\Usage\Example\EarlyFail;
1012
use PhpTypedValues\Usage\Example\LateFail;
1113
use PhpTypedValues\Usage\Example\OptionalFail;
@@ -16,21 +18,20 @@
1618
echo PHP_EOL . '> COMPOSITE' . PHP_EOL;
1719

1820
$test = EarlyFail::fromScalars(id: 1, firstName: 'Foobar', height: 170);
19-
2021
echo $test->getId()->toString() . PHP_EOL;
2122
echo $test->getFirstName()->toString() . PHP_EOL;
2223
echo $test->getHeight()->toString() . PHP_EOL;
2324

2425
$test = LateFail::fromScalars(id: 1, firstName: 'Foobar', height: 170);
25-
2626
echo $test->getId()->toString() . PHP_EOL;
2727
echo $test->getFirstName()->toString() . PHP_EOL;
2828
echo $test->getHeight()->toString() . PHP_EOL;
2929

3030
$test = OptionalFail::fromScalars(id: 1, firstName: 'Foobar', height: 170);
31-
3231
echo $test->getId()->toString() . PHP_EOL;
3332
echo $test->getFirstName()->toString() . PHP_EOL;
3433
echo $test->getHeight()->toString() . PHP_EOL;
34+
echo json_encode($test, JSON_THROW_ON_ERROR) . PHP_EOL;
3535

36+
$test = ArrayOfStrings::tryFromArray(['string', 'not-empty', '1', 2, true]);
3637
echo json_encode($test, JSON_THROW_ON_ERROR) . PHP_EOL;

src/Usage/Example/ArrayOfStrings.php

100644100755
Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,25 @@
66

77
require_once 'vendor/autoload.php';
88

9+
use Generator;
910
use PhpTypedValues\Abstract\Array\ArrayType;
1011
use PhpTypedValues\Exception\StringTypeException;
1112
use PhpTypedValues\Exception\TypeException;
13+
use PhpTypedValues\Exception\UndefinedTypeException;
1214
use PhpTypedValues\String\StringNonEmpty;
1315
use PhpTypedValues\Undefined\Alias\Undefined;
1416

1517
/**
1618
* @internal
1719
*
1820
* @psalm-internal PhpTypedValues
21+
*
1922
* @psalm-immutable
2023
*/
2124
final readonly class ArrayOfStrings extends ArrayType
2225
{
2326
/**
24-
* @param StringNonEmpty[] $value
27+
* @param StringNonEmpty|Undefined[] $value
2528
*
2629
* @throws StringTypeException
2730
* @throws TypeException
@@ -34,8 +37,8 @@ public function __construct(
3437
}
3538

3639
foreach ($value as $item) {
37-
if (!$item instanceof StringNonEmpty) {
38-
throw new StringTypeException('Expected array of StringNonEmpty instance');
40+
if ((!$item instanceof StringNonEmpty) && (!$item instanceof Undefined)) {
41+
throw new StringTypeException('Expected array of StringNonEmpty or Undefined instance');
3942
}
4043
}
4144
}
@@ -44,7 +47,7 @@ public function __construct(
4447
* @throws StringTypeException
4548
* @throws TypeException
4649
*/
47-
public static function fromArray(array $value): self
50+
public static function fromArray(array $value): static
4851
{
4952
$typed = [];
5053
foreach ($value as $item) {
@@ -55,7 +58,7 @@ public static function fromArray(array $value): self
5558
}
5659

5760
/**
58-
* @return StringNonEmpty[]
61+
* @return StringNonEmpty|Undefined[]
5962
*/
6063
public function value(): array
6164
{
@@ -64,8 +67,10 @@ public function value(): array
6467

6568
/**
6669
* @return non-empty-list<non-empty-string>
70+
*
71+
* @throws UndefinedTypeException
6772
*/
68-
public function toStrings(): array
73+
public function toArray(): array
6974
{
7075
$result = [];
7176
foreach ($this->value as $item) {
@@ -81,24 +86,28 @@ public function toStrings(): array
8186
/** @return non-empty-list<non-empty-string> */
8287
public function jsonSerialize(): array
8388
{
84-
return $this->toStrings();
89+
return $this->toArray();
8590
}
8691

8792
/**
88-
* @return Traversable<StringNonEmpty>
93+
* @return Generator<StringNonEmpty>
8994
*/
90-
public function getIterator(): Traversable
95+
public function getIterator(): Generator
9196
{
9297
yield from $this->value;
9398
}
9499

100+
/**
101+
* @throws StringTypeException
102+
* @throws TypeException
103+
*/
95104
public static function tryFromArray(array $value): static|Undefined
96105
{
97106
$typed = [];
98107
foreach ($value as $item) {
99-
$typed[] = StringNonEmpty::tryFromString($item);
108+
$typed[] = StringNonEmpty::tryFromMixed($item);
100109
}
101110

102-
return new self($typed);
111+
return new static($typed);
103112
}
104113
}

src/Usage/Example/EarlyFail.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* @internal
1818
*
1919
* @psalm-internal PhpTypedValues
20+
*
2021
* @psalm-immutable
2122
*/
2223
final readonly class EarlyFail

src/Usage/Example/LateFail.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* @internal
1717
*
1818
* @psalm-internal PhpTypedValues
19+
*
1920
* @psalm-immutable
2021
*/
2122
final readonly class LateFail

src/Usage/Example/OptionalFail.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* @internal
2020
*
2121
* @psalm-internal PhpTypedValues
22+
*
2223
* @psalm-immutable
2324
*/
2425
final readonly class OptionalFail implements JsonSerializable

0 commit comments

Comments
 (0)