|
| 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 |
0 commit comments