wexample-wex-addon-dev-php 12.0.3__tar.gz → 12.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/PKG-INFO +8 -8
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/README.md +4 -4
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/pyproject.toml +7 -4
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/resources/design_rules/php_code.md +11 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/agents/editor/agent.yml +1 -1
- wexample_wex_addon_dev_php-12.2.0/src/wexample_wex_addon_dev_php/services/symfony/knowledge/entity.md +80 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/workdir/php_package_workdir.py +37 -2
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/workdir/php_symfony_workdir.py +167 -0
- wexample_wex_addon_dev_php-12.2.0/tests/workdir/__init__.py +0 -0
- wexample_wex_addon_dev_php-12.2.0/tests/workdir/test_php_package_workdir.py +54 -0
- wexample_wex_addon_dev_php-12.0.3/src/wexample_wex_addon_dev_php/services/symfony/knowledge/entity.md +0 -4
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/__pycache__/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/config_value/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/config_value/php_package_readme_config_value.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/const/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/const/tags.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/file/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/file/php_composer_json_file.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/formatter/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/formatter/php_code_formatter.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/php_addon_manager.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/py.typed +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/resources/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/resources/docker/Dockerfile.roave +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/resources/writing_rules/php_code.md +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/composer/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/composer/commands/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/composer/commands/service/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/composer/commands/service/install_local.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/composer/commands/service/refresh_lock.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/composer/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/composer/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/composer/service.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/caddy/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/caddy/Caddyfile +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/dev/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/dev/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/local/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/local/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/prod/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/prod/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/php/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/php/web.ini +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/frankenphp/service.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/app_service.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/db/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/db/migrate.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance/disable.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance/enable.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/service/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/service/install_local.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/service/refresh_lock.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/samples/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/samples/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/samples/env/dev/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/samples/env/prod/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/laravel/service.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/ai/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/ai/shared/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/ai/shared/context.j2 +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/ai/shared/php.j2 +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/caddy/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/caddy/Caddyfile +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/dev/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/dev/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/local/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/local/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/prod/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/prod/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/php/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/samples/php/web.ini +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/php/service.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/commands/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/commands/runtime/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/env/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/env/local/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/service.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/agents/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/agents/editor/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/agents/editor/context.j2 +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/shared/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/shared/context.j2 +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/app_service.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/service/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/service/deploy.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/service/install_local.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/service/watch.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/knowledge/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/env.build +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/images/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/images/Dockerfile.dev +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/images/Dockerfile.release +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/dev/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/dev/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/local/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/local/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/prod/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/prod/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/symfony/service.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/commands/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/commands/service/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/commands/url/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/commands/url/replace.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/docker/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/.wex.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/docker/docker-compose-cli.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/env/dev/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/env/local/wp-cli/.gitkeep +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/env/prod/docker/docker-compose.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/services/wordpress/service.yml +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/workdir/__init__.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/workdir/php_packages_suite_workdir.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/src/wexample_wex_addon_dev_php/workdir/php_workdir.py +0 -0
- {wexample_wex_addon_dev_php-12.0.3 → wexample_wex_addon_dev_php-12.2.0}/tests/.gitkeep +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wexample-wex-addon-dev-php
|
|
3
|
-
Version: 12.0
|
|
3
|
+
Version: 12.2.0
|
|
4
4
|
Summary: Extends wex with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers
|
|
5
5
|
Author-Email: weeger <contact@wexample.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,9 +12,9 @@ Requires-Python: >=3.10
|
|
|
12
12
|
Requires-Dist: attrs>=23.1.0
|
|
13
13
|
Requires-Dist: cattrs>=23.1.0
|
|
14
14
|
Requires-Dist: wexample-filestate-php>=6.4.0
|
|
15
|
-
Requires-Dist: wexample-filestate-symfony>=
|
|
16
|
-
Requires-Dist: wexample-wex-addon-ai>=
|
|
17
|
-
Requires-Dist: wexample-wex-addon-app>=31.
|
|
15
|
+
Requires-Dist: wexample-filestate-symfony>=2.0.0
|
|
16
|
+
Requires-Dist: wexample-wex-addon-ai>=15.0.0
|
|
17
|
+
Requires-Dist: wexample-wex-addon-app>=31.1.0
|
|
18
18
|
Requires-Dist: wexample-wex-addon-dev-javascript>=8.1.0
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -23,7 +23,7 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
|
|
24
24
|
# wex_addon_dev_php
|
|
25
25
|
|
|
26
|
-
Version: 12.0
|
|
26
|
+
Version: 12.2.0
|
|
27
27
|
|
|
28
28
|
`wex_addon_dev_php` extends the wex CLI with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers. It is aimed at PHP developers who manage their apps through wex and need framework-aware automation — dependency wiring, migration runs, lock-file refreshes — executed directly inside the running service containers. The addon registers four workdir types (`php`, `php-package`, `php-packages-suite`, `php-symfony`) and contributes its commands through the standard `PhpAddonManager` plugin point.
|
|
29
29
|
|
|
@@ -274,9 +274,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
274
274
|
- attrs: >=23.1.0
|
|
275
275
|
- cattrs: >=23.1.0
|
|
276
276
|
- wexample-filestate-php: >=6.4.0
|
|
277
|
-
- wexample-filestate-symfony: >=
|
|
278
|
-
- wexample-wex-addon-ai: >=
|
|
279
|
-
- wexample-wex-addon-app: >=31.
|
|
277
|
+
- wexample-filestate-symfony: >=2.0.0
|
|
278
|
+
- wexample-wex-addon-ai: >=15.0.0
|
|
279
|
+
- wexample-wex-addon-app: >=31.1.0
|
|
280
280
|
- wexample-wex-addon-dev-javascript: >=8.1.0
|
|
281
281
|
|
|
282
282
|
## Versioning & Compatibility Policy
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# wex_addon_dev_php
|
|
2
2
|
|
|
3
|
-
Version: 12.0
|
|
3
|
+
Version: 12.2.0
|
|
4
4
|
|
|
5
5
|
`wex_addon_dev_php` extends the wex CLI with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers. It is aimed at PHP developers who manage their apps through wex and need framework-aware automation — dependency wiring, migration runs, lock-file refreshes — executed directly inside the running service containers. The addon registers four workdir types (`php`, `php-package`, `php-packages-suite`, `php-symfony`) and contributes its commands through the standard `PhpAddonManager` plugin point.
|
|
6
6
|
|
|
@@ -251,9 +251,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
251
251
|
- attrs: >=23.1.0
|
|
252
252
|
- cattrs: >=23.1.0
|
|
253
253
|
- wexample-filestate-php: >=6.4.0
|
|
254
|
-
- wexample-filestate-symfony: >=
|
|
255
|
-
- wexample-wex-addon-ai: >=
|
|
256
|
-
- wexample-wex-addon-app: >=31.
|
|
254
|
+
- wexample-filestate-symfony: >=2.0.0
|
|
255
|
+
- wexample-wex-addon-ai: >=15.0.0
|
|
256
|
+
- wexample-wex-addon-app: >=31.1.0
|
|
257
257
|
- wexample-wex-addon-dev-javascript: >=8.1.0
|
|
258
258
|
|
|
259
259
|
## Versioning & Compatibility Policy
|
|
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "wexample-wex-addon-dev-php"
|
|
9
|
-
version = "12.0
|
|
9
|
+
version = "12.2.0"
|
|
10
10
|
description = "Extends wex with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers"
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "weeger", email = "contact@wexample.com" },
|
|
@@ -21,9 +21,9 @@ dependencies = [
|
|
|
21
21
|
"attrs>=23.1.0",
|
|
22
22
|
"cattrs>=23.1.0",
|
|
23
23
|
"wexample-filestate-php>=6.4.0",
|
|
24
|
-
"wexample-filestate-symfony>=
|
|
25
|
-
"wexample-wex-addon-ai>=
|
|
26
|
-
"wexample-wex-addon-app>=31.
|
|
24
|
+
"wexample-filestate-symfony>=2.0.0",
|
|
25
|
+
"wexample-wex-addon-ai>=15.0.0",
|
|
26
|
+
"wexample-wex-addon-app>=31.1.0",
|
|
27
27
|
"wexample-wex-addon-dev-javascript>=8.1.0",
|
|
28
28
|
]
|
|
29
29
|
|
|
@@ -43,6 +43,9 @@ dev = [
|
|
|
43
43
|
"pytest-cov",
|
|
44
44
|
]
|
|
45
45
|
|
|
46
|
+
[project.entry-points."wex.addons"]
|
|
47
|
+
php = "wexample_wex_addon_dev_php.php_addon_manager:PhpAddonManager"
|
|
48
|
+
|
|
46
49
|
[tool.setuptools.packages.find]
|
|
47
50
|
include = [
|
|
48
51
|
"*",
|
|
@@ -15,6 +15,17 @@ is already a decision made.
|
|
|
15
15
|
- Suffix the name with the kind where the kind is the whole point: `ArrayHelper`,
|
|
16
16
|
`WithDomId` for a trait, `AbstractFromArrayObject` for a base class. The reader picks
|
|
17
17
|
up what a class is for from its name before opening it.
|
|
18
|
+
- A route says who it is for in its **path**, never in its name. A path opening on an
|
|
19
|
+
underscore — `/_system/component`, `/_forms/submit`, `/_live/subscribe-info` — is
|
|
20
|
+
reached by a program; one without is a page someone opens and reads. Route *names*
|
|
21
|
+
never carry that underscore, whichever kind they are: `system_component_render`, not
|
|
22
|
+
`_system_component_render`. The rule reads in one direction only, so a name can be
|
|
23
|
+
renamed without moving a url and a page can be turned into an endpoint without
|
|
24
|
+
touching every `path()` call. Symfony's own `_profiler` and `_wdt` carry it on both
|
|
25
|
+
and are left alone.
|
|
26
|
+
The one exception is `/api`, which holds machine endpoints and, at `/api/`, the page
|
|
27
|
+
documenting them. Two natures under one prefix: moving it to `/_api` would rewrite the
|
|
28
|
+
`baseUrl` of every client for a gain nobody reads, so it stays as it is.
|
|
18
29
|
- Helper classes are `final`-shaped by use: only `public static` methods, no state, no
|
|
19
30
|
instantiation. A helper that needs a constructor has stopped being a helper and belongs
|
|
20
31
|
in `Class/`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
description: Edits this app's Symfony code, following the conventions of this stack
|
|
2
|
-
model: claude
|
|
2
|
+
model: claude-sonnet-5
|
|
3
3
|
modes: talk
|
|
4
4
|
# `reset` until extraction exists would drop a conversation before anything took its
|
|
5
5
|
# value out. Switch back once `/session-extract` runs ahead of it.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
A Symfony entity must extend, directly or indirectly, the abstract class
|
|
2
|
+
`Wexample\SymfonyHelpers\Entity\AbstractEntity`. It provides the `id` column, the
|
|
3
|
+
`BaseEntityTrait` and the entity naming/path helpers the rest of the framework relies
|
|
4
|
+
on — an entity that does not extend it is not wired into those conventions.
|
|
5
|
+
|
|
6
|
+
Writing that class is one step of six. The five others are what turn a Doctrine row into
|
|
7
|
+
something the API and the front can address, and each is carried by a package that
|
|
8
|
+
documents it under its own `.wex/knowledge/`.
|
|
9
|
+
|
|
10
|
+
| # | Step | Produces |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| 1 | Write the class and its attributes | `src/Entity/{Entity}.php` |
|
|
13
|
+
| 2 | `wex app::state/rectify` | manipulator trait, repository, and one artefact family per attribute declared |
|
|
14
|
+
| 3 | Write the DTO and return it from the scaffolded normalizer | `src/Api/Dto/{Entity}Dto.php` |
|
|
15
|
+
| 4 | `php bin/console pseudocode:generate:pseudocode` | `pseudocode/entity/{entity}.yml` |
|
|
16
|
+
| 5 | `php bin/console api:export:entities`, then js-api's `generate-entities` and `generate-repositories` | the json schema, the TypeScript entity and its repository |
|
|
17
|
+
| 6 | Consume it from the front | — |
|
|
18
|
+
|
|
19
|
+
Where each one is described:
|
|
20
|
+
|
|
21
|
+
- steps 1 and 2 — `wexample-filestate-symfony`, `cookbook/create-an-entity`. `#[ApiEntity]`,
|
|
22
|
+
`#[EntityForm]`, `#[LinkableEntity]`, `#[ImportableEntity]` and `#[ApiEndpoint]` each
|
|
23
|
+
scaffold their own family; nothing existing is ever rewritten, so a second pass is
|
|
24
|
+
harmless and the generated body is a starting point rather than a contract.
|
|
25
|
+
- step 4 — `wexample/symfony-pseudocode`, `usage/overview`.
|
|
26
|
+
- step 5 — `wexample/symfony-api`, `usage/entity-export-pipeline`. The three commands are
|
|
27
|
+
run by hand from the app, because the first needs a booted kernel to scan. An entity
|
|
28
|
+
belonging to a bundle still has its artefacts belonging to the bundle, which is a matter
|
|
29
|
+
of path arguments — and this is the only part of the chain with traps, listed there.
|
|
30
|
+
- step 6 — `@wexample/js-api`, `usage/overview`.
|
|
31
|
+
|
|
32
|
+
Step 3 is the one piece nothing scaffolds, because the serialized shape is a choice and not
|
|
33
|
+
a deduction. The DTO extends `AbstractEntityDto`, and the generated normalizer returns
|
|
34
|
+
`{Entity}Dto::fromEntity($entity)` instead of the generic array, its return type widened to
|
|
35
|
+
`NormalizableDataInterface`. A DTO property is named after the *entity* property rather than
|
|
36
|
+
the column: that name is the one the step-5 schema makes the browser check.
|
|
37
|
+
|
|
38
|
+
## Nothing creates an entity but its repository
|
|
39
|
+
|
|
40
|
+
`new {Entity}()` written outside the repository is not allowed. `AbstractRepository`
|
|
41
|
+
resolves `createNew{Entity}()` and `saveNew{Entity}()` magically: both call the
|
|
42
|
+
`createNew{Entity}()` the repository declares, and `saveNew` persists and flushes what it
|
|
43
|
+
returns. So the repository carries one hand-written method, and the caller only ever sees
|
|
44
|
+
the magic one — declared in the class docblock, which is what makes the IDE and static
|
|
45
|
+
analysis follow it:
|
|
46
|
+
|
|
47
|
+
```php
|
|
48
|
+
/**
|
|
49
|
+
* @method Message saveNewMessage(Session $session, string $type, string $body)
|
|
50
|
+
*/
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The magic matches the entity's short name and nothing else, so a special case is written
|
|
54
|
+
out in full as `createNew{Entity}From{Source}()` and called by that name. Being an ordinary
|
|
55
|
+
method it persists nothing unless it says so — `MessageRepository::saveNewMessageAndPushEvent()`
|
|
56
|
+
in `wexample/symfony-ai` is that shape, delegating to `saveNewMessage()` and dispatching
|
|
57
|
+
only once the row is written.
|
|
58
|
+
|
|
59
|
+
## Posted data comes from a DTO, never from the request
|
|
60
|
+
|
|
61
|
+
`$request->getPayload()` is not read. A POST body is declared as a DTO and validated before
|
|
62
|
+
the controller method runs:
|
|
63
|
+
|
|
64
|
+
```php
|
|
65
|
+
#[ValidateRequestContent(dto: CreateMessageDto::class, attributeName: 'createMessageDto')]
|
|
66
|
+
public function create(CreateMessageDto $createMessageDto, ...): ApiResponse
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The DTO extends `AbstractDto`, sits in `Api\Dto\Entity\{Entity}\`, and carries its
|
|
70
|
+
constraints on its properties — `#[Type]`, `#[NotBlank]`, `#[RequiredDtoProperty]`. It is
|
|
71
|
+
modelled on the entity it feeds, one property per field written, and modelled on it rather
|
|
72
|
+
than copied from it: what a caller may set is not what the entity holds. `CreateMessageDto`
|
|
73
|
+
asks for `session` and `body` and not for `type`, because a caller allowed to name the
|
|
74
|
+
author of a turn would be allowed to write the answers.
|
|
75
|
+
|
|
76
|
+
This is not the family of step 3: that DTO extends `AbstractEntityDto` and goes out through
|
|
77
|
+
the normalizer, this one comes in.
|
|
78
|
+
|
|
79
|
+
Forms branch off the same rectify and go nowhere near pseudocode or js-api —
|
|
80
|
+
`wexample/symfony-forms`, `cookbook/create-a-form`.
|
|
@@ -27,6 +27,16 @@ if TYPE_CHECKING:
|
|
|
27
27
|
|
|
28
28
|
@base_class
|
|
29
29
|
class PhpPackageWorkdir(WithRunnerWorkdirMixin, PhpWorkdir):
|
|
30
|
+
@staticmethod
|
|
31
|
+
def line_opens_a_comment(line: str) -> bool:
|
|
32
|
+
"""Whether a line is a comment, judged on the characters it opens with.
|
|
33
|
+
|
|
34
|
+
A line-level test and not a parse: it catches the docblock body and the `//`
|
|
35
|
+
line, which is where a class gets *mentioned* rather than used. A name trailing
|
|
36
|
+
real code after a `//` on the same line stays reported — rarer, and for a
|
|
37
|
+
dependency check erring toward reporting is the right way round.
|
|
38
|
+
"""
|
|
39
|
+
return line.lstrip().startswith(("*", "//", "#", "/*"))
|
|
30
40
|
def get_package_import_name(self) -> str:
|
|
31
41
|
"""Get the full package import name with vendor prefix."""
|
|
32
42
|
return f"{string_to_pascal_case(self.get_vendor_name())}\\{string_to_pascal_case(self.get_project_name())}"
|
|
@@ -60,12 +70,37 @@ class PhpPackageWorkdir(WithRunnerWorkdirMixin, PhpWorkdir):
|
|
|
60
70
|
def search_imports_in_codebase(
|
|
61
71
|
self, searched_package: PhpPackageWorkdir
|
|
62
72
|
) -> list[SearchResult]:
|
|
63
|
-
"""Find PHP `use`/qualified references to the given package.
|
|
73
|
+
"""Find PHP `use`/qualified references to the given package.
|
|
74
|
+
|
|
75
|
+
Two shapes count as an import: the `use` statement, and the fully qualified
|
|
76
|
+
name written inline (`new \\Wexample\\Foo\\Bar()`). The second is why matches
|
|
77
|
+
have to be sifted afterwards — a fully qualified name reads the same inside a
|
|
78
|
+
docblock, and an `@deprecated` note pointing at the class that replaces this
|
|
79
|
+
one is documentation, not a dependency.
|
|
80
|
+
|
|
81
|
+
The `use` branch needs no sifting: it is anchored on `^\\s*use`, which a
|
|
82
|
+
docblock's leading `*` already prevents from matching.
|
|
83
|
+
"""
|
|
64
84
|
import re
|
|
65
85
|
|
|
66
86
|
pkg = re.escape(searched_package.get_package_import_name())
|
|
67
87
|
pattern = rf"(?m)^\s*use\s+{pkg}(?:\\\\[\w]+)*\s*;|{pkg}(?:\\\\[\w]+)*(?!\w)"
|
|
68
|
-
|
|
88
|
+
results = self.search_in_codebase(pattern, regex=True, flags=re.MULTILINE)
|
|
89
|
+
|
|
90
|
+
lines_by_path: dict[str, list[str]] = {}
|
|
91
|
+
kept: list[SearchResult] = []
|
|
92
|
+
|
|
93
|
+
for result in results:
|
|
94
|
+
path = str(result.item.get_path())
|
|
95
|
+
if path not in lines_by_path:
|
|
96
|
+
lines_by_path[path] = result.item.read_text().splitlines()
|
|
97
|
+
|
|
98
|
+
lines = lines_by_path[path]
|
|
99
|
+
line = lines[result.line - 1] if result.line <= len(lines) else ""
|
|
100
|
+
if not self.line_opens_a_comment(line):
|
|
101
|
+
kept.append(result)
|
|
102
|
+
|
|
103
|
+
return kept
|
|
69
104
|
|
|
70
105
|
def search_in_codebase(
|
|
71
106
|
self, string: str, *, regex: bool = False, flags: int = 0
|
|
@@ -7,6 +7,9 @@ from wexample_helpers.decorator.base_class import base_class
|
|
|
7
7
|
from wexample_wex_addon_dev_php.workdir.php_workdir import PhpWorkdir
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Iterator
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
10
13
|
from wexample_config.const.types import DictConfig
|
|
11
14
|
from wexample_config.options_provider.abstract_options_provider import (
|
|
12
15
|
AbstractOptionsProvider,
|
|
@@ -19,6 +22,40 @@ if TYPE_CHECKING:
|
|
|
19
22
|
|
|
20
23
|
@base_class
|
|
21
24
|
class PhpSymfonyWorkdir(PhpWorkdir):
|
|
25
|
+
@staticmethod
|
|
26
|
+
def _create_install_nodes(leaves: dict[str, dict]) -> list[dict]:
|
|
27
|
+
from wexample_filestate.const.disk import DiskItemType
|
|
28
|
+
|
|
29
|
+
roots: list[dict] = []
|
|
30
|
+
directories: dict[str, dict] = {}
|
|
31
|
+
|
|
32
|
+
for destination in sorted(leaves):
|
|
33
|
+
*parents, filename = destination.split("/")
|
|
34
|
+
siblings = roots
|
|
35
|
+
prefix = ""
|
|
36
|
+
|
|
37
|
+
for part in parents:
|
|
38
|
+
prefix = f"{prefix}/{part}" if prefix else part
|
|
39
|
+
node = directories.get(prefix)
|
|
40
|
+
if node is None:
|
|
41
|
+
node = {
|
|
42
|
+
"name": part,
|
|
43
|
+
"type": DiskItemType.DIRECTORY,
|
|
44
|
+
"children": [],
|
|
45
|
+
}
|
|
46
|
+
directories[prefix] = node
|
|
47
|
+
siblings.append(node)
|
|
48
|
+
siblings = node["children"]
|
|
49
|
+
|
|
50
|
+
siblings.append(
|
|
51
|
+
{
|
|
52
|
+
"name": filename,
|
|
53
|
+
"type": DiskItemType.FILE,
|
|
54
|
+
**leaves[destination],
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return roots
|
|
22
59
|
def get_options_providers(self) -> list[type[AbstractOptionsProvider]]:
|
|
23
60
|
from wexample_filestate_symfony.options_provider.symfony_options_provider import (
|
|
24
61
|
SymfonyOptionsProvider,
|
|
@@ -129,6 +166,14 @@ class PhpSymfonyWorkdir(PhpWorkdir):
|
|
|
129
166
|
src_excluded.append("Repository")
|
|
130
167
|
src_dir["children"].append(repository_dir)
|
|
131
168
|
|
|
169
|
+
# Only the entity that asked for a provider of its own. `#[Searchable]`
|
|
170
|
+
# alone is served by the generic provider of wexample/symfony-search and
|
|
171
|
+
# needs no class, so this subtree stays empty in most applications.
|
|
172
|
+
search_dir = self._create_search_subtree(entity_dir)
|
|
173
|
+
if search_dir is not None:
|
|
174
|
+
src_excluded.append("Search")
|
|
175
|
+
src_dir["children"].append(search_dir)
|
|
176
|
+
|
|
132
177
|
# Same reserve as the repository subtree: no entity asking for a form
|
|
133
178
|
# means no src/Form and no src/Service/FormProcessor to bring into
|
|
134
179
|
# being.
|
|
@@ -149,6 +194,9 @@ class PhpSymfonyWorkdir(PhpWorkdir):
|
|
|
149
194
|
if services_subtree is not None:
|
|
150
195
|
src_dir["children"].append(services_subtree)
|
|
151
196
|
|
|
197
|
+
# What packages ask for lands beside src/, not inside it.
|
|
198
|
+
raw_value["children"] += self._create_install_subtrees()
|
|
199
|
+
|
|
152
200
|
# An app without src/Api is not an API app: nothing to scaffold, and
|
|
153
201
|
# declaring the subtree would create the directories out of thin air.
|
|
154
202
|
if (self.get_path() / "src" / "Api").is_dir():
|
|
@@ -560,6 +608,50 @@ class PhpSymfonyWorkdir(PhpWorkdir):
|
|
|
560
608
|
],
|
|
561
609
|
}
|
|
562
610
|
|
|
611
|
+
def _create_install_subtrees(self) -> list[dict]:
|
|
612
|
+
"""The nodes installed packages ask for through `extra.install`.
|
|
613
|
+
|
|
614
|
+
Three things a package may need from the application that hosts it: its
|
|
615
|
+
bundle listed in `config/bundles.php`, a configuration file dropped once
|
|
616
|
+
and then left to the reader, and a block kept up to date inside a file
|
|
617
|
+
the reader owns. Everything is declared in the package's composer.json,
|
|
618
|
+
so installing it is enough for `rectify` to know.
|
|
619
|
+
"""
|
|
620
|
+
# An application file only: a bundle has no bundle list of its own.
|
|
621
|
+
if not (self.get_path() / "config" / "bundles.php").is_file():
|
|
622
|
+
return []
|
|
623
|
+
|
|
624
|
+
from wexample_filestate_symfony.option.symfony.bundles_registration_option import (
|
|
625
|
+
BundlesRegistrationOption,
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
leaves: dict[str, dict] = {
|
|
629
|
+
"config/bundles.php": {
|
|
630
|
+
"symfony": [BundlesRegistrationOption.get_name()],
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
for name, directory, install in self._declared_installs():
|
|
635
|
+
for source, destination in install.get("files", {}).items():
|
|
636
|
+
template = directory / source
|
|
637
|
+
if template.is_file():
|
|
638
|
+
leaves.setdefault(destination, {}).update(
|
|
639
|
+
{
|
|
640
|
+
"should_exist": True,
|
|
641
|
+
"default_content": template.read_text(),
|
|
642
|
+
}
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
for source, destination in install.get("blocks", {}).items():
|
|
646
|
+
template = directory / source
|
|
647
|
+
if template.is_file():
|
|
648
|
+
blocks = leaves.setdefault(destination, {}).setdefault(
|
|
649
|
+
"managed_blocks", {}
|
|
650
|
+
)
|
|
651
|
+
blocks[name] = template.read_text()
|
|
652
|
+
|
|
653
|
+
return self._create_install_nodes(leaves)
|
|
654
|
+
|
|
563
655
|
def _create_php_file_children_filter(self) -> ChildrenFileFactoryOption:
|
|
564
656
|
from wexample_filestate_symfony.option.symfony.entity_extend_option import (
|
|
565
657
|
EntityExtendOption,
|
|
@@ -646,6 +738,73 @@ class PhpSymfonyWorkdir(PhpWorkdir):
|
|
|
646
738
|
],
|
|
647
739
|
}
|
|
648
740
|
|
|
741
|
+
def _create_search_subtree(self, entity_dir: dict) -> dict | None:
|
|
742
|
+
"""The providers written by hand for entities that reason.
|
|
743
|
+
|
|
744
|
+
`#[Searchable]` declares a field list and the generic provider searches
|
|
745
|
+
it — that is the ordinary case and it produces no file. `provider: true`
|
|
746
|
+
is the entity whose answer depends on who is asking, and that one needs
|
|
747
|
+
a class.
|
|
748
|
+
"""
|
|
749
|
+
from wexample_filestate.const.disk import DiskItemType
|
|
750
|
+
from wexample_filestate.option.sidecar_of_option import SidecarOfOption
|
|
751
|
+
from wexample_filestate_symfony.file.symfony_entity_search_provider_file import (
|
|
752
|
+
SymfonyEntitySearchProviderFile,
|
|
753
|
+
)
|
|
754
|
+
from wexample_filestate_symfony.file.symfony_entity_search_scoring_file import (
|
|
755
|
+
SymfonyEntitySearchScoringFile,
|
|
756
|
+
)
|
|
757
|
+
from wexample_filestate_symfony.helper.entity import (
|
|
758
|
+
entity_attribute_flag,
|
|
759
|
+
entity_attribute_names_argument,
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
search_path = self.get_path() / "src" / "Search"
|
|
763
|
+
|
|
764
|
+
def _is_missing_provider(path) -> bool:
|
|
765
|
+
if not entity_attribute_flag(path, "Searchable", "provider"):
|
|
766
|
+
return False
|
|
767
|
+
# An existing provider is declared by the sweep beside; declaring it
|
|
768
|
+
# here too would build two items for the one path.
|
|
769
|
+
return not (search_path / f"{path.stem}SearchProvider.php").exists()
|
|
770
|
+
|
|
771
|
+
# `scoring: InvoiceSearchScoring::class` asks for a class of that name;
|
|
772
|
+
# the sidecar writes it under the name the convention gives, which is
|
|
773
|
+
# the one the attribute is expected to have used.
|
|
774
|
+
def _is_missing_scoring(path) -> bool:
|
|
775
|
+
if not entity_attribute_names_argument(path, "Searchable", "scoring"):
|
|
776
|
+
return False
|
|
777
|
+
return not (search_path / f"{path.stem}SearchScoring.php").exists()
|
|
778
|
+
|
|
779
|
+
entity_paths = list((self.get_path() / "src" / "Entity").glob("*.php"))
|
|
780
|
+
if (
|
|
781
|
+
not search_path.is_dir()
|
|
782
|
+
and not any(map(_is_missing_provider, entity_paths))
|
|
783
|
+
and not any(map(_is_missing_scoring, entity_paths))
|
|
784
|
+
):
|
|
785
|
+
return None
|
|
786
|
+
|
|
787
|
+
return {
|
|
788
|
+
"name": "Search",
|
|
789
|
+
"type": DiskItemType.DIRECTORY,
|
|
790
|
+
"should_exist": True,
|
|
791
|
+
"children": [
|
|
792
|
+
self._create_php_file_children_filter(),
|
|
793
|
+
SidecarOfOption(
|
|
794
|
+
primary=entity_dir,
|
|
795
|
+
filename="{stem}SearchProvider.php",
|
|
796
|
+
cls=SymfonyEntitySearchProviderFile,
|
|
797
|
+
filter=_is_missing_provider,
|
|
798
|
+
),
|
|
799
|
+
SidecarOfOption(
|
|
800
|
+
primary=entity_dir,
|
|
801
|
+
filename="{stem}SearchScoring.php",
|
|
802
|
+
cls=SymfonyEntitySearchScoringFile,
|
|
803
|
+
filter=_is_missing_scoring,
|
|
804
|
+
),
|
|
805
|
+
],
|
|
806
|
+
}
|
|
807
|
+
|
|
649
808
|
def _create_services_subtree(self) -> dict | None:
|
|
650
809
|
from wexample_filestate.const.disk import DiskItemType
|
|
651
810
|
from wexample_filestate_symfony.option.symfony.services_resource_option import (
|
|
@@ -690,3 +849,11 @@ class PhpSymfonyWorkdir(PhpWorkdir):
|
|
|
690
849
|
runner.workdir = runner.rebase_path(self.get_path())
|
|
691
850
|
|
|
692
851
|
return runner
|
|
852
|
+
|
|
853
|
+
def _declared_installs(self) -> Iterator[tuple[str, Path, dict]]:
|
|
854
|
+
from wexample_filestate_symfony.helper.composer import installed_packages
|
|
855
|
+
|
|
856
|
+
for name, directory, manifest in installed_packages(self.get_path()):
|
|
857
|
+
install = manifest.get("extra", {}).get("install", {})
|
|
858
|
+
if install:
|
|
859
|
+
yield name, directory, install
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
# The line that set this off: an `@deprecated` note naming the class that replaces
|
|
6
|
+
# this one. Verbatim from symfony-helpers' SystemParameter.php.
|
|
7
|
+
DEPRECATION_NOTE = (
|
|
8
|
+
" * @deprecated use `Wexample\\SymfonyPlatform\\Entity\\Configuration`, which holds a"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_a_docblock_body_is_a_comment() -> None:
|
|
13
|
+
assert _line_opens_a_comment(DEPRECATION_NOTE)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_a_name_trailing_real_code_stays_reported() -> None:
|
|
17
|
+
"""Documented limit: only the line's opening decides, so this is still an import."""
|
|
18
|
+
assert not _line_opens_a_comment(
|
|
19
|
+
"$c = new \\Wexample\\SymfonyPlatform\\Entity\\Configuration(); // built here"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.parametrize(
|
|
24
|
+
"line",
|
|
25
|
+
[
|
|
26
|
+
"// use Wexample\\SymfonyPlatform\\Entity\\Configuration;",
|
|
27
|
+
"# Wexample\\SymfonyPlatform\\Entity\\Configuration",
|
|
28
|
+
"/* Wexample\\SymfonyPlatform\\Entity\\Configuration */",
|
|
29
|
+
" * @see Wexample\\SymfonyPlatform\\Entity\\Configuration",
|
|
30
|
+
],
|
|
31
|
+
)
|
|
32
|
+
def test_every_comment_opener_is_recognised(line: str) -> None:
|
|
33
|
+
assert _line_opens_a_comment(line)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.mark.parametrize(
|
|
37
|
+
"line",
|
|
38
|
+
[
|
|
39
|
+
"use Wexample\\SymfonyPlatform\\Entity\\Configuration;",
|
|
40
|
+
" use Wexample\\SymfonyPlatform\\Entity\\Configuration;",
|
|
41
|
+
" $c = new \\Wexample\\SymfonyPlatform\\Entity\\Configuration();",
|
|
42
|
+
"class SystemParameter extends AbstractEntity",
|
|
43
|
+
],
|
|
44
|
+
)
|
|
45
|
+
def test_real_code_is_not_a_comment(line: str) -> None:
|
|
46
|
+
assert not _line_opens_a_comment(line)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _line_opens_a_comment(line: str) -> bool:
|
|
50
|
+
from wexample_wex_addon_dev_php.workdir.php_package_workdir import (
|
|
51
|
+
PhpPackageWorkdir,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return PhpPackageWorkdir.line_opens_a_comment(line)
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
A Symfony entity must extend, directly or indirectly, the abstract class
|
|
2
|
-
`Wexample\SymfonyHelpers\Entity\AbstractEntity`. It provides the `id` column, the
|
|
3
|
-
`BaseEntityTrait` and the entity naming/path helpers the rest of the framework relies
|
|
4
|
-
on — an entity that does not extend it is not wired into those conventions.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|