Skip to content

Commit 4aea710

Browse files
committed
feat: reflector support
2 parents 14b5a34 + ff697c3 commit 4aea710

File tree

81 files changed

+6622
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+6622
-11
lines changed

.junie/guidelines.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Constructo Development Guidelines
2+
3+
## Build/Configuration Instructions
4+
5+
### Environment Setup
6+
7+
This project uses **Docker Compose** for development environment management. All development commands must be executed
8+
within Docker containers.
9+
10+
**Prerequisites:**
11+
12+
- Docker and Docker Compose installed
13+
- PHP 8.3+ (handled by Docker)
14+
15+
**Initial Setup:**
16+
17+
```bash
18+
make setup # Prunes, installs dependencies, and starts containers
19+
```
20+
21+
**Key Commands:**
22+
23+
```bash
24+
make up # Start Docker containers
25+
make down # Stop containers
26+
make install # Install Composer dependencies
27+
```
28+
29+
### Composer Scripts
30+
31+
The project includes comprehensive Composer scripts for development:
32+
33+
```bash
34+
# Via Docker (recommended)
35+
make test # Run PHPUnit tests with coverage
36+
make lint # Run all linting tools
37+
make ci # Full CI pipeline (lint + test)
38+
39+
# Individual linting tools
40+
make lint-phpcs # PHP CodeSniffer (PSR-12)
41+
make lint-phpstan # Static analysis
42+
make lint-phpmd # Mess detector
43+
make lint-rector # Code modernization
44+
make lint-psalm # Static analysis
45+
```
46+
47+
## Testing Information
48+
49+
### Test Configuration
50+
51+
- **Framework:** PHPUnit 10.5+
52+
- **Configuration:** `phpunit.xml`
53+
- **Test Directory:** `tests/`
54+
- **Coverage Reports:** Generated in `tests/.phpunit/` (HTML, Clover, Text formats)
55+
- **Extensions:** PHPUnit Pretty Print with profiling and memory display
56+
57+
### Running Tests
58+
59+
```bash
60+
# Run all tests with coverage
61+
make test
62+
63+
# Run specific test file (within Docker container)
64+
docker compose exec constructo vendor/bin/phpunit tests/Path/To/TestFile.php
65+
66+
# Run tests by filter/pattern (correct approach)
67+
docker compose exec constructo vendor/bin/phpunit --filter=TestClassName
68+
# Note: make test FILTER=TestName is NOT the correct command for filtering
69+
```
70+
71+
### Test Structure and Patterns
72+
73+
Tests follow the src/ directory structure and use these patterns:
74+
75+
1. **Namespace:** `Constructo\Test\` + corresponding src namespace
76+
2. **Class naming:** `{ClassName}Test extends TestCase`
77+
3. **Method naming:** `test{FeatureDescription}(): void`
78+
79+
**Example Test Pattern:**
80+
81+
```php
82+
<?php
83+
declare(strict_types=1);
84+
85+
namespace Constructo\Test\Core\Example;
86+
87+
use Constructo\Core\Serialize\Builder;
88+
use Constructo\Support\Set;
89+
use PHPUnit\Framework\TestCase;
90+
91+
final class ExampleTest extends TestCase
92+
{
93+
public function testBasicFunctionality(): void
94+
{
95+
$builder = new Builder();
96+
$data = Set::createFrom(['key' => 'value']);
97+
98+
$result = $builder->build(\stdClass::class, $data);
99+
100+
$this->assertInstanceOf(\stdClass::class, $result);
101+
}
102+
}
103+
```
104+
105+
### Adding New Tests
106+
107+
1. Create a test file in `tests/` mirroring the `src/` structure
108+
2. Use `final class {Name}Test extends TestCase`
109+
3. Import required classes and use stub classes from `Constructo\Test\Stub\`
110+
4. Use `Set::createFrom()` for test data and `Target::createFrom()` for reflection
111+
5. Run tests via `make test` to verify
112+
113+
### Test Best Practices
114+
115+
**Avoid Redundant Type Checking:**
116+
117+
- Do not use `$this->assertInstanceOf(ClassName::class, $object)` immediately after creating an object with
118+
`new ClassName()` or calling a method that returns a specific type
119+
- If the constructor or method fails, PHP will throw an exception or type error; the test doesn't need to verify the
120+
object type
121+
- Instead, test meaningful functionality like method calls, state changes, return values, or object behavior
122+
- **Bad Example:** `$registry = $factory->make(); $this->assertInstanceOf(Registry::class, $registry);`
123+
- **Good Example:** `$registry = $factory->make(); $this->assertTrue($registry->hasSpec('required'));`
124+
- Focus on testing that the created object works correctly, not just that it exists
125+
126+
### Testing Classes with Magic Methods
127+
128+
**Field Class Testing Pattern:**
129+
130+
When testing classes like `Field` that use `__call` method to map virtual methods from docblock through specs:
131+
132+
- Focus on testing the mapping engine that connects docblock methods to specs
133+
- Create a simple `Specs` instance and use `register()` to add test specs instead of using factories
134+
- Testing with one spec is sufficient to verify the mapping mechanism works
135+
- Test the fluent API behavior and rule registration through the `__call` method
136+
137+
**Factory Naming Conventions:**
138+
139+
- Use `DefaultSpecsFactory` instead of `BasicSpecsFactory` for consistency
140+
- When possible, avoid factories in tests and create objects directly for simpler test setup
141+
142+
## Additional Development Information
143+
144+
### Project Architecture
145+
146+
**Constructo** is a PHP serialization/deserialization library with these core components:
147+
148+
- **Contracts:** Interfaces defining library behavior (`src/Contract/`)
149+
- **Core:** Main serialization/deserialization logic (`src/Core/`)
150+
- **Support:** Utility classes and reflection helpers (`src/Support/`)
151+
- **Types:** Type-specific implementations (`src/Type/`)
152+
153+
## Coding Standards and Practices
154+
155+
This project adheres to strict coding standards and best practices:
156+
157+
- **Strict Types:** All files use `declare(strict_types=1);`
158+
- **PSR-12:** Follow PSR-12 coding standards for consistency
159+
- **Type Hints:** Just use type hints if the typing is not clear or is ambiguous
160+
- **Comments:** Do not use comments for code explanations, prefer clear code structure
161+
162+
### Key Classes
163+
164+
- `Builder`: Main deserialization class using chain of responsibility pattern
165+
- `Set`: Data container for serialization input
166+
- `Target`: Reflection wrapper for class analysis
167+
- `Engine`: Base class for reflection-based operations
168+
169+
### Code Quality Tools
170+
171+
The project enforces strict code quality:
172+
173+
- **PHP 8.3+ strict types** (`declare(strict_types=1);`)
174+
- **PSR-12 coding standards** (PHP CodeSniffer)
175+
- **Static analysis** (PHPStan level max, Psalm)
176+
- **Code modernization** (Rector)
177+
- **Mess detection** (PHPMD)
178+
179+
### Autoloading
180+
181+
- **PSR-4:** `Constructo\``src/`
182+
- **Functions:** Auto-loaded from `functions/` directory
183+
- **Test PSR-4:** `Constructo\Test\``tests/`
184+
185+
### Dependencies
186+
187+
- **Runtime:** PHP 8.3+, ext-json, jawira/case-converter, visus/cuid2
188+
- **Testing:** PHPUnit, Faker, php-mock
189+
- **Quality:** Multiple static analysis and linting tools
190+
191+
### Development Workflow
192+
193+
1. Use Docker environment for all operations
194+
2. Follow strict typing and PSR-12 standards
195+
3. Write comprehensive tests with stub classes
196+
4. Run a full CI pipeline before commits: `make ci`
197+
5. Use `make fix` for automated code formatting
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Constructo\Contract\Managed;
6+
7+
use Constructo\Exception\ManagedException;
8+
9+
interface IdGenerator
10+
{
11+
/**
12+
* @throws ManagedException
13+
*/
14+
public function generate(): string;
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Constructo\Contract\Reflect;
6+
7+
use Constructo\Support\Metadata\Schema\Registry\Specs;
8+
9+
interface SpecsFactory
10+
{
11+
public function make(): Specs;
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Constructo\Contract\Reflect;
6+
7+
use Constructo\Support\Metadata\Schema\Registry\Types;
8+
9+
interface TypesFactory
10+
{
11+
public function make(): Types;
12+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Constructo\Core\Reflect\Introspection;
6+
7+
use Iterator;
8+
use ReflectionClass;
9+
use ReflectionException;
10+
11+
class Introspector
12+
{
13+
public function analyze(string $source): Result
14+
{
15+
try {
16+
$reflection = new ReflectionClass($source);
17+
18+
if (! $reflection->implementsInterface(Iterator::class)) {
19+
return new Result($source);
20+
}
21+
22+
$currentMethod = $reflection->getMethod('current');
23+
return new Result($source, $currentMethod->getReturnType());
24+
} catch (ReflectionException) {
25+
// If reflection fails, return null
26+
}
27+
return new Result($source);
28+
}
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Constructo\Core\Reflect\Introspection;
6+
7+
use ReflectionNamedType;
8+
use ReflectionType;
9+
10+
readonly class Result
11+
{
12+
public function __construct(
13+
public string $source,
14+
public ?ReflectionType $type = null,
15+
) {
16+
}
17+
18+
public function introspectable(): ?string
19+
{
20+
return (($this->type instanceof ReflectionNamedType) && ! $this->type->isBuiltin())
21+
? $this->type->getName()
22+
: null;
23+
}
24+
}

0 commit comments

Comments
 (0)