From 86d354b97d3e32131adf15cc0c153bafdab1d38a Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 29 Nov 2025 23:33:18 +0100 Subject: [PATCH 1/4] ISSUE-75: Add CLAUDE directives + support for 'act' as local pipeline runner. --- .github/workflows/ci.yaml | 12 ++- .gitignore | 2 +- CLAUDE.md | 208 ++++++++++++++++++++++++++++++++++++++ rector.php | 2 - 4 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 CLAUDE.md diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1ea8d56..21d5cf9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: [7.2, 7.4, 8.2] + php-version: [7.4, 8.2] include: - php-version: 8.2 env: @@ -25,7 +25,9 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} - tools: composer:1.10.22 + tools: composer:v2 + extensions: xdebug + coverage: xdebug - name: Cache Composer dependencies uses: actions/cache@v2 @@ -54,4 +56,8 @@ jobs: run: XDEBUG_MODE=coverage php vendor/bin/phpunit - name: Upload coverage to Codecov - run: ./codecov \ No newline at end of file + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + run: | + BRANCH=$(git rev-parse --abbrev-ref HEAD) + ./codecov -r BowlOfSoup/NormalizerBundle -B "${BRANCH}" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 45bdb03..c984fef 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ !/var/**/ !/var/**/.gitkeep /phpstan.neon ->>>>>>> fc967e0fc51238085fa8a2a2882bcdab42090dec +.env.local diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7101ff2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,208 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Bowl Of Soup Normalizer is a Symfony bundle that provides annotation-based normalization and serialization of objects. It uses an opt-in mechanism where properties/methods must be explicitly marked for normalization using annotations. + +**Key Features:** +- Normalizes class properties and methods (public, protected, private) via annotations +- Handles Doctrine proxy objects and circular references +- Object caching via `getId()` method to avoid re-normalizing same objects +- Annotation caching (per normalize command and permanent in prod mode) +- Supports Symfony translations with locale and domain configuration +- Context groups for different normalization scenarios + +## Development Commands + +### Running Tests Directly + +```bash +# Run all tests +vendor/bin/phpunit + +# Run tests with coverage (requires Xdebug) +XDEBUG_MODE=coverage php -dzend_extension=xdebug.so vendor/bin/phpunit +# Coverage output: tests/coverage/ + +# Run specific test file +vendor/bin/phpunit tests/Service/NormalizerTest.php +``` + +### Code Quality Tools + +```bash +# Static analysis (level 3) +vendor/bin/phpstan + +# Code style fixing +vendor/bin/php-cs-fixer fix + +# Automated refactoring (dry-run recommended first) +vendor/bin/rector process --dry-run --no-progress-bar --ansi + +# Apply rector changes +vendor/bin/rector process +``` + +### Running GitHub Actions Locally with act + +You can run the complete CI pipeline locally using [act](https://nektosact.com). +If act isn't installed, try to install it (on macOS via homebrew). + +```bash +# Run CI with specific PHP version +act -j build --matrix php-version:7.4 + +# Run CI with PHP 8.2 +act -j build --matrix php-version:8.2 + +# Run all PHP versions (7.2, 7.4, 8.2) +act -j build + +# List available jobs +act --list +``` + +**Codecov Token**: To upload coverage to Codecov, create a `.env.local` file in the project root (copy from `.env`): +```bash +cp .env .env.local +# Edit .env.local and add your token: +CODECOV_TOKEN=your-codecov-token-here +``` +The `.env.local` file is gitignored and will be automatically picked up by act. The `.env` file is committed as a template. + +**Note**: The first run will be slow as it downloads the container image and sets up PHP. Subsequent runs are much faster due to container reuse (configured in `~/Library/Application Support/act/actrc`). + +**Configuration**: act is configured to: +- Use `catthehacker/ubuntu:act-latest` as the base image (medium size) +- Reuse containers between runs for faster execution +- Use linux/amd64 architecture (required for M-series Macs) +- Read environment variables from `.env.local` only (`.env` is just a template) + +## Architecture + +### Core Components + +**Normalizer** (`src/Service/Normalizer.php`) +- Entry point for normalization operations +- Handles both single objects and collections +- Manages ObjectCache for circular reference detection +- Delegates to PropertyNormalizer and MethodNormalizer + +**Serializer** (`src/Service/Serializer.php`) +- Wraps Normalizer with encoding capabilities (JSON, XML) +- Uses EncoderFactory to create encoders +- Supports sorting via `@Serialize` annotation + +**ObjectCache** (`src/Model/ObjectCache.php`) +- Static cache preventing circular references +- Caches normalized results by object name and identifier (from `getId()`) +- Must be cleared between normalize operations + +### Annotation System + +Three main annotations in `src/Annotation/`: + +**@Normalize** - Properties/methods to include in normalization +- `name`: Output key name +- `group`: Array of context groups +- `type`: Special handling (collection, datetime, object) +- `format`: Date format for datetime types +- `callback`: Method to call for value transformation +- `normalizeCallbackResult`: Whether to normalize callback return value +- `skipEmpty`: Skip if value is empty +- `maxDepth`: Limit object nesting depth + +**@Serialize** - Class-level serialization configuration +- `sortProperties`: Sort output keys alphabetically +- `group`: Context group for serialization + +**@Translate** - Translate values using Symfony translator +- `locale`: Translation locale +- `domain`: Translation domain (filename) + +### Extractors + +Located in `src/Service/Extractor/`: +- `AnnotationExtractor`: Parses annotations from classes/properties/methods +- `PropertyExtractor`: Extracts property metadata +- `MethodExtractor`: Extracts method metadata +- `ClassExtractor`: Coordinates extraction process + +### Normalizers + +Located in `src/Service/Normalize/`: +- `PropertyNormalizer`: Normalizes object properties +- `MethodNormalizer`: Normalizes method return values +- Both extend `AbstractNormalizer` which handles type-specific normalization logic + +### Encoders + +Located in `src/Service/Encoder/`: +- `EncoderJson`: JSON encoding +- `EncoderXml`: XML encoding +- `EncoderFactory`: Creates encoder instances by type string + +## Important Patterns + +### Context Groups +Annotations use `group` parameter to control normalization context: +```php +@Normalize(name="email", group={"api", "admin"}) +@Normalize(name="internalId", group={"admin"}) +``` +When normalizing, pass group to include only matching annotations. + +### Circular Reference Handling +Objects implementing `getId()` are cached and reused. If a circular reference is detected, the object's ID value is returned instead of re-normalizing. + +### Doctrine Proxy Support +The bundle handles Doctrine proxy objects by extracting the real class name before processing. + +### Additional PHP Coding Standards + +#### General PHP standards + +- `declare(strict_types=1);` MUST be declared at the top for *new* PHP files +- Short array notation MUST be used +- Use Monolog for all logging operations (Psr\Log\LoggerInterface) +- Declare statements MUST be terminated by a semicolon +- Classes from the global namespace MUST NOT be imported (but prefixed with \) +- Import statements MUST be alphabetized +- The PHP features "parameter type widening" and "contravariant argument types" MUST NOT be used +- Boolean operators between conditions MUST always be at the beginning of the line +- The concatenation operator MUST be preceded and followed by one space +- Do not use YODA conditions +- All boolean API property names MUST be prepended with "is", "has" or "should" + +#### PHPDoc + +- There MUST NOT be an @author, @version or @copyright tag +- Extended type information SHOULD be used when possible (PHPStan array types) +- Arguments MUST be documented as relaxed as possible, while return values MUST be documented as precise as possible +- The @param tag MUST be omitted when the argument is properly type-hinted +- The @return tag MUST be omitted when the method or function has a proper return type-hint +- An entire docblock MUST be omitted in case it does not add "any value" over the method name, argument types and return type +- Constants MUST NOT be documented using the @var tag + +#### PHPUnit + +- We use Mockery for mocking, so all tests that use Mockery MUST extend Mockery\Adapter\Phpunit\MockeryTestCase +- Unit tests MUST not use the `@testdox` tag, but use a descriptive human-readable test method name instead +- Data providers MUST be used when possible and applicable +- All unit test class members MUST be unset in the `tearDown()` method + +## Configuration Files + +- `phpunit.xml`: Test configuration, excludes DI/EventListener/Exception/Model from coverage +- `.php-cs-fixer.php`: Custom finder supporting Git diff, STDIN, or CLI input; PSR-2/Symfony standards +- `rector.php`: Configured for PHP 7.2+ and Symfony 5.4 +- `phpstan.neon.dist`: Level 3 analysis, Symfony extension enabled + +## Testing + +Tests use fixtures in `tests/assets/` directory (Person, Address, Social, etc.). The `NormalizerTestTrait` provides common test setup for service instantiation. + +Test structure mirrors source: `tests/Service/`, `tests/Annotation/`, etc. diff --git a/rector.php b/rector.php index 027b024..d1f4505 100644 --- a/rector.php +++ b/rector.php @@ -4,7 +4,6 @@ use Rector\Config\RectorConfig; use Rector\Set\ValueObject\LevelSetList; -use Rector\Symfony\Set\SymfonyLevelSetList; return static function (RectorConfig $rectorConfig): void { @@ -21,7 +20,6 @@ $rectorConfig->sets([ LevelSetList::UP_TO_PHP_72, - SymfonyLevelSetList::UP_TO_SYMFONY_54, ]); $rectorConfig->importNames(true, false); From cd3f49ff98edc881acdf25ebd125b09d0d31f6d9 Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 29 Nov 2025 23:38:09 +0100 Subject: [PATCH 2/4] ISSUE-75: Fix cache for CI --- .github/workflows/ci.yaml | 2 +- CLAUDE.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 21d5cf9..44d8c5a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -30,7 +30,7 @@ jobs: coverage: xdebug - name: Cache Composer dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: $HOME/.composer/cache key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} diff --git a/CLAUDE.md b/CLAUDE.md index 7101ff2..4d22e66 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,7 +75,7 @@ The `.env.local` file is gitignored and will be automatically picked up by act. **Note**: The first run will be slow as it downloads the container image and sets up PHP. Subsequent runs are much faster due to container reuse (configured in `~/Library/Application Support/act/actrc`). -**Configuration**: act is configured to: +**Configuration**: act MUST be configured to: - Use `catthehacker/ubuntu:act-latest` as the base image (medium size) - Reuse containers between runs for faster execution - Use linux/amd64 architecture (required for M-series Macs) From 51e29121293979bd80c36ca50f0dc33477008bb9 Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 29 Nov 2025 23:42:48 +0100 Subject: [PATCH 3/4] ISSUE-75: Add .env template --- .env | 1 + 1 file changed, 1 insertion(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..6f85717 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +CODECOV_TOKEN= From c81c971a24149a594515c0baccb2702f69e0e9ec Mon Sep 17 00:00:00 2001 From: BowlOfSoup Date: Sat, 29 Nov 2025 23:51:31 +0100 Subject: [PATCH 4/4] ISSUE-75: Small changes to CLAUDE.md --- CLAUDE.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4d22e66..0791d4f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,7 +73,7 @@ CODECOV_TOKEN=your-codecov-token-here ``` The `.env.local` file is gitignored and will be automatically picked up by act. The `.env` file is committed as a template. -**Note**: The first run will be slow as it downloads the container image and sets up PHP. Subsequent runs are much faster due to container reuse (configured in `~/Library/Application Support/act/actrc`). +**Note**: The first run will be slow as it downloads the container image and sets up PHP. Subsequent runs are much faster due to container reuse; configure this! **Configuration**: act MUST be configured to: - Use `catthehacker/ubuntu:act-latest` as the base image (medium size) @@ -96,11 +96,6 @@ The `.env.local` file is gitignored and will be automatically picked up by act. - Uses EncoderFactory to create encoders - Supports sorting via `@Serialize` annotation -**ObjectCache** (`src/Model/ObjectCache.php`) -- Static cache preventing circular references -- Caches normalized results by object name and identifier (from `getId()`) -- Must be cleared between normalize operations - ### Annotation System Three main annotations in `src/Annotation/`: