diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 29cccba..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,203 +0,0 @@ -# Copilot Instructions for ruudk/code-generator - -## Repository Summary - -This is a PHP library that revolutionizes code generation through generators and yield syntax. The `CodeGenerator` class allows developers to yield code line by line while automatically handling indentation, namespace imports, and formatting. Instead of manual string concatenation, developers can write readable generator functions that produce clean, well-formatted PHP code with proper imports and consistent structure. - -Key use cases include generating PHP classes, interfaces, enums, traits, and functions with automatic namespace management and beautiful formatting. - -## High-Level Repository Information - -- **Type**: PHP Composer library/package -- **PHP Version**: Requires PHP 8.4+ (uses property hooks syntax) -- **Size**: Small focused library (~7 source files, ~13 test files) -- **Languages**: PHP only -- **Framework**: Pure PHP with no external runtime dependencies -- **Package Manager**: Composer -- **Target Runtime**: PHP 8.4+ CLI and web environments -- **Namespace**: `Ruudk\CodeGenerator` - -## Build and Validation Instructions - -**CRITICAL**: This project requires PHP 8.4+. All commands will fail with syntax errors on PHP 8.3 or lower due to property hooks usage. - -### Prerequisites -```bash -# Ensure PHP 8.4+ is available -php --version # Must show 8.4+ - -# Install dependencies -composer install -``` - -### Important: Composer Dependency Management - -**NEVER run `composer update`** unless explicitly requested. This command updates all dependencies to their latest versions and modifies `composer.lock`, which should only be done intentionally by the project maintainer. - -- **Use `composer install`** for normal dependency installation - this respects the locked versions -- **Composer modifications** (adding/removing packages) should only be done when explicitly requested -- **Never commit `composer.lock` changes** unless explicitly asked to update dependencies - -### Complete Validation Sequence (CI Pipeline Order) - -The following commands MUST be run in this exact order. Each command must pass before proceeding: - -1. **Run Code Style Fixer** - ```bash - vendor/bin/php-cs-fixer check --diff - ``` - - Takes 5-10 seconds - - Validates PSR-12 and custom coding standards - - To fix issues: `vendor/bin/php-cs-fixer fix` - -2. **Run Static Analysis** - ```bash - vendor/bin/phpstan analyse - ``` - - Takes 10-15 seconds - - Runs at level 9 (strictest) - - Configuration in phpstan.php - -3. **Run Unit Tests** - ```bash - vendor/bin/phpunit - ``` - - Takes 5-10 seconds - - Must have 100% pass rate - - Configuration in phpunit.xml - -### Development Workflow - -1. **Make code changes** -2. **Auto-fix style issues**: `vendor/bin/php-cs-fixer fix` -3. **Run validation sequence** (all commands above) -4. **Test examples** to ensure they work -5. **Commit only if all validations pass** - -### Common Issues and Workarounds - -- **PHP Version Error**: Project requires PHP 8.4+ due to property hooks syntax. Cannot run on older PHP versions. -- **Memory Issues**: Set `memory_limit=-1` for PHPUnit if needed -- **Lock File Issues**: Run `composer install` before any validation commands -- **Style Failures**: Run `vendor/bin/php-cs-fixer fix` to auto-fix most issues -- **Example Failures**: Examples must execute without errors; they test real library usage - -## Project Layout and Architecture - -### Core Architecture - -The library follows a simple yet powerful architecture: - -- **`CodeGenerator`**: Main class for code generation and namespace management -- **`Group`**: Handles indentation and nesting of code blocks -- **`FullyQualified`**: Represents fully qualified class names -- **`ClassName`**: Validates and represents class names -- **`NamespaceName`**: Handles namespace parsing and validation -- **`FunctionName`**: Represents function names for imports -- **`Alias`**: Handles aliased imports - -### Directory Structure - -``` -/ -├── .github/ -│ ├── workflows/ci.yml # GitHub Actions CI pipeline -│ └── FUNDING.yml # Sponsorship info -├── src/ # Main source code (7 files) -│ ├── CodeGenerator.php # Core generator class -│ ├── Group.php # Indentation/grouping -│ ├── FullyQualified.php # FQCN handling -│ ├── ClassName.php # Class name validation -│ ├── NamespaceName.php # Namespace management -│ ├── FunctionName.php # Function imports -│ └── Alias.php # Import aliases -├── tests/ # Unit tests (matches src structure) -│ ├── CodeGeneratorTest.php # Core functionality tests -│ ├── GroupTest.php # Grouping tests -│ ├── FullyQualifiedTest.php # FQCN tests -│ ├── ClassNameTest.php # Class name tests -│ ├── NamespaceNameTest.php # Namespace tests -│ ├── FunctionNameTest.php # Function tests -│ └── Fixtures/TestEnum.php # Test fixtures -├── examples/ # Working examples -│ ├── example.php # Comprehensive feature demo -│ └── class.php # Simple class generation -├── .php-cs-fixer.php # Code style configuration -├── phpstan.php # Static analysis config -├── phpstan.neon # PHPStan include file -├── phpunit.xml # Test configuration -├── captainhook.json # Git hooks configuration -├── composer-dependency-analyser.php # Dependency analysis config -└── composer.json # Project dependencies -``` - -### Configuration Files - -- **`.php-cs-fixer.php`**: PHP CS Fixer rules using TicketSwap ruleset -- **`phpstan.php`**: PHPStan level 9 with strict rules and custom error formatter -- **`phpunit.xml`**: PHPUnit configuration with strict settings -- **`captainhook.json`**: Pre-commit hooks that run full validation suite -- **`composer-dependency-analyser.php`**: Dependency analysis configuration - -### CI/CD Pipeline - -GitHub Actions workflow (`.github/workflows/ci.yml`): -- Runs on Ubuntu latest with PHP 8.4 -- Matrix strategy for PHP versions -- Caches Composer dependencies -- Executes complete validation sequence -- Must pass for all commits to main branch - -### Key Dependencies - -**Runtime**: None (pure PHP library) - -**Development**: -- `phpunit/phpunit`: Unit testing framework -- `phpstan/phpstan`: Static analysis at level 9 -- `friendsofphp/php-cs-fixer`: Code style enforcement -- `captainhook/captainhook-phar`: Git hooks -- `shipmonk/composer-dependency-analyser`: Dependency validation -- `ergebnis/composer-normalize`: Composer.json formatting - -### Usage Patterns - -The library is designed around generator functions that yield lines of code: - -```php -$generator = new CodeGenerator('App\\Services'); - -echo $generator->dumpFile(function() use ($generator) { - yield 'class UserService'; - yield '{'; - yield $generator->indent(function() use ($generator) { - yield sprintf('private %s $repository;', $generator->import('App\\Repository\\UserRepository')); - yield ''; - yield 'public function find(int $id): ?User'; - yield '{'; - yield $generator->indent([ - 'return $this->repository->find($id);' - ]); - yield '}'; - }); - yield '}'; -}); -``` - -### File Modification Guidelines - -- **Source files** (`src/`): Core library functionality - modify carefully -- **Test files** (`tests/`): Add tests for new features, maintain coverage -- **Examples** (`examples/`): Must remain functional - they're validated in CI -- **Config files**: Changes may affect entire validation pipeline -- **README.md**: Keep examples in sync with actual working code - -### Trust These Instructions - -These instructions are comprehensive and tested. Only search for additional information if: -- Commands fail with unexpected errors -- New dependencies are added to composer.json -- PHP version requirements change -- CI pipeline is modified - -The validation sequence provided is the authoritative build process used by the project maintainer and CI system. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7dd375..f32a837 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,11 @@ jobs: - name: Check composer.json normalization run: composer normalize --diff --dry-run + - name: Check README examples are in sync + run: | + vendor/bin/readme-examples-sync + git diff --exit-code README.md || (echo "README.md is out of sync with example files. Run 'vendor/bin/readme-examples-sync' and commit the changes." && exit 1) + - name: Check dependencies run: vendor/bin/composer-dependency-analyser diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml deleted file mode 100644 index 10339e4..0000000 --- a/.github/workflows/copilot-setup-steps.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: "Copilot Setup Steps" - -# Automatically run the setup steps when they are changed to allow for easy validation, and -# allow manual testing through the repository's "Actions" tab -on: - workflow_dispatch: - push: - paths: - - .github/workflows/copilot-setup-steps.yml - pull_request: - paths: - - .github/workflows/copilot-setup-steps.yml - -jobs: - # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. - copilot-setup-steps: - runs-on: ubuntu-latest - - # Set the permissions to the lowest permissions possible needed for your steps. - # Copilot will be given its own token for its operations. - permissions: - # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. - contents: read - - # You can define any steps you want, and they will run before the agent starts. - # If you do not check out your code, Copilot will do this for you. - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup PHP 8.4 - uses: shivammathur/setup-php@v2 - with: - php-version: '8.4' - coverage: none - - - uses: "ramsey/composer-install@v3" diff --git a/captainhook.json b/captainhook.json deleted file mode 100644 index a9df0d7..0000000 --- a/captainhook.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "commit-msg": { - "enabled": true, - "actions": [] - }, - "pre-commit": { - "enabled": true, - "actions": [ - { - "action": "composer validate --strict" - }, - { - "action": "composer normalize --diff --dry-run" - }, - { - "action": "vendor/bin/composer-dependency-analyser" - }, - { - "action": "\\CaptainHook\\App\\Hook\\PHP\\Action\\Linting" - }, - { - "action": "cd examples && for file in *.php; do echo \"Testing examples/$file...\"; php \"$file\" > /dev/null 2>&1 || { echo \"✗ Failed: examples/$file\"; exit 1; }; echo \"✓ Passed: examples/$file\"; done", - "options": { - "label": "Validate example files" - } - }, - { - "action": "\\Ruudk\\ReadmeExamplesSyncHook\\SyncReadmeExamples" - }, - { - "action": "vendor/bin/php-cs-fixer check --diff" - }, - { - "action": "vendor/bin/phpstan" - }, - { - "action": "vendor/bin/phpunit" - } - ] - } -} diff --git a/composer.json b/composer.json index a91b283..440fe2c 100644 --- a/composer.json +++ b/composer.json @@ -17,14 +17,13 @@ "php": "^8.4" }, "require-dev": { - "captainhook/captainhook-phar": "^5.25", "ergebnis/composer-normalize": "^2.47", "friendsofphp/php-cs-fixer": "^3.85", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^12.3", - "ruudk/readme-examples-sync-hook": "^1.0", + "ruudk/readme-examples-sync-hook": "^2.0", "shipmonk/composer-dependency-analyser": "^1.8", "ticketswap/php-cs-fixer-config": "^1.0", "ticketswap/phpstan-error-formatter": "^1.1" @@ -41,9 +40,13 @@ }, "config": { "allow-plugins": { - "captainhook/captainhook-phar": true, "ergebnis/composer-normalize": true, "phpstan/extension-installer": true } + }, + "scripts": { + "post-install-cmd": [ + "command -v lefthook >/dev/null 2>&1 && lefthook install || true" + ] } } diff --git a/composer.lock b/composer.lock index 18fce8e..0f47549 100644 --- a/composer.lock +++ b/composer.lock @@ -4,75 +4,9 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "169e60238e2c2aafa7743e7ce581e4fe", + "content-hash": "c0a05d79482be61f67f7c3e79e6e78bc", "packages": [], "packages-dev": [ - { - "name": "captainhook/captainhook-phar", - "version": "5.25.11", - "source": { - "type": "git", - "url": "https://github.com/captainhook-git/captainhook-phar.git", - "reference": "a5dbcd8d20b3dcdb1cbd6948d0d3a058453b3d6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/captainhook-git/captainhook-phar/zipball/a5dbcd8d20b3dcdb1cbd6948d0d3a058453b3d6a", - "reference": "a5dbcd8d20b3dcdb1cbd6948d0d3a058453b3d6a", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1||^2.0", - "ext-json": "*", - "ext-spl": "*", - "phar-io/composer-distributor": "^1.0.1", - "php": ">=8.0" - }, - "require-dev": { - "composer/composer": "^1.1 || ^2.0" - }, - "type": "composer-plugin", - "extra": { - "class": "CaptainHook\\Composer\\Plugin" - }, - "autoload": { - "psr-4": { - "CaptainHook\\Composer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Sebastian Feldmann", - "email": "sf@sebastian-feldmann.info" - } - ], - "description": "PHP git hook manager", - "homepage": "https://github.com/captainhook-git/captainhook", - "keywords": [ - "commit-msg", - "git", - "hooks", - "post-merge", - "pre-commit", - "pre-push", - "prepare-commit-msg" - ], - "support": { - "issues": "https://github.com/captainhook-git/captainhook/issues", - "source": "https://github.com/captainhook-git/captainhook-phar/tree/5.25.11" - }, - "funding": [ - { - "url": "https://github.com/sponsors/sebastianfeldmann", - "type": "github" - } - ], - "time": "2025-04-08T07:07:48+00:00" - }, { "name": "clue/ndjson-react", "version": "v1.3.0", @@ -1516,220 +1450,6 @@ }, "time": "2025-07-27T20:03:57+00:00" }, - { - "name": "phar-io/composer-distributor", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/phar-io/composer-distributor.git", - "reference": "dd7d936290b2a42b0c64bfe08090b5c597c280c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/composer-distributor/zipball/dd7d936290b2a42b0c64bfe08090b5c597c280c9", - "reference": "dd7d936290b2a42b0c64bfe08090b5c597c280c9", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1 || ^2.0", - "ext-dom": "*", - "ext-libxml": "*", - "phar-io/filesystem": "^2.0", - "phar-io/gnupg": "^1.0", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "composer/composer": "^2.0", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "PharIo\\ComposerDistributor\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Andreas Heigl", - "email": "andreas@heigl.org", - "role": "Developer" - }, - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Feldmann", - "email": "sf@sebastian-feldmann.info", - "role": "Developer" - } - ], - "description": "Base Code for a composer plugin that installs PHAR-files", - "homepage": "https://phar.io", - "keywords": [ - "bin", - "binary", - "composer", - "distribute", - "phar", - "phive" - ], - "support": { - "issues": "https://github.com/phar-io/composer-distributor/issues", - "source": "https://github.com/phar-io/composer-distributor/tree/1.0.2" - }, - "funding": [ - { - "url": "https://phar.io", - "type": "other" - } - ], - "time": "2023-05-31T17:05:49+00:00" - }, - { - "name": "phar-io/executor", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/executor.git", - "reference": "5bfb7400224a0c1cf83343660af85c7f5a073473" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/executor/zipball/5bfb7400224a0c1cf83343660af85c7f5a073473", - "reference": "5bfb7400224a0c1cf83343660af85c7f5a073473", - "shasum": "" - }, - "require": { - "phar-io/filesystem": "^2.0", - "php": "^7.2||^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - } - ], - "support": { - "issues": "https://github.com/phar-io/executor/issues", - "source": "https://github.com/phar-io/executor/tree/1.0.1" - }, - "time": "2020-11-30T10:53:57+00:00" - }, - { - "name": "phar-io/filesystem", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/filesystem.git", - "reference": "222e3ea432262a05706b7066697c21257664d9d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/filesystem/zipball/222e3ea432262a05706b7066697c21257664d9d1", - "reference": "222e3ea432262a05706b7066697c21257664d9d1", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - } - ], - "support": { - "issues": "https://github.com/phar-io/filesystem/issues", - "source": "https://github.com/phar-io/filesystem/tree/2.0.1" - }, - "time": "2020-11-30T10:16:22+00:00" - }, - { - "name": "phar-io/gnupg", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/gnupg.git", - "reference": "ed8ab1740ac4e9db99500e7252911f2821357093" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/gnupg/zipball/ed8ab1740ac4e9db99500e7252911f2821357093", - "reference": "ed8ab1740ac4e9db99500e7252911f2821357093", - "shasum": "" - }, - "require": { - "phar-io/executor": "^1.0", - "phar-io/filesystem": "^2.0", - "php": "^7.2||^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - } - ], - "description": "Thin GnuPG wrapper class around the gnupg binary, mimicking the pecl/gnupg api", - "support": { - "issues": "https://github.com/phar-io/gnupg/issues", - "source": "https://github.com/phar-io/gnupg/tree/1.0.3" - }, - "time": "2024-08-22T20:45:57+00:00" - }, { "name": "phar-io/manifest", "version": "2.0.4", @@ -3127,30 +2847,32 @@ }, { "name": "ruudk/readme-examples-sync-hook", - "version": "1.0.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/ruudk/readme-examples-sync-hook.git", - "reference": "7ba5a64d4bd33ef538de90f95674e343ab36fa1e" + "reference": "a7a73379aa6608534362cb96879e6430b4ddecdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ruudk/readme-examples-sync-hook/zipball/7ba5a64d4bd33ef538de90f95674e343ab36fa1e", - "reference": "7ba5a64d4bd33ef538de90f95674e343ab36fa1e", + "url": "https://api.github.com/repos/ruudk/readme-examples-sync-hook/zipball/a7a73379aa6608534362cb96879e6430b4ddecdd", + "reference": "a7a73379aa6608534362cb96879e6430b4ddecdd", "shasum": "" }, "require": { "php": "^8.4" }, "require-dev": { - "captainhook/captainhook": "^5.25", - "ergebnis/composer-normalize": "^2.47", - "erickskrauch/php-cs-fixer-custom-fixers": "^1.3", + "ergebnis/composer-normalize": "^2.48", "friendsofphp/php-cs-fixer": "^3.85", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.1", + "ticketswap/php-cs-fixer-config": "^1.0", "ticketswap/phpstan-error-formatter": "^1.1" }, + "bin": [ + "bin/readme-examples-sync" + ], "type": "library", "autoload": { "psr-4": { @@ -3173,9 +2895,9 @@ ], "support": { "issues": "https://github.com/ruudk/readme-examples-sync-hook/issues", - "source": "https://github.com/ruudk/readme-examples-sync-hook/tree/1.0.0" + "source": "https://github.com/ruudk/readme-examples-sync-hook/tree/2.0.0" }, - "time": "2025-08-13T07:46:22+00:00" + "time": "2025-10-22T17:24:46+00:00" }, { "name": "sebastian/cli-parser", diff --git a/lefthook.yaml b/lefthook.yaml new file mode 100644 index 0000000..9c6b4d7 --- /dev/null +++ b/lefthook.yaml @@ -0,0 +1,61 @@ +pre-commit: + parallel: false + commands: + ensure-sync: + glob: "*.php" + run: bin/graphql-client-code-generator --config=examples/config.php --config=tests/config.php --ensure-sync + + validate-examples: + glob: + - "*.php" + run: | + cd examples && for file in *.php; do + echo "Testing examples/$file..." + php "$file" > /dev/null 2>&1 || { echo "✗ Failed: examples/$file"; exit 1; } + echo "✓ Passed: examples/$file" + done + + sync-readme-examples: + glob: + - "*.php" + - "*.md" + run: vendor/bin/readme-examples-sync + stage_fixed: true + + composer-validate: + glob: + - "composer.json" + - "composer.lock" + run: composer validate --strict + + composer-normalize: + glob: "composer.json" + run: composer normalize --diff + stage_fixed: true + + composer-dependency-analyser: + glob: + - "*.php" + - "composer.json" + - "composer.lock" + run: vendor/bin/composer-dependency-analyser + + php-cs-fixer: + glob: "*.php" + run: vendor/bin/php-cs-fixer fix --config .php-cs-fixer.php -- {staged_files} + stage_fixed: true + + twig-cs-fixer: + glob: "*.twig" + run: vendor/bin/twig-cs-fixer fix -- {staged_files} + stage_fixed: true + + phpstan: + glob: + - "*.php" + - "*.neon" + run: vendor/bin/phpstan + + phpunit: + glob: "*.php" + run: vendor/bin/phpunit