From 62d507222ab62943022dca61dcc0eff9a2303e9c Mon Sep 17 00:00:00 2001 From: Daniel Carbone Date: Thu, 25 Apr 2024 11:31:13 -0500 Subject: [PATCH] xmlns improvements, template file hierarchy improvements (#100) * Improving xmlns detection on xhtml type * Fixing xml unserializing in child types * Internal cleanup & refactor --- .../actions/load-fhir-validator/action.yaml | 64 + .github/actions/load-fhir-xsd/action.yaml | 71 + .github/workflows/tests.yaml | 70 +- .gitignore | 9 +- bin/config.php | 2 +- bin/generate.php | 6 +- composer.lock | 2717 +++++++++++++++++ files/constants.php | 32 +- files/funcs.php | 6 +- src/Builder.php | 278 +- src/Definition/Properties.php | 8 +- src/Definition/Type.php | 16 +- src/Definition/TypeImports.php | 7 + src/Definition/Types.php | 30 +- src/Render/Templates.php | 150 +- .../classes}/class_autoloader.php | 41 +- .../classes/class_config.php} | 73 +- .../classes}/class_constants.php | 2 +- template/core/classes/class_debug_client.php | 174 ++ .../core/classes/class_response_parser.php | 267 ++ .../classes/class_type_map.php} | 2 +- template/core/enums/enum_api_format.php | 50 + template/core/enums/enum_config_key.php | 50 + template/core/enums/enum_type.php | 51 + .../interface_comment_container.php} | 2 +- .../interfaces/interface_contained_type.php} | 4 +- .../interfaces/interface_type.php} | 5 +- .../interface_xml_serializable.php} | 16 +- .../interface_xml_serializable_config.php | 64 + .../{ => core}/tests/test_class_constants.php | 0 .../{ => core}/tests/test_class_type_map.php | 0 .../traits/trait_change_tracking.php} | 0 .../traits/trait_comment_container.php} | 0 .../traits/trait_validation_assertions.php} | 46 +- .../traits/trait_xml_namespace.php} | 9 +- .../traits/trait_xml_serializable_config.php | 71 + template/file/header_type.php | 2 +- .../tests/types/integration/class_default.php | 79 - .../integration/header_domain_resource.php | 53 - template/types/class_xhtml.php | 50 +- .../serialization/xml/serialize/header.php | 15 +- .../xml/serialize/resource_container.php | 15 +- .../serialization/xml/unserialize/header.php | 42 +- .../tests/integration/class.php} | 70 +- .../types => types/tests}/unit/body_base.php | 0 .../tests}/unit/body_primitive.php | 0 .../tests/unit/class.php} | 6 +- .../types => types/tests}/unit/header.php | 0 template/types/validation/methods.php | 41 +- template/utilities/class_response_parser.php | 264 -- 50 files changed, 4084 insertions(+), 946 deletions(-) create mode 100644 .github/actions/load-fhir-validator/action.yaml create mode 100644 .github/actions/load-fhir-xsd/action.yaml create mode 100644 composer.lock rename template/{utilities => core/classes}/class_autoloader.php (72%) rename template/{utilities/class_response_parser_config.php => core/classes/class_config.php} (51%) rename template/{utilities => core/classes}/class_constants.php (98%) create mode 100644 template/core/classes/class_debug_client.php create mode 100644 template/core/classes/class_response_parser.php rename template/{utilities/class_typemap.php => core/classes/class_type_map.php} (99%) create mode 100644 template/core/enums/enum_api_format.php create mode 100644 template/core/enums/enum_config_key.php create mode 100644 template/core/enums/enum_type.php rename template/{interfaces/phpfhir_comment_container.php => core/interfaces/interface_comment_container.php} (97%) rename template/{interfaces/phpfhir_contained_type.php => core/interfaces/interface_contained_type.php} (94%) rename template/{interfaces/phpfhir_type.php => core/interfaces/interface_type.php} (92%) rename template/{interfaces/phpfhir_xml_serializable.php => core/interfaces/interface_xml_serializable.php} (65%) create mode 100644 template/core/interfaces/interface_xml_serializable_config.php rename template/{ => core}/tests/test_class_constants.php (100%) rename template/{ => core}/tests/test_class_type_map.php (100%) rename template/{traits/phpfhir_change_tracking.php => core/traits/trait_change_tracking.php} (100%) rename template/{traits/phpfhir_comment_container.php => core/traits/trait_comment_container.php} (100%) rename template/{traits/phpfhir_validation_assertions.php => core/traits/trait_validation_assertions.php} (75%) rename template/{traits/phpfhir_xml_namespace.php => core/traits/trait_xml_namespace.php} (84%) create mode 100644 template/core/traits/trait_xml_serializable_config.php delete mode 100644 template/tests/types/integration/class_default.php delete mode 100644 template/tests/types/integration/header_domain_resource.php rename template/{tests/types/integration/body_domain_resource.php => types/tests/integration/class.php} (87%) rename template/{tests/types => types/tests}/unit/body_base.php (100%) rename template/{tests/types => types/tests}/unit/body_primitive.php (100%) rename template/{tests/types/unit/class_default.php => types/tests/unit/class.php} (79%) rename template/{tests/types => types/tests}/unit/header.php (100%) delete mode 100644 template/utilities/class_response_parser.php diff --git a/.github/actions/load-fhir-validator/action.yaml b/.github/actions/load-fhir-validator/action.yaml new file mode 100644 index 00000000..e6b78a45 --- /dev/null +++ b/.github/actions/load-fhir-validator/action.yaml @@ -0,0 +1,64 @@ +name: 'Load FHIR Validator' +description: 'Downloads and caches a FHIR validator jar' + +inputs: + version: + required: false + description: 'Version of validator to download' + default: '6.3.4' + + base-url: + required: false + description: 'Base download URL' + default: 'https://github.com/hapifhir/org.hl7.fhir.core/releases/download' + + filename: + required: false + description: 'Validator filename' + default: 'validator_cli.jar' + +runs: + using: composite + steps: + - id: vars + shell: bash -e {0} + # language=sh + run: | + echo 'url=${{ inputs.base-url }}/${{ inputs.version }}/${{ inputs.filename }}' >> $GITHUB_OUTPUT + echo 'cache-key=fhir-validator-${{ inputs.version }}-2' >> $GITHUB_OUTPUT + + - uses: actions/cache@v4 + id: cache + with: + path: | + ${{ github.workspace }}/bin + /home/runner/.fhir + key: '${{ steps.vars.outputs.cache-key }}' + + - name: 'Install Java' + if: steps.cache.outputs.cache-hit != 'true' && steps.cache.outputs.cache-hit != true + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 18 + + - name: 'Download and cache extensions' + if: steps.cache.outputs.cache-hit != 'true' && steps.cache.outputs.cache-hit != true + shell: bash -e {0} + # language=sh + run: | + echo '::group::Downloading validator_cli.jar ${{ inputs.version }}' + mkdir -p '${{ github.workspace }}/bin' + wget -q '${{ steps.vars.outputs.url }}' -O '${{ github.workspace }}/bin/${{ inputs.filename }}' + echo '::endgroup::' + + echo '::group::Caching validator assets' + java -jar '${{ github.workspace }}/bin/validator_cli.jar' -version 1.0.2 + java -jar '${{ github.workspace }}/bin/validator_cli.jar' -version 1.4.0 + java -jar '${{ github.workspace }}/bin/validator_cli.jar' -version 3.0.2 + java -jar '${{ github.workspace }}/bin/validator_cli.jar' -version 4.0.1 + java -jar '${{ github.workspace }}/bin/validator_cli.jar' -version 4.1.0 + java -jar '${{ github.workspace }}/bin/validator_cli.jar' -version 4.3.0 + java -jar '${{ github.workspace }}/bin/validator_cli.jar' -version 5.0.0 + echo '::endgroup::' + diff --git a/.github/actions/load-fhir-xsd/action.yaml b/.github/actions/load-fhir-xsd/action.yaml new file mode 100644 index 00000000..79cdf9f8 --- /dev/null +++ b/.github/actions/load-fhir-xsd/action.yaml @@ -0,0 +1,71 @@ +name: 'Load FHIR XSD' +description: 'Downloads and caches a a version of FHIR source files' + +inputs: + version: + required: true + description: 'Version to download' + + base-url: + required: false + description: 'Base FHIR URL' + default: 'https://hl7.org/fhir' + + filename: + required: false + description: 'Name of xsd file' + default: 'fhir-all-xsd.zip' + + user-agent: + required: false + description: 'User agent string to use' + default: 'Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0' + +outputs: + cache-key: + description: 'Actions Cache key' + value: '${{ steps.vars.cache-key }}' + +runs: + using: composite + steps: + - id: vars + shell: bash -e {0} + # language=sh + run: | + _month="$(date "+%Y%m")" + _cache_key="fhir-source-${{ inputs.version }}-${_month}-1" + _dl_url="${{ inputs.base-url}}/${{ inputs.version }}/${{ inputs.filename }}" + _cache_dir="${{ github.workspace }}/input" + _xsd_cache_dir="${_cache_dir}/${{ inputs.version }}" + _zip_filename="${_cache_dir}/${{ inputs.version }}.zip" + + echo "cache-key=${_cache_key}" >> $GITHUB_OUTPUT + echo "cache-dir=${_cache_dir}" >> $GITHUB_OUTPUT + echo "xsd-cache-dir=${_xsd_cache_dir}" >> $GITHUB_OUTPUT + echo "url=${_dl_url}" >> $GITHUB_OUTPUT + echo "zip-filename=${_zip_filename}" >> $GITHUB_OUTPUT + + - uses: actions/cache@v4 + id: cache + with: + path: '${{ steps.vars.outputs.cache-dir }}' + key: '${{ steps.vars.outputs.cache-key }}' + + - name: 'Download' + if: steps.cache.outputs.cache-hit != 'true' && steps.cache.outputs.cache-hit != true + shell: bash -e {0} + # language=sh + run: | + mkdir -p '${{ steps.vars.outputs.xsd-cache-dir }}' + + curl \ + -L \ + -A '${{ inputs.user-agent }}' \ + -o '${{ steps.vars.outputs.zip-filename }}' \ + '${{ steps.vars.outputs.url }}' + + unzip -o -qq '${{ steps.vars.outputs.zip-filename }}' -d '${{ steps.vars.outputs.xsd-cache-dir }}' + + ls -l '${{ steps.vars.outputs.cache-dir}}' + ls -l '${{ steps.vars.outputs.xsd-cache-dir }}' \ No newline at end of file diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 5c18d4a7..bfdc6cb1 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -26,8 +26,35 @@ on: - 'phpunit.xml' jobs: + load-fhir-source: + runs-on: ubuntu-22.04 + strategy: + fail-fast: true + matrix: + fhir-version: [ 'DSTU1', 'DSTU2', 'STU3', 'R4', 'R5' ] + + name: 'Download ${{ matrix.fhir-version }} Source' + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/load-fhir-xsd + with: + version: '${{ matrix.fhir-version }}' + + + load-fhir-validator: + runs-on: ubuntu-22.04 + name: 'Download FHIR Validator' + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/load-fhir-validator + run: runs-on: ubuntu-22.04 + needs: + - load-fhir-source + - load-fhir-validator strategy: fail-fast: false matrix: @@ -43,21 +70,33 @@ jobs: - php-version: '8.3' phpunit-version: '11.1' paratest-version: '7' - name: ${{ matrix.fhir-version }} - PHP ${{ matrix.php-version }} - PHPUnit ${{ matrix.phpunit-version }} + name: '${{ matrix.fhir-version }} - PHP ${{ matrix.php-version }} - PHPUnit ${{ matrix.phpunit-version }}' steps: - - uses: actions/checkout@v4 + - name: 'Check the stuff out' + uses: actions/checkout@v4 + + - name: 'Load FHIR ${{ matrix.fhir-version }} Source' + uses: ./.github/actions/load-fhir-xsd + with: + version: '${{ matrix.fhir-version }}' + + - name: 'Install FHIR validator' + uses: ./.github/actions/load-fhir-validator - - uses: shivammathur/setup-php@v2 + - name: 'Install PHP' + uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} extensions: libxml simplexml json zip # https://github.com/mheap/phpunit-github-actions-printer/blob/master/.github/workflows/ci.yml#L32 - - uses: dcarbone/install-jq-action@v2 + - name: 'Install jq' + uses: dcarbone/install-jq-action@v2 with: force: true - name: 'Set Composer Dep Versions' + # language=sh run: | tmpf="$(mktemp)" jq -rcM '."require-dev"."phpunit/phpunit" = "^${{ matrix.phpunit-version }}" | ."require-dev"."brianium/paratest" = "^${{ matrix.paratest-version }}"' composer.json > "${tmpf}" @@ -65,37 +104,34 @@ jobs: - name: 'Get Composer Cache Directory' id: composer-cache + # language=sh run: | echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: "composer-${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.phpunit-version }}-${{ hashFiles('**/composer.lock') }}" - - - name: 'Cache downloaded FHIR sources' + - name: 'Cache Composer Stuff' uses: actions/cache@v4 with: - path: ./input - key: "fhir-${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.phpunit-version }}-${{ hashFiles('**/composer.lock') }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "composer-${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.phpunit-version }}-${{ hashFiles('composer.lock') }}" - name: 'Install Composer deps' + # language=sh run: | + rm composer.lock composer install --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist - name: 'Generate code' + # language=sh run: | ./bin/generate.sh --useExisting --versions '${{ matrix.fhir-version }}' - - uses: actions/setup-java@v4 + - name: 'Install Java' + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 18 - - name: 'Install FHIR validator' - run: | - wget --quiet https://github.com/hapifhir/org.hl7.fhir.core/releases/download/6.3.4/validator_cli.jar -O ./bin/validator_cli.jar - - name: 'Run tests' + # language=sh run: | ./vendor/bin/paratest -c 'phpunit/${{ matrix.fhir-version }}.xml' diff --git a/.gitignore b/.gitignore index 9101b267..84781ffe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ -/vendor -/composer.lock -/tmp +.idea/ +.svn/ +tmp/ +vendor/ /*.iml -/.idea -/.svn /.*.cache \ No newline at end of file diff --git a/bin/config.php b/bin/config.php index f5ced6f2..a0937156 100644 --- a/bin/config.php +++ b/bin/config.php @@ -55,7 +55,7 @@ 'testEndpoint' => 'https://hapi.fhir.org/baseDstu3', ], 'R4' => [ - 'url' => 'https://www.hl7.org/fhir/R4/fhir-all-xsd.zip', + 'url' => 'https://hl7.org/fhir/R4/fhir-all-xsd.zip', 'namespace' => '\\HL7\\FHIR\\R4', 'testEndpoint' => 'https://hapi.fhir.org/baseR4', ], diff --git a/bin/generate.php b/bin/generate.php index c4279cea..5b417c92 100644 --- a/bin/generate.php +++ b/bin/generate.php @@ -413,7 +413,7 @@ function is_dir_empty(string $dir): bool if ($download) { // Download zip file... - echo sprintf('Downloading %s from %s%s', $version, $url, PHP_EOL); + echo sprintf('Downloading %s from %s to %s%s', $version, $url, $zip_file_name, PHP_EOL); $fh = fopen($zip_file_name, 'w'); $ch = curl_init($url); curl_setopt_array( @@ -511,9 +511,9 @@ function is_dir_empty(string $dir): bool $builder = new Builder($build_config, $definition); if ($only_library) { - $builder->renderFHIRClasses(); + $builder->writeFhirTypeFiles(); } elseif ($only_tests) { - $builder->renderTestClasses(); + $builder->writeFhirTestFiles(); } else { $builder->render(); } diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..7931e875 --- /dev/null +++ b/composer.lock @@ -0,0 +1,2717 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "d40d2cacd0a5617d1c50da10577b49ea", + "packages": [ + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + } + ], + "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.4.3", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "64fcfd0e28a6b8078a19dbf9127be2ee645b92ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/64fcfd0e28a6b8078a19dbf9127be2ee645b92ec", + "reference": "64fcfd0e28a6b8078a19dbf9127be2ee645b92ec", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.1.0", + "jean85/pretty-package-versions": "^2.0.5", + "php": "~8.2.0 || ~8.3.0", + "phpunit/php-code-coverage": "^10.1.11 || ^11.0.0", + "phpunit/php-file-iterator": "^4.1.0 || ^5.0.0", + "phpunit/php-timer": "^6.0.0 || ^7.0.0", + "phpunit/phpunit": "^10.5.9 || ^11.0.3", + "sebastian/environment": "^6.0.1 || ^7.0.0", + "symfony/console": "^6.4.3 || ^7.0.3", + "symfony/process": "^6.4.3 || ^7.0.3" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^1.10.58", + "phpstan/phpstan-deprecation-rules": "^1.1.4", + "phpstan/phpstan-phpunit": "^1.3.15", + "phpstan/phpstan-strict-rules": "^1.5.2", + "squizlabs/php_codesniffer": "^3.9.0", + "symfony/filesystem": "^6.4.3 || ^7.0.3" + }, + "bin": [ + "bin/paratest", + "bin/paratest.bat", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.4.3" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2024-02-20T07:24:02+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-02-07T09:43:46+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.6", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", + "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + }, + "time": "2024-03-08T09:58:59+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.6.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/4b18b21a5527a3d5ffdac2fd35d3ab25a9597654", + "reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10.5.17", + "predis/predis": "^1.1 || ^2", + "ruflin/elastica": "^7", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.6.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-04-12T21:02:21+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.0.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + }, + "time": "2024-03-05T20:51:40+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "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" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "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" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e35a2cbcabac0e6865fd373742ea432a3c34f92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e35a2cbcabac0e6865fd373742ea432a3c34f92", + "reference": "7e35a2cbcabac0e6865fd373742ea432a3c34f92", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.0", + "phpunit/php-text-template": "^4.0", + "sebastian/code-unit-reverse-lookup": "^4.0", + "sebastian/complexity": "^4.0", + "sebastian/environment": "^7.0", + "sebastian/lines-of-code": "^3.0", + "sebastian/version": "^5.0", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-12T15:35:40+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "99e95c94ad9500daca992354fa09d7b99abe2210" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/99e95c94ad9500daca992354fa09d7b99abe2210", + "reference": "99e95c94ad9500daca992354fa09d7b99abe2210", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:05:04+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5d8d9355a16d8cc5a1305b0a85342cfa420612be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5d8d9355a16d8cc5a1305b0a85342cfa420612be", + "reference": "5d8d9355a16d8cc5a1305b0a85342cfa420612be", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:05:50+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "d38f6cbff1cdb6f40b03c9811421561668cc133e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/d38f6cbff1cdb6f40b03c9811421561668cc133e", + "reference": "d38f6cbff1cdb6f40b03c9811421561668cc133e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:06:56+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "8a59d9e25720482ee7fcdf296595e08795b84dc5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8a59d9e25720482ee7fcdf296595e08795b84dc5", + "reference": "8a59d9e25720482ee7fcdf296595e08795b84dc5", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:08:01+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "51e342a0bc987e0ea8418105d0711f08ae116de3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/51e342a0bc987e0ea8418105d0711f08ae116de3", + "reference": "51e342a0bc987e0ea8418105d0711f08ae116de3", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0", + "phpunit/php-file-iterator": "^5.0", + "phpunit/php-invoker": "^5.0", + "phpunit/php-text-template": "^4.0", + "phpunit/php-timer": "^7.0", + "sebastian/cli-parser": "^3.0", + "sebastian/code-unit": "^3.0", + "sebastian/comparator": "^6.0", + "sebastian/diff": "^6.0", + "sebastian/environment": "^7.0", + "sebastian/exporter": "^6.0", + "sebastian/global-state": "^7.0", + "sebastian/object-enumerator": "^6.0", + "sebastian/type": "^5.0", + "sebastian/version": "^5.0" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.1-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.1.2" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-04-14T07:13:56+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "00a74d5568694711f0222e54fb281e1d15fdf04a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/00a74d5568694711f0222e54fb281e1d15fdf04a", + "reference": "00a74d5568694711f0222e54fb281e1d15fdf04a", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:26:58+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "6634549cb8d702282a04a774e36a7477d2bd9015" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/6634549cb8d702282a04a774e36a7477d2bd9015", + "reference": "6634549cb8d702282a04a774e36a7477d2bd9015", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T05:50:41+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "df80c875d3e459b45c6039e4d9b71d4fbccae25d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/df80c875d3e459b45c6039e4d9b71d4fbccae25d", + "reference": "df80c875d3e459b45c6039e4d9b71d4fbccae25d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T05:52:17+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "bd0f2fa5b9257c69903537b266ccb80fcf940db8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/bd0f2fa5b9257c69903537b266ccb80fcf940db8", + "reference": "bd0f2fa5b9257c69903537b266ccb80fcf940db8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T05:53:45+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "88a434ad86150e11a606ac4866b09130712671f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/88a434ad86150e11a606ac4866b09130712671f0", + "reference": "88a434ad86150e11a606ac4866b09130712671f0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T05:55:19+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ab83243ecc233de5655b76f577711de9f842e712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ab83243ecc233de5655b76f577711de9f842e712", + "reference": "ab83243ecc233de5655b76f577711de9f842e712", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:30:33+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "4eb3a442574d0e9d141aab209cd4aaf25701b09a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4eb3a442574d0e9d141aab209cd4aaf25701b09a", + "reference": "4eb3a442574d0e9d141aab209cd4aaf25701b09a", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:56:34+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "f291e5a317c321c0381fa9ecc796fa2d21b186da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f291e5a317c321c0381fa9ecc796fa2d21b186da", + "reference": "f291e5a317c321c0381fa9ecc796fa2d21b186da", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:28:20+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "c3a307e832f2e69c7ef869e31fc644fde0e7cb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c3a307e832f2e69c7ef869e31fc644fde0e7cb3e", + "reference": "c3a307e832f2e69c7ef869e31fc644fde0e7cb3e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:32:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "376c5b3f6b43c78fdc049740bca76a7c846706c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/376c5b3f6b43c78fdc049740bca76a7c846706c0", + "reference": "376c5b3f6b43c78fdc049740bca76a7c846706c0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:00:36+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f75f6c460da0bbd9668f43a3dde0ec0ba7faa678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f75f6c460da0bbd9668f43a3dde0ec0ba7faa678", + "reference": "f75f6c460da0bbd9668f43a3dde0ec0ba7faa678", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:01:29+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "bb2a6255d30853425fd38f032eb64ced9f7f132d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/bb2a6255d30853425fd38f032eb64ced9f7f132d", + "reference": "bb2a6255d30853425fd38f032eb64ced9f7f132d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:02:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "b75224967b5a466925c6d54e68edd0edf8dd4ed4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b75224967b5a466925c6d54e68edd0edf8dd4ed4", + "reference": "b75224967b5a466925c6d54e68edd0edf8dd4ed4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:08:48+00:00" + }, + { + "name": "sebastian/type", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "b8502785eb3523ca0dd4afe9ca62235590020f3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8502785eb3523ca0dd4afe9ca62235590020f3f", + "reference": "b8502785eb3523ca0dd4afe9ca62235590020f3f", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:09:34+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "13999475d2cb1ab33cb73403ba356a814fdbb001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/13999475d2cb1ab33cb73403ba356a814fdbb001", + "reference": "13999475d2cb1ab33cb73403ba356a814fdbb001", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-02-02T06:10:47+00:00" + }, + { + "name": "symfony/console", + "version": "v7.0.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "fde915cd8e7eb99b3d531d3d5c09531429c3f9e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/fde915cd8e7eb99b3d531d3d5c09531429c3f9e5", + "reference": "fde915cd8e7eb99b3d531d3d5c09531429c3f9e5", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.0.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-01T11:04:53+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/process", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "11bbf19a0fb7b36345861e85c5768844c552906e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e", + "reference": "11bbf19a0fb7b36345861e85c5768844c552906e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.4.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-19T21:51:00+00:00" + }, + { + "name": "symfony/string", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-01T13:17:36+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "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" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^8.1", + "ext-simplexml": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-dom": "*" + }, + "platform-dev": { + "ext-curl": "*" + }, + "plugin-api-version": "2.6.0" +} diff --git a/files/constants.php b/files/constants.php index d1893297..32da7ca6 100644 --- a/files/constants.php +++ b/files/constants.php @@ -41,7 +41,7 @@ const PHPFHIR_XHTML_TYPE_DESCRIPTION = 'XHTML type used in special cases'; // FHIR XML NS -const PHPFHIR_FHIR_XMLNS = 'http://hl7.org/fhir'; +const PHPFHIR_FHIR_XMLNS = 'https://hl7.org/fhir'; // XSDs const PHPFHIR_SKIP_XML_XSD = 'xml.xsd'; @@ -52,17 +52,18 @@ // Properties const PHPFHIR_UNLIMITED = -1; -const PHPFHIR_RESOURCE_TYPE_PROPERTY = 'resourceType'; const PHPFHIR_VALUE_PROPERTY_NAME = 'value'; // Rendering const PHPFHIR_DOCBLOC_MAX_LENGTH = 80; const PHPFHIR_NAMESPACE_TRIM_CUTSET = " \t\n\r\0\x0b\\/"; define('PHPFHIR_TEMPLATE_DIR', realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'template')); -const PHPFHIR_TEMPLATE_INTERFACES_DIR = PHPFHIR_TEMPLATE_DIR . DIRECTORY_SEPARATOR . 'interfaces'; -const PHPFHIR_TEMPLATE_TRAITS_DIR = PHPFHIR_TEMPLATE_DIR . DIRECTORY_SEPARATOR . 'traits'; + +// "file" directory. will move at some point. const PHPFHIR_TEMPLATE_FILE_DIR = PHPFHIR_TEMPLATE_DIR . DIRECTORY_SEPARATOR . 'file'; -const PHPFHIR_TEMPLATE_UTILITIES_DIR = PHPFHIR_TEMPLATE_DIR . DIRECTORY_SEPARATOR . 'utilities'; + +// Core interfaces, traits, and classes +const PHPFHIR_TEMPLATE_CORE_DIR = PHPFHIR_TEMPLATE_DIR . DIRECTORY_SEPARATOR . 'core'; // Type rendering const PHPFHIR_TEMPLATE_TYPES_DIR = PHPFHIR_TEMPLATE_DIR . DIRECTORY_SEPARATOR . 'types'; @@ -73,28 +74,35 @@ const PHPFHIR_TEMPLATE_TYPES_VALIDATION_DIR = PHPFHIR_TEMPLATE_TYPES_DIR . DIRECTORY_SEPARATOR . 'validation'; -// Test Generation -const PHPFHIR_TEMPLATE_TESTS_DIR = PHPFHIR_TEMPLATE_DIR . DIRECTORY_SEPARATOR . 'tests'; -const PHPFHIR_TEMPLATE_TESTS_TYPES_DIR = PHPFHIR_TEMPLATE_TESTS_DIR . DIRECTORY_SEPARATOR . 'types'; +// Fhir type test templates +const PHPFHIR_TEMPLATE_TYPE_TESTS_DIR = PHPFHIR_TEMPLATE_TYPES_DIR . DIRECTORY_SEPARATOR . 'tests'; -// php fhir class names +// Core class names const PHPFHIR_CLASSNAME_AUTOLOADER = 'PHPFHIRAutoloader'; -const PHPFHIR_CLASSNAME_RESPONSE_PARSER_CONFIG = 'PHPFHIRResponseParserConfig'; +const PHPFHIR_CLASSNAME_CONFIG = 'PHPFHIRConfig'; const PHPFHIR_CLASSNAME_RESPONSE_PARSER = 'PHPFHIRResponseParser'; const PHPFHIR_CLASSNAME_CONSTANTS = 'PHPFHIRConstants'; const PHPFHIR_CLASSNAME_TYPEMAP = 'PHPFHIRTypeMap'; +const PHPFHIR_CLASSNAME_DEBUG_CLIENT = 'PHPFHIRDebugClient'; -// php fhir interface names +// Core interface names const PHPFHIR_INTERFACE_TYPE = 'PHPFHIRTypeInterface'; const PHPFHIR_INTERFACE_XML_SERIALIZABLE = 'PHPFHIRXmlSerializableInterface'; +const PHPFHIR_INTERFACE_XML_SERIALIZALE_CONFIG = 'PHPFHIRXmlSerializableConfigInterface'; const PHPFHIR_INTERFACE_CONTAINED_TYPE = 'PHPFHIRContainedTypeInterface'; const PHPFHIR_INTERFACE_COMMENT_CONTAINER = 'PHPFHIRCommentContainerInterface'; -// traits +// Core traits const PHPFHIR_TRAIT_COMMENT_CONTAINER = 'PHPFHIRCommentContainerTrait'; const PHPFHIR_TRAIT_VALIDATION_ASSERTIONS = 'PHPFHIRValidationAssertionsTrait'; const PHPFHIR_TRAIT_CHANGE_TRACKING = 'PHPFHIRChangeTrackingTrait'; const PHPFHIR_TRAIT_XMLNS = 'PHPFHIRXmlNamespaceTrait'; +const PHPFHIR_TRAIT_XML_SERIALIZABLE_CONFIG = 'PHPFHIRXmlSerializableConfigTrait'; + +// Core enums +const PHPFHIR_ENUM_CONFIG_KEY = 'PHPFHIRConfigKeyEnum'; +const PHPFHIR_ENUM_TYPE = 'PHPFHIRTypeEnum'; +const PHPFHIR_ENUM_API_FORMAT = 'PHPFHIRApiFormatEnum'; // validation constants const PHPFHIR_VALIDATION_ENUM = 'enum'; diff --git a/files/funcs.php b/files/funcs.php index dd0b3468..0c354db8 100644 --- a/files/funcs.php +++ b/files/funcs.php @@ -1,7 +1,7 @@ -definition; } + /** + * Generate FHIR object classes based on XSD + * @throws \ErrorException + */ + public function render(): void + { + $this->prerender(); + + $this->writeCoreTypeFiles(); + + $this->writeFhirTypeFiles(); + + if (!$this->config->isSkipTests()) { + $this->writeFhirTestFiles(); + } + } + /** * Generate FHIR classes only. * @throws \ErrorException */ - public function renderFHIRClasses(): void + public function writeFhirTypeFiles(): void { + // register custom error handler to force explosions. set_error_handler(function ($errNum, $errStr, $errFile, $errLine) { throw new \ErrorException($errStr, $errNum, 1, $errFile, $errLine); }); @@ -87,8 +105,6 @@ public function renderFHIRClasses(): void $definition = $this->getDefinition(); - $this->renderPhpFhirTypes(); - $types = $definition->getTypes(); $log->startBreak('FHIR Class Generation'); @@ -99,11 +115,11 @@ public function renderFHIRClasses(): void if (PHPFHIR_XHTML_TYPE_NAME === $type->getFHIRName()) { $classDefinition = Templates::renderXhtmlTypeClass($this->config, $types, $type); } else { - $classDefinition = Templates::renderTypeClass($this->config, $types, $type); + $classDefinition = Templates::renderFhirTypeClass($this->config, $types, $type); } $filepath = FileUtils::buildTypeFilePath($this->config, $type); if (!file_put_contents($filepath, $classDefinition)) { - throw new RuntimeException( + throw new \RuntimeException( sprintf( 'Unable to write Type %s class definition to file %s', $filepath, @@ -120,7 +136,7 @@ public function renderFHIRClasses(): void /** * Generate Test classes only. Tests will not pass if FHIR classes have not been built. */ - public function renderTestClasses(): void + public function writeFhirTestFiles(): void { $log = $this->config->getLogger(); @@ -131,24 +147,6 @@ public function renderTestClasses(): void $log->startBreak('Test Class Generation'); - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getTestsNamespace(PHPFHIR_TEST_TYPE_BASE, true), - PHPFHIR_TEST_CLASSNAME_CONSTANTS - ), - Templates::renderConstantsTestClass($this->config, $types) - ); - - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getTestsNamespace(PHPFHIR_TEST_TYPE_BASE, true), - PHPFHIR_TEST_CLASSNAME_TYPEMAP - ), - Templates::renderTypeMapTestClass($this->config, $types) - ); - $testTypes = [PHPFHIR_TEST_TYPE_UNIT]; if (null !== $this->config->getTestEndpoint()) { $testTypes[] = PHPFHIR_TEST_TYPE_INTEGRATION; @@ -163,12 +161,12 @@ public function renderTestClasses(): void foreach ($testTypes as $testType) { // skip domain resources // TODO(@dcarbone): why did you do this. - if (PHPFHIR_TEST_TYPE_INTEGRATION === $testType && !$type->isDomainResource()) { + if (PHPFHIR_TEST_TYPE_INTEGRATION === $testType && !$type->isResourceType()) { continue; } $log->debug("Generated {$testType} test class for type {$type}..."); - $classDefinition = Templates::renderTypeTestClass($this->config, $types, $type, $testType); + $classDefinition = Templates::renderFhirTypeClassTest($this->config, $types, $type, $testType); $filepath = FileUtils::buildTypeTestFilePath($this->config, $type, $testType); if (false === file_put_contents($filepath, $classDefinition)) { throw new RuntimeException( @@ -186,23 +184,6 @@ public function renderTestClasses(): void $log->endBreak('Test Class Generation'); } - /** - * Generate FHIR object classes based on XSD - * @throws \ErrorException - */ - public function render(): void - { - $this->prerender(); - - $this->renderFHIRClasses(); - - if (!$this->config->isSkipTests()) { - $this->renderTestClasses(); - } - - $this->renderPhpFhirTypes(); - } - /** * Commands to run prior to class generation */ @@ -218,11 +199,85 @@ protected function prerender(): void } } + /** + * @return \RecursiveIteratorIterator + */ + protected function getCoreTypeFileIterator(): \RecursiveIteratorIterator + { + return new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator( + PHPFHIR_TEMPLATE_CORE_DIR, + \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS + ), + ); + } + + /** + * TODO(@dcarbone): refactor generation system, too sloppy right now. + * + * Renders core PHP FHIR type classes, interfaces, traits, and enums. + * + * @return void + */ + protected function writeCoreTypeFiles(): void + { + $this->log->startBreak('Core Files'); + + // localize types + $types = $this->getDefinition()->getTypes(); + + // render each core file + foreach($this->getCoreTypeFileIterator() as $fpath => $fi) { + /** @var $fi \SplFileInfo */ + + // get filename + $fname = basename($fpath); + // store "type" + $ftype = substr($fname, 0, strpos($fname, '_')); + // trim "type" and ".php" + $fname = strstr(substr($fname, strpos($fname,'_') + 1), '.', true); + // classname suffix + $suffix = ucfirst($ftype); + + // define "default" namespace + $ns = $this->config->getNamespace(true); + + if ('class' === $ftype) { + // 'class' types do have suffix + $suffix = ''; + } else if ('test' === $ftype) { + // test classes have different namespace + $ns = $this->config->getTestsNamespace(PHPFHIR_TEST_TYPE_BASE, true); + // trim subtype + $fname = substr($fname, strpos($fname, '_') + 1); + } + + // construct class filename + $cname = sprintf( + 'PHPFHIR%s%s', + implode('', array_map('ucfirst', explode('_', $fname))), + $suffix + ); + + // write file to disk + $this->writeFile( + FileUtils::buildGenericFilePath( + $this->config, + $ns, + $cname, + ), + Templates::renderCoreType($fpath, $this->config, $types) + ); + } + + $this->log->endBreak('Core Files'); + } + /** * @param string $filePath * @param string $fileContents */ - private function writeClassFile(string $filePath, string $fileContents): void + private function writeFile(string $filePath, string $fileContents): void { $this->log->info(sprintf('Writing %s...', $filePath)); $b = file_put_contents($filePath, $fileContents); @@ -236,139 +291,4 @@ private function writeClassFile(string $filePath, string $fileContents): void } $this->log->debug(sprintf('%d bytes written to file %s', $b, $filePath)); } - - protected function renderPhpFhirTypes(): void - { - $types = $this->definition->getTypes(); - - // Constants class - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_CLASSNAME_CONSTANTS - ), - Templates::renderConstants($this->config, $types) - ); - - // TypeMap class - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_CLASSNAME_TYPEMAP - ), - Templates::renderTypeMapClass($this->config, $types) - ); - - // Autoloader class - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_CLASSNAME_AUTOLOADER - ), - Templates::renderAutoloaderClass($this->config, $types) - ); - - // FHIRType interface - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_INTERFACE_TYPE - ), - Templates::renderPhpFhirTypeInterface($this->config, $types) - ); - - // XmlSerialize interface - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_INTERFACE_XML_SERIALIZABLE - ), - Templates::renderPhpFhirXmlSerializableInterface($this->config, $types) - ); - - // ContainedType interface - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_INTERFACE_CONTAINED_TYPE - ), - Templates::renderPhpFhirContainedTypeInterface($this->config, $types) - ); - - // CommentContainer interface - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_INTERFACE_COMMENT_CONTAINER - ), - Templates::renderPhpFhirCommentContainerInterface($this->config, $types) - ); - - // CommentContainer trait - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_TRAIT_COMMENT_CONTAINER - ), - Templates::renderPhpFhirCommentContainerTrait($this->config, $types) - ); - - // ValidationAssertions trait - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_TRAIT_VALIDATION_ASSERTIONS - ), - Templates::renderPhpFhirValidationAssertionsTrait($this->config, $types) - ); - - // ChangeTracking trait - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_TRAIT_CHANGE_TRACKING - ), - Templates::renderPhpFhirChangeTrackingTrait($this->config, $types) - ); - - // XMLNS trait - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_TRAIT_XMLNS - ), - Templates::renderPhpFhirXhtmlNamespaceTrait($this->config, $types) - ); - - // ResponseParser config class - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_CLASSNAME_RESPONSE_PARSER_CONFIG - ), - Templates::renderPhpFhirResponseParserConfigClass($this->config, $types) - ); - - // ResponseParser class - $this->writeClassFile( - FileUtils::buildGenericFilePath( - $this->config, - $this->config->getNamespace(true), - PHPFHIR_CLASSNAME_RESPONSE_PARSER - ), - Templates::renderPhpFhirResponseParserClass($this->config, $types) - ); - } } diff --git a/src/Definition/Properties.php b/src/Definition/Properties.php index 6820ef83..b349f4c0 100644 --- a/src/Definition/Properties.php +++ b/src/Definition/Properties.php @@ -37,7 +37,7 @@ class Properties implements Countable /** @var \DCarbone\PHPFHIR\Definition\Property[] */ private array $_localProperties; /** @var \DCarbone\PHPFHIR\Definition\Property[] */ - private array $_directSortedProperties; + private array $_localSortedProperties; /** @var bool */ private bool $cacheBuilt = false; @@ -195,7 +195,7 @@ public function localPropertiesIterator(): iterable public function localSortedPropertiesIterator(): iterable { $this->_buildLocalCaches(); - return SplFixedArray::fromArray($this->_directSortedProperties, false); + return SplFixedArray::fromArray($this->_localSortedProperties, false); } /** @@ -211,7 +211,7 @@ private function _buildLocalCaches(): void if (!$this->cacheBuilt) { $this->_sortedProperties = $this->properties; $this->_localProperties = []; - $this->_directSortedProperties = []; + $this->_localSortedProperties = []; usort( $this->_sortedProperties, function (Property $a, Property $b) { @@ -225,7 +225,7 @@ function (Property $a, Property $b) { } foreach ($this->_sortedProperties as $property) { if (!$property->isOverloaded()) { - $this->_directSortedProperties[] = $property; + $this->_localSortedProperties[] = $property; } } $this->cacheBuilt = true; diff --git a/src/Definition/Type.php b/src/Definition/Type.php index 06021bc5..6cafcd57 100644 --- a/src/Definition/Type.php +++ b/src/Definition/Type.php @@ -507,13 +507,11 @@ public function hasPrimitiveContainerParent(): bool } /** - * TODO: super hacky. - * * @return bool */ - public function isDomainResource(): bool + public function isResourceType(): bool { - return str_contains($this->getFullyQualifiedNamespace(false), 'DomainResource'); + return str_contains($this->getFullyQualifiedNamespace(false), '\\FHIRResource\\'); } /** @@ -751,11 +749,6 @@ public function getDirectlyImplementedInterfaces(): array $interfaces = []; $parentType = $this->getParentType(); - if (!$this->isAbstract()) { - // always add xml serializable interface to all non-abstract types - $interfaces[] = PHPFHIR_INTERFACE_XML_SERIALIZABLE; - } - if (null === $parentType) { if ($this->isCommentContainer()) { $interfaces[] = PHPFHIR_INTERFACE_COMMENT_CONTAINER; @@ -769,11 +762,6 @@ public function getDirectlyImplementedInterfaces(): array $interfaces[] = PHPFHIR_INTERFACE_CONTAINED_TYPE; } - // if this is not abstract and has no concrete parent, add the \JsonSerializable interface - if (!$this->isAbstract() && !$this->hasConcreteParent()) { - $interfaces[] = '\\JsonSerializable'; - } - return $interfaces; } diff --git a/src/Definition/TypeImports.php b/src/Definition/TypeImports.php index 5fe2aebe..0bd9ba63 100644 --- a/src/Definition/TypeImports.php +++ b/src/Definition/TypeImports.php @@ -225,6 +225,13 @@ private function buildImports(): void $sortedProperties = $this->type->getAllPropertiesIterator(); + // always add xml serializable interface and root config to all non-abstract types + if (!$this->type->isAbstract()) { + $this->addImport(PHPFHIR_CLASSNAME_CONFIG, $configNS); + $this->addImport(PHPFHIR_INTERFACE_XML_SERIALIZABLE, $configNS); + $this->addImport(PHPFHIR_INTERFACE_XML_SERIALIZALE_CONFIG, $configNS); + } + // if this type is in a nested namespace, there are a few base interfaces, classes, and traits // that may need to be imported to ensure function if ($typeNS !== $configNS) { diff --git a/src/Definition/Types.php b/src/Definition/Types.php index 3465cd05..657267ed 100644 --- a/src/Definition/Types.php +++ b/src/Definition/Types.php @@ -42,6 +42,12 @@ class Types implements Countable */ private Type $containerType; + /** + * This will eventually be the "Bundle" type seen + * @var \DCarbone\PHPFHIR\Definition\Type + */ + private Type $bundleType; + /** * FHIRTypes constructor. * @param \DCarbone\PHPFHIR\Config\VersionConfig $config @@ -71,7 +77,7 @@ public function __debugInfo() * @param string $name * @return \DCarbone\PHPFHIR\Definition\Type|null */ - public function getTypeByName(string $name): ?Type + public function getTypeByName(string $name): null|Type { foreach ($this->types as $type) { if ($type->getFHIRName() === $name) { @@ -85,7 +91,7 @@ public function getTypeByName(string $name): ?Type * @param string $name * @return \DCarbone\PHPFHIR\Definition\Type|null */ - public function getTypeByClassName(string $name): ?Type + public function getTypeByClassName(string $name): null|Type { foreach ($this->types as $type) { if ($type->getClassName() === $name) { @@ -100,7 +106,7 @@ public function getTypeByClassName(string $name): ?Type * @param bool $leadingSlash * @return \DCarbone\PHPFHIR\Definition\Type|null */ - public function getTypeByFQN(string $fqn, bool $leadingSlash): ?Type + public function getTypeByFQN(string $fqn, bool $leadingSlash): null|Type { foreach ($this->types as $type) { if ($type->getFullyQualifiedClassName($leadingSlash) === $fqn) { @@ -192,7 +198,7 @@ function (Type $t1, Type $t2) { * @param string $version * @return \DCarbone\PHPFHIR\Definition\Type|null */ - public function getContainerType(string $version): ?Type + public function getContainerType(string $version): null|Type { if (!isset($this->containerType)) { foreach ($this->types as $type) { @@ -228,6 +234,22 @@ public function isContainedType(string $version, Type $type): bool return false; } + /** + * @return \DCarbone\PHPFHIR\Definition\Type|null + */ + public function getBundleType(): null|Type + { + if (!isset($this->bundleType)) { + foreach($this->types as $type) { + if ($type->getFHIRName() === 'Bundle') { + $this->bundleType = $type; + break; + } + } + } + return $this->bundleType ?? null; + } + /** * @return int */ diff --git a/src/Render/Templates.php b/src/Render/Templates.php index 6001203e..e97d0bad 100644 --- a/src/Render/Templates.php +++ b/src/Render/Templates.php @@ -29,109 +29,14 @@ abstract class Templates { /** + * @param string $coreFilename * @param \DCarbone\PHPFHIR\Config\VersionConfig $config * @param \DCarbone\PHPFHIR\Definition\Types $types * @return string */ - public static function renderPhpFhirTypeInterface(VersionConfig $config, Types $types): string + public static function renderCoreType(string $coreFilename, VersionConfig $config, Types $types): string { - return require PHPFHIR_TEMPLATE_INTERFACES_DIR . DIRECTORY_SEPARATOR . 'phpfhir_type.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderPhpFhirXmlSerializableInterface(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_INTERFACES_DIR . DIRECTORY_SEPARATOR . 'phpfhir_xml_serializable.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderPhpFhirCommentContainerInterface(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_INTERFACES_DIR . DIRECTORY_SEPARATOR .'phpfhir_comment_container.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderPhpFhirCommentContainerTrait(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_TRAITS_DIR . DIRECTORY_SEPARATOR . 'phpfhir_comment_container.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderPhpFhirValidationAssertionsTrait(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_TRAITS_DIR . DIRECTORY_SEPARATOR .'phpfhir_validation_assertions.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderPhpFhirChangeTrackingTrait(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_TRAITS_DIR . DIRECTORY_SEPARATOR . 'phpfhir_change_tracking.php'; - } - - public static function renderPhpFhirXhtmlNamespaceTrait(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_TRAITS_DIR . DIRECTORY_SEPARATOR . 'phpfhir_xml_namespace.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderPhpFhirContainedTypeInterface(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_INTERFACES_DIR . DIRECTORY_SEPARATOR . 'phpfhir_contained_type.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderPhpFhirResponseParserConfigClass(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_UTILITIES_DIR . DIRECTORY_SEPARATOR .'class_response_parser_config.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderPhpFhirResponseParserClass(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_UTILITIES_DIR . DIRECTORY_SEPARATOR .'class_response_parser.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @param \DCarbone\PHPFHIR\Definition\Type $type - * @return string - */ - public static function renderTypeClass(VersionConfig $config, Types $types, Type $type): string - { - return require PHPFHIR_TEMPLATE_TYPES_DIR . DIRECTORY_SEPARATOR . 'class_default.php'; + return require $coreFilename; } /** @@ -148,51 +53,12 @@ public static function renderXhtmlTypeClass(VersionConfig $config, Types $types, /** * @param \DCarbone\PHPFHIR\Config\VersionConfig $config * @param \DCarbone\PHPFHIR\Definition\Types $types + * @param \DCarbone\PHPFHIR\Definition\Type $type * @return string */ - public static function renderConstants(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_UTILITIES_DIR . DIRECTORY_SEPARATOR .'class_constants.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderTypeMapClass(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_UTILITIES_DIR . DIRECTORY_SEPARATOR . 'class_typemap.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderAutoloaderClass(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_UTILITIES_DIR . DIRECTORY_SEPARATOR . 'class_autoloader.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderConstantsTestClass(VersionConfig $config, Types $types): string - { - return require PHPFHIR_TEMPLATE_TESTS_DIR . DIRECTORY_SEPARATOR . 'test_class_constants.php'; - } - - /** - * @param \DCarbone\PHPFHIR\Config\VersionConfig $config - * @param \DCarbone\PHPFHIR\Definition\Types $types - * @return string - */ - public static function renderTypeMapTestClass(VersionConfig $config, Types $types): string + public static function renderFhirTypeClass(VersionConfig $config, Types $types, Type $type): string { - return require PHPFHIR_TEMPLATE_TESTS_DIR . DIRECTORY_SEPARATOR . 'test_class_type_map.php'; + return require PHPFHIR_TEMPLATE_TYPES_DIR . DIRECTORY_SEPARATOR . 'class_default.php'; } /** @@ -202,8 +68,8 @@ public static function renderTypeMapTestClass(VersionConfig $config, Types $type * @param string $testType * @return string */ - public static function renderTypeTestClass(VersionConfig $config, Types $types, Type $type, string $testType): string + public static function renderFhirTypeClassTest(VersionConfig $config, Types $types, Type $type, string $testType): string { - return require PHPFHIR_TEMPLATE_TESTS_TYPES_DIR . DIRECTORY_SEPARATOR . $testType . DIRECTORY_SEPARATOR .'class_default.php'; + return require PHPFHIR_TEMPLATE_TYPE_TESTS_DIR . DIRECTORY_SEPARATOR . $testType . DIRECTORY_SEPARATOR .'class.php'; } } \ No newline at end of file diff --git a/template/utilities/class_autoloader.php b/template/core/classes/class_autoloader.php similarity index 72% rename from template/utilities/class_autoloader.php rename to template/core/classes/class_autoloader.php index 15e7833e..ec275b61 100644 --- a/template/utilities/class_autoloader.php +++ b/template/core/classes/class_autoloader.php @@ -27,7 +27,7 @@ ob_start(); -echo "', false)) { require __DIR__ . DIRECTORY_SEPARATOR . '.php'; } +if (!interface_exists('\', false)) { + require __DIR__ . DIRECTORY_SEPARATOR . '.php'; +} +if (!interface_exists('\', false)) { + require __DIR__ . DIRECTORY_SEPARATOR . '.php'; +} // traits if (!trait_exists('\', false)) { @@ -62,20 +68,37 @@ if (!trait_exists('\', false)) { require __DIR__ . DIRECTORY_SEPARATOR . '.php'; } +if (!trait_exists('\', false)) { + require __DIR__ . DIRECTORY_SEPARATOR . '.php'; +} -// common classes +// enums +if (!enum_exists('\', false)) { + require __DIR__ . DIRECTORY_SEPARATOR . '.php'; +} +if (!enum_exists('\', false)) { + require __DIR__ . DIRECTORY_SEPARATOR . '.php'; +} +if (!enum_exists('\', false)) { + require __DIR__ . DIRECTORY_SEPARATOR . '.php'; +} + +// classes if (!class_exists('\', false)) { require __DIR__ . DIRECTORY_SEPARATOR . '.php'; } if (!class_exists('\', false)) { require __DIR__ . DIRECTORY_SEPARATOR . '.php'; } -if (!class_exists('\', false)) { - require __DIR__ . DIRECTORY_SEPARATOR . '.php'; +if (!class_exists('\', false)) { + require __DIR__ . DIRECTORY_SEPARATOR . '.php'; } if (!class_exists('\', false)) { require __DIR__ . DIRECTORY_SEPARATOR . '.php'; } +if (!class_exists('\', false)) { + require __DIR__ . DIRECTORY_SEPARATOR . '.php'; +} /** * Class @@ -102,10 +125,10 @@ abstract class */ public static function register(): bool { - if (self::$_registered) { - return self::$_registered; + if (!self::$_registered) { + self::$_registered = spl_autoload_register(__CLASS__ . '::loadClass', true); } - return self::$_registered = spl_autoload_register([__CLASS__, 'loadClass'], true); + return self::$_registered; } /** @@ -114,7 +137,7 @@ public static function register(): bool public static function unregister(): bool { if (self::$_registered) { - if (spl_autoload_unregister([__CLASS__, 'loadClass'])) { + if (spl_autoload_unregister(__CLASS__ . '::loadClass')) { self::$_registered = false; return true; } @@ -128,7 +151,7 @@ public static function unregister(): bool * @param string $class * @return bool|null */ - public static function loadClass(string $class): ?bool + public static function loadClass(string $class): null|bool { if (isset(self::_CLASS_MAP[$class])) { return (bool)require __DIR__ . DIRECTORY_SEPARATOR . self::_CLASS_MAP[$class]; diff --git a/template/utilities/class_response_parser_config.php b/template/core/classes/class_config.php similarity index 51% rename from template/utilities/class_response_parser_config.php rename to template/core/classes/class_config.php index f28bedbe..b1333eef 100644 --- a/template/utilities/class_response_parser_config.php +++ b/template/core/classes/class_config.php @@ -25,7 +25,7 @@ ob_start(); -echo " /** - * Class + * Class * @package \ */ -class implements \JsonSerializable +class implements , \JsonSerializable { - const KEY_REGISTER_AUTOLOADER = 'registerAutoloader'; - const KEY_LIBXML_OPTS = 'libxmlOpts'; - - /** @var array */ - private static array $_keysWithDefaults = [ - self::KEY_REGISTER_AUTOLOADER => false, - self::KEY_LIBXML_OPTS => getLibxmlOpts()) ? 'null' : $opts; ?>, - ]; + use ; /** @var bool */ private bool $registerAutoloader; - /** @var int */ - private int $libxmlOpts; /** - * Constructor + * Constructor * @param array $config */ public function __construct(array $config = []) { - foreach(self::$_keysWithDefaults as $k => $v) { - if (isset($config[$k]) || array_key_exists($k, $config)) { - $this->{'set'.$k}($config[$k]); - } else { - $this->{'set'.$k}($v); - } + foreach(self::getKeysWithDefaults() as $k => $v) { + $this->setKey($k, $config[$k] ?? $v); } } /** - * @param bool $registerAutoloader - * @return void + * TODO(@dcarbone): Return const once we drop 8.1 + * + * @return array */ - public function setRegisterAutoloader($registerAutoloader): void + public static function getKeysWithDefaults(): array { - $this->registerAutoloader = (bool)$registerAutoloader; + return [ + PHPFHIRConfigKeysEnum::REGISTER_AUTOLOADER->value => false, + PHPFHIRConfigKeysEnum::LIBXML_OPTS->value => ::DEFAULT_LIBXML_OPTS, + ]; } /** - * @return bool + * Set arbitrary key on this config + * + * @param \|string $key + * @param mixed $value + * @return static */ - public function getRegisterAutoloader(): bool + public function setKey(PHPFHIRConfigKeysEnum|string $key, mixed $value): self { - return $this->registerAutoloader; + if (!is_string($key)) { + $key = $key->value; + } + $this->{'set'.$key}($value); + return $this; } /** - * @param int $libxmlOpts + * @param bool $registerAutoloader + * @return static */ - public function setLibxmlOpts(int $libxmlOpts): void + public function setRegisterAutoloader(bool $registerAutoloader): self { - $this->libxmlOpts = $libxmlOpts; + $this->registerAutoloader = $registerAutoloader; + return $this; } /** - * @return int + * @return bool */ - public function getLibxmlOpts(): int + public function getRegisterAutoloader(): bool { - return $this->libxmlOpts; + return $this->registerAutoloader; } /** * @return \stdClass */ - public function jsonSerialize() + public function jsonSerialize(): \stdClass { - $out= new \stdClass(); - foreach(self::$_keysWithDefaults as $k => $_) { + $out = new \stdClass(); + foreach(self::getKeysWithDefaults() as $k => $_) { $out->{$k} = $this->{'get'.$k}(); } return $out; diff --git a/template/utilities/class_constants.php b/template/core/classes/class_constants.php similarity index 98% rename from template/utilities/class_constants.php rename to template/core/classes/class_constants.php index d3020da5..4fb976e3 100644 --- a/template/utilities/class_constants.php +++ b/template/core/classes/class_constants.php @@ -25,7 +25,7 @@ ob_start(); -echo "getNamespace(false); + +ob_start(); + +echo " +/** + * This client is intended for debug purposes only, and is not intended to be used in a production environment. + * + * Class + + * @package \ + + + */ +class + +{ + /** @var string */ + private string $_baseUrl; + /** @var array */ + private array $_curlOpts; + + /** + * Constructor + * + * @param string $baseUrl URL of FHIR server to query + * @param array $curlOpts Base curl options array + */ + public function __construct(string $baseUrl, array $curlOpts = []) + { + $this->_baseUrl = $baseUrl; + $this->_curlOpts = $curlOpts; + } + + /** + * Execute a read operation for a particular resource. + * + * @see https://www.hl7.org/fhir/http.html#read + * + * @param string|\ $resource + * @param string $id + * @param string|\ $format + * @param null|\ $parser + * @return null|\ + + * @throws \Exception + */ + public function readOne(string| $resource, string $id, string| $format = ::JSON, null| $parser = null): null| + + { + if (!is_string($resource)) { + $resource = $resource->value; + } + if (!is_string($format)) { + $format = $format->value; + } + + $url = sprintf('%s/%s/%s?_format=%s', $this->_baseUrl, $resource, $id, $format); + + $ch = curl_init($url); + curl_setopt_array( + $ch, + $this->_curlOpts + [ + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => 0, + ] + ); + + $resp = curl_exec($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $err = curl_error($ch); + curl_close($ch); + + if ('' !== $err) { + throw new \Exception(sprintf('Error executing "%s": %s', $url, $err)); + } + if (200 !== $code) { + throw new \Exception(sprintf('Error executing "%s": Expected 200 OK, saw %d', $url, $code)); + } + + if (null === $parser) { + $parser = new (new ()); + } + + return $parser->parse($resp); + } + + /** + * Execute a read operation for a particular resource. + * + * @see https://www.hl7.org/fhir/http.html#read + * + * @param string|\ $resource + * @param string $id + * @param string|\ $format + * @param null|\ $parser + * @return null|\ + + * @throws \Exception + */ + public function readFirst(string| $resource, string| $format = ::JSON, null| $parser = null): null| + + { + if (!is_string($resource)) { + $resource = $resource->value; + } + if (!is_string($format)) { + $format = $format->value; + } + + $url = sprintf('%s/%s?_count=1&_format=%s', $this->_baseUrl, $resource, $id, $format); + + $ch = curl_init($url); + curl_setopt_array( + $ch, + $this->_curlOpts + [ + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => 0, + ] + ); + + $resp = curl_exec($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $err = curl_error($ch); + curl_close($ch); + + if ('' !== $err) { + throw new \Exception(sprintf('Error executing "%s": %s', $url, $err)); + } + if (200 !== $code) { + throw new \Exception(sprintf('Error executing "%s": Expected 200 OK, saw %d', $url, $code)); + } + + if (null === $parser) { + $parser = new (new ()); + } + + return $parser->parse($resp); + } +} +getNamespace(false); + +ob_start(); + +echo " + +/** + * Class + + * @package \ + + + */ +class + +{ + private const XML_START = ['<']; + private const JSON_START = ['{', '[']; + + /** @var \ $config */ + private $config; + + /** + * Constructor + * @param null|\ $config + */ + public function __construct(null| $config = null) + { + if (null === $config) { + $config = new ; + } + $this->config = $config; + } + + /** + * @return \ + + */ + public function getConfig(): + + { + return $this->config; + } + + /** + * Attempts to parse the provided input into FHIR objects. + * + * @param null|string|array|\stdClass|\SimpleXMLElement|\DOMDocument $input + * @return null|\ + + */ + public function parse(null|string|array|\stdClass|\SimpleXMLElement|\DOMDocument $input): null| + + { + if (null === $input) { + return null; + } else if (is_string($input)) { + return $this->parseString($input); + } else if (is_array($input)) { + return $this->parseArray($input); + } else { + return $this->parseObject($input); + } + } + + /** + * @param array $input + * @return null|\ + + */ + public function parseArray(array $input): null| + + { + if ([] === $input) { + return null; + } + if (isset($input[::JSON_FIELD_RESOURCE_TYPE])) { + $className = ::getTypeClass($input[::JSON_FIELD_RESOURCE_TYPE]); + if (null === $className) { + throw new \UnexpectedValueException(sprintf( + 'Provided input has "%s" value of "%s", but it does not map to any known type. Other keys: ["%s"]', + ::JSON_FIELD_RESOURCE_TYPE, + $input[::JSON_FIELD_RESOURCE_TYPE], + implode('","', array_keys($input)) + )); + } + return new $className($input); + } + throw new \DomainException(sprintf( + 'Unable to determine FHIR Type from provided array: missing "%s" key. Available keys: ["%s"]', + ::JSON_FIELD_RESOURCE_TYPE, + implode('","', array_keys($input)) + )); + } + + /** + * @param \stdClass $input + * @return null|\ + + */ + public function parseStdClass(\stdClass $input): null| + + { + return $this->parseArray((array)$input); + } + + /** + * @param \SimpleXMLElement $input + * @return null|\ + + */ + public function parseSimpleXMLElement(\SimpleXMLElement $input): null| + + { + return $this->parseDOMDocument(dom_import_simplexml($input)->ownerDocument); + } + + /** + * @param \DOMDocument $input + * @return null|\ + + */ + public function parseDOMDocument(\DOMDocument $input): null| + + { + $elementName = $input->documentElement->nodeName; + /** @var \ $fhirType */ + $fhirType = ::getTypeClass($elementName); + if (null === $fhirType) { + throw new \UnexpectedValueException(sprintf( + 'Unable to locate FHIR type for root XML element "%s". Input seen: %s', + $elementName, + $this->getPrintableStringInput($input->saveXML()) + )); + } + return $fhirType::xmlUnserialize($input->documentElement, $this->config); + } + + /** + * @param \stdClass|\SimpleXMLElement|\DOMDocument $input + * @return null|\ + + */ + public function parseObject(\stdClass|\SimpleXMLElement|\DOMDocument $input): null| + + { + if ($input instanceof \stdClass) { + return $this->parseStdClass($input); + } elseif ($input instanceof \SimpleXMLElement) { + return $this->parseSimpleXMLElement($input); + } else { + return $this->parseDOMDocument($input); + } + } + + /** + * @param string $input + * @return null|\ + + */ + public function parseXml(string $input): null| + + { + libxml_use_internal_errors(true); + $dom = new \DOMDocument(); + $dom->loadXML($input, $this->config->getLibxmlOpts()); + $err = libxml_get_last_error(); + libxml_use_internal_errors(false); + if (false === $err) { + return $this->parseDOMDocument($dom); + } + throw new \DomainException(sprintf( + 'Unable to parse provided input as XML. Error: %s; Input: %s', + $err ? $err->message : 'Unknown', + $this->getPrintableStringInput($input) + )); + } + + /** + * @param string $input + * @return null|\ + + */ + public function parseJson(string $input): null| + + { + $decoded = json_decode($input, true); + $err = json_last_error(); + if (JSON_ERROR_NONE !== $err) { + throw new \DomainException(sprintf( + 'Unable to parse provided input as JSON. Error: %s; Input: %s', + json_last_error_msg(), + $this->getPrintableStringInput($input) + )); + } + + return $this->parseArray($decoded); + } + + /** + * @param string $input + * @return null|\ + + */ + public function parseString(string $input): null| + + { + $input = trim($input); + if ('' === $input) { + return null; + } + $chr = $input[0]; + if (in_array(self::XML_START, $chr, true)) { + return $this->parseXml($input); + } elseif (in_array(self::JSON_START, $chr, true)) { + return $this->parseJson($input); + } + throw new \UnexpectedValueException(sprintf( + 'Input string must be either XML or JSON encoded object. Provided: %s', + $this->getPrintableStringInput($input) + )); + } + + /** + * @param string $input + * @return string + */ + protected function getPrintableStringInput(string $input): string + { + if (strlen($input) > 100) { + return sprintf('%s[...]', substr($input, 0, 100)); + } + return $input; + } +} +getNamespace(false); + +ob_start(); + +echo " +/** + * Enum + + * @package \ + + + */ +enum : string +{ + case XML = 'xml'; + case JSON = 'json'; +} + +getNamespace(false); + +ob_start(); + +echo " +/** + * Enum + + * @package \ + + + */ +enum : string +{ + case REGISTER_AUTOLOADER = 'registerAutoloader'; + case LIBXML_OPTS = 'libxmlOpts'; +} + +getNamespace(false); + +ob_start(); + +echo " +/** + * Enum + + * @package \ + + + */ +enum : string +{ +getNameSortedIterator() as $type) : if ($type->isAbstract()) { continue; } ?> + case getConstName(false); ?> = 'getFHIRName(); ?>'; + +} + + */ -interface extends , +interface extends { /** diff --git a/template/interfaces/phpfhir_type.php b/template/core/interfaces/interface_type.php similarity index 92% rename from template/interfaces/phpfhir_type.php rename to template/core/interfaces/interface_type.php index bfc179ff..904198d8 100644 --- a/template/interfaces/phpfhir_type.php +++ b/template/core/interfaces/interface_type.php @@ -24,7 +24,7 @@ ob_start(); -echo " */ -interface - +interface extends , \JsonSerializable { /** * Returns the FHIR name represented by this Type diff --git a/template/interfaces/phpfhir_xml_serializable.php b/template/core/interfaces/interface_xml_serializable.php similarity index 65% rename from template/interfaces/phpfhir_xml_serializable.php rename to template/core/interfaces/interface_xml_serializable.php index e5b81d38..bcc203b6 100644 --- a/template/interfaces/phpfhir_xml_serializable.php +++ b/template/core/interfaces/interface_xml_serializable.php @@ -24,7 +24,7 @@ ob_start(); -echo " $config XML serialization config. Supports an integer value interpreted as libxml opts for backwards compatibility. * @return null|static */ - public static function xmlUnserialize(null|string|\DOMElement $element, $type = null, ?int $libxmlOpts = getLibxmlOpts()) ? 'null' : $opts; ?>): null|self; + public static function xmlUnserialize(null|string|\DOMNode $element, $type = null, null|int| $config = null): null|self; /** - * @param null|\DOMElement $element - * @param null|int $libxmlOpts - * @return \DOMElement + * @param null|\DOMNode $element + * @param null|int|\ $config XML serialization config. Supports an integer value interpreted as libxml opts for backwards compatibility. + * @return \DOMNode */ - public function xmlSerialize(null|\DOMElement $element = null, null|int $libxmlOpts = getLibxmlOpts()) ? 'null' : $opts; ?>): \DOMElement; + public function xmlSerialize(null|\DOMNode $element = null, null|int| $config = null): \DOMNode; } getNamespace(false); + +ob_start(); + +echo " +/** + * Interface + + * @package \ + + + */ +interface + +{ + public const DEFAULT_LIBXML_OPTS = LIBXML_NONET | LIBXML_PARSEHUGE | LIBXML_COMPACT; + + /** + * Sets the option flags to provide to libxml when serializing and unserializing XML + * + * @param int $libxmlOpts + * @return static + */ + public function setLibxmlOpts(int $libxmlOpts): self; + + /** + * Must return the set libxml option flags + * + * @return int + */ + public function getLibxmlOpts(): int; +} + * @param null|array $value) * @return null|string */ - protected function _assertMinOccurs(string $typeName, string $fieldName, int $expected, ?array $value): ?string + protected function _assertMinOccurs(string $typeName, string $fieldName, int $expected, null|array $value): null|string { if (0 >= $expected) { return null; @@ -73,9 +73,9 @@ protected function _assertMinOccurs(string $typeName, string $fieldName, int $ex * @param null|array $value * @return null|string */ - protected function _assertMaxOccurs(string $typeName, string $fieldName, int $expected, ?array $value): ?string + protected function _assertMaxOccurs(string $typeName, string $fieldName, int $expected, null|array $value): null|string { - if (PHPFHIRConstants::UNLIMITED === $expected || null === $value || !is_array($value) || [] === $value || $expected >= ($cnt = count($value))) { + if (PHPFHIRConstants::UNLIMITED === $expected || null === $value || [] === $value || $expected >= ($cnt = count($value))) { return null; } return sprintf('Field "%s" on type "%s" must have no more than %d elements, %d seen', $fieldName, $typeName, $expected, $cnt); @@ -89,7 +89,7 @@ protected function _assertMaxOccurs(string $typeName, string $fieldName, int $ex * @param null|string $value * @return null|string */ - protected function _assertMinLength(string $typeName, string $fieldName, int $expected, ?string $value): ?string + protected function _assertMinLength(string $typeName, string $fieldName, int $expected, null|string $value): null|string { if (0 >= $expected) { return null; @@ -112,7 +112,7 @@ protected function _assertMinLength(string $typeName, string $fieldName, int $ex * @param null|string $value * @return null|string */ - protected function _assertMaxLength(string $typeName, string $fieldName, int $expected, ?string $value): ?string + protected function _assertMaxLength(string $typeName, string $fieldName, int $expected, null|string $value): null|string { if (PHPFHIRConstants::UNLIMITED === $expected || !is_string($value) || '' === $value) { return null; @@ -132,7 +132,7 @@ protected function _assertMaxLength(string $typeName, string $fieldName, int $ex * @param mixed $value * @return null|string */ - protected function _assertValueInEnum(string $typeName, string $fieldName, array $expected, $value): ?string + protected function _assertValueInEnum(string $typeName, string $fieldName, array $expected, mixed $value): null|string { if ([] === $expected || in_array($value, $expected, true)) { return null; @@ -157,12 +157,12 @@ function($v) { return var_export($v, true); }, * @param string $typeName * @param string $fieldName * @param string $pattern - * @param null|string $value + * @param null|float|int|string|bool $value * @return null|string */ - protected function _assertPatternMatch(string $typeName, string $fieldName, string $pattern, ?string $value): ?string + protected function _assertPatternMatch(string $typeName, string $fieldName, string $pattern, null|float|int|string|bool $value): null|string { - if (!is_string($pattern) || '' === $pattern || (bool)preg_match($pattern, $value)) { + if ('' === $pattern || (bool)preg_match($pattern, (string)$value)) { return null; } return sprintf('Field "%s" on type "%s" value of "%s" does not match pattern: %s', $fieldName, $typeName, $value, $pattern); @@ -176,25 +176,17 @@ protected function _assertPatternMatch(string $typeName, string $fieldName, stri * @param mixed $value * @return null|string */ - protected function _performValidation(string $typeName, string $fieldName, string $ruleName, $constraint, $value): ?string + protected function _performValidation(string $typeName, string $fieldName, string $ruleName, mixed $constraint, mixed $value): null|string { - switch($ruleName) { - case PHPFHIRConstants::: - return $this->_assertValueInEnum($typeName, $fieldName, $constraint, $value); - case PHPFHIRConstants::: - return $this->_assertMinLength($typeName, $fieldName, $constraint, $value); - case PHPFHIRConstants::: - return $this->_assertMaxLength($typeName, $fieldName, $constraint, $value); - case PHPFHIRConstants::: - return $this->_assertMinOccurs($typeName, $fieldName, $constraint, $value); - case PHPFHIRConstants::: - return $this->_assertMaxOccurs($typeName, $fieldName, $constraint, $value); - case PHPFHIRConstants::: - return $this->_assertPatternMatch($typeName, $fieldName, $constraint, $value); - - default: - return sprintf('Type "%s" specifies unknown validation for field "%s": Name "%s"; Constraint "%s"', $typeName, $fieldName, $ruleName, var_export($constraint, true)); - } + return match ($ruleName) { + PHPFHIRConstants::VALIDATE_ENUM => $this->_assertValueInEnum($typeName, $fieldName, $constraint, $value), + PHPFHIRConstants::VALIDATE_MIN_LENGTH => $this->_assertMinLength($typeName, $fieldName, $constraint, $value), + PHPFHIRConstants::VALIDATE_MAX_LENGTH => $this->_assertMaxLength($typeName, $fieldName, $constraint, $value), + PHPFHIRConstants::VALIDATE_MIN_OCCURS => $this->_assertMinOccurs($typeName, $fieldName, $constraint, $value), + PHPFHIRConstants::VALIDATE_MAX_OCCURS => $this->_assertMaxOccurs($typeName, $fieldName, $constraint, $value), + PHPFHIRConstants::VALIDATE_PATTERN => $this->_assertPatternMatch($typeName, $fieldName, $constraint, $value), + default => sprintf('Type "%s" specifies unknown validation for field "%s": Name "%s"; Constraint "%s"', $typeName, $fieldName, $ruleName, var_export($constraint, true)), + }; } } */ - trait +trait { /** @var string */ @@ -48,7 +48,7 @@ trait /** * @param null|string $xmlNamespace - * @return self + * @return static */ public function _setFHIRXMLNamespace(null|string $xmlNamespace): self { @@ -65,10 +65,15 @@ public function _getFHIRXMLNamespace(): string } /** + * @param string $elementName Name to use for the element * @return string + * @throws \InvalidArgumentException */ public function _getFHIRXMLElementDefinition(string $elementName): string { + if ('' === $elementName) { + throw new \InvalidArgumentException(sprintf('%s::_getFHIRXMLElementDefinition - $elementName is required', get_called_class())); + } $xmlns = $this->_getFHIRXMLNamespace(); if ('' !== $xmlns) { $xmlns = sprintf(' xmlns="%s"', $xmlns); diff --git a/template/core/traits/trait_xml_serializable_config.php b/template/core/traits/trait_xml_serializable_config.php new file mode 100644 index 00000000..56c0ed66 --- /dev/null +++ b/template/core/traits/trait_xml_serializable_config.php @@ -0,0 +1,71 @@ +getNamespace(false); + +ob_start(); +echo " + /** + * Trait + + * @package \ + + + */ +trait + +{ + /** @var int */ + private int $libxmlOpts; + + /** + * Sets the option flags to provide to libxml when serializing and unserializing XML + * + * @param int $libxmlOpts + * @return static + */ + public function setLibxmlOpts(int $libxmlOpts): self + { + $this->libxmlOpts = $libxmlOpts; + return $this; + } + + /** + * Returns set libxml option flags + * + * @return int + */ + public function getLibxmlOpts(): int + { + return $this->libxmlOpts ?? ::DEFAULT_LIBXML_OPTS; + } +} +getKind(); - -$bundleType = null; -$bundleEntryProperty = null; -// only bother to locate bundle type if there is a configured test endpoint -if ($type->isDomainResource()) { - // TODO: find a more efficient way to do this... - foreach ($types->getIterator() as $bt) { - if ($bt->getFHIRName() === 'Bundle') { - $bundleType = $bt; - break; - } - } - if (null === $bundleType) { - throw ExceptionUtils::createBundleTypeNotFoundException($type); - } - - foreach($bundleType->getAllPropertiesIterator() as $prop) { - if ($prop->getName() === 'entry') { - $bundleEntryProperty = $prop; - break; - } - } - - if (null === $bundleEntryProperty) { - throw ExceptionUtils::createBundleEntryPropertyNotFoundException($type); - } -} - -ob_start(); - -if ($type->isDomainResource()) : - echo require_with( - PHPFHIR_TEMPLATE_TESTS_TYPES_DIR . DIRECTORY_SEPARATOR . $testType . DIRECTORY_SEPARATOR . 'header_domain_resource.php', - [ - 'config' => $config, - 'type' => $type, - 'bundleType' => $bundleType, - ] - ); - - echo require_with( - PHPFHIR_TEMPLATE_TESTS_TYPES_DIR . DIRECTORY_SEPARATOR . $testType . DIRECTORY_SEPARATOR . 'body_domain_resource.php', - [ - 'config' => $config, - 'type' => $type, - 'bundleType' => $bundleType, - 'bundleEntryProperty' => $bundleEntryProperty, - ] - ); -endif; - -echo "}\n"; -return ob_get_clean(); diff --git a/template/tests/types/integration/header_domain_resource.php b/template/tests/types/integration/header_domain_resource.php deleted file mode 100644 index 5a04d2ad..00000000 --- a/template/tests/types/integration/header_domain_resource.php +++ /dev/null @@ -1,53 +0,0 @@ -getFullyQualifiedTestNamespace(PHPFHIR_TEST_TYPE_INTEGRATION, false); -$testClassname = $type->getTestClassName(); -$typeNS = $type->getFullyQualifiedClassName(false); -$typeClassname = $type->getClassName(); - -echo "namespace {$testNS};\n\n"; - -echo CopyrightUtils::getFullPHPFHIRCopyrightComment(); -echo "\n\n"; -echo "use PHPUnit\\Framework\\TestCase;\n"; -echo "use {$type->getFullyQualifiedClassName(false)};\n"; -echo "use PHPUnit\\Framework\\AssertionFailedError;\n"; -echo "use {$bundleType->getFullyQualifiedClassName(false)};\n"; -?> - -/** - * Class - - * @package \ - - */ -class extends TestCase -{ -getClassName(); ?> implements , ; + const _NOISE_NODES = ['html', 'head', 'body']; + /** @var null|\DOMNode */ private null|\DOMNode $_node = null; @@ -84,19 +86,28 @@ public function getNode(): null|\DOMNode public function setNode(null|string|\DOMNode $node): self { if (null === $node) { - $newNode = null; - } else if (is_string($node)) { - $dom = new \DOMDocument(); + $this->_trackValueSet($this->_node, null); + $this->_node = null; + return $this; + } + $dom = new \DOMDocument(); + if (is_string($node)) { $dom->loadHTML($node); - $newNode = $dom->documentElement; } else if ($node instanceof \DOMDocument) { - $dom = new \DOMDocument(); $dom->appendChild($dom->importNode($node->documentElement, true)); - $newNode = $dom->documentElement; } else { - $dom = new \DOMDocument(); $dom->appendChild($dom->importNode($node, true)); - $newNode = $dom->documentElement; + } + $newNode = $dom->documentElement; + while (null !== $newNode) { + if (in_array(strtolower($newNode->nodeName), self::_NOISE_NODES, true)) { + $newNode = $newNode->firstChild; + } else { + break; + } + } + if ('' !== ($ens = (string)$newNode?->namespaceURI)) { + $this->_setFHIRXMLNamespace($ens); } $this->_trackValueSet($this->_node, $newNode); $this->_node = $newNode; @@ -121,23 +132,31 @@ public function setNode(null|string|\DOMNode $node): self } /** - * @param \DOMElement|null $element - * @param null|int $libxmlOpts - * @return \DOMElement + * @param \DOMNode|null $element + * @param null|int|\ $config XML serialization config. Supports an integer value interpreted as libxml opts for backwards compatibility. + * @return \DOMNode */ - public function xmlSerialize(\DOMElement $element = null, ?int $libxmlOpts = getLibxmlOpts()) ? 'null' : $opts; ?>): \DOMElement + public function xmlSerialize(\DOMNode $element = null, null|int| $config = null): \DOMNode { + if (is_int($config)) { + $libxmlOpts = $config; + $config = null; + } else { + $libxmlOpts = $config?->getLibxmlOpts() ?? ::DEFAULT_LIBXML_OPTS; + } if (null === $element) { $dom = new \DOMDocument(); $dom->loadXML($this->_getFHIRXMLElementDefinition(''), $libxmlOpts); $element = $dom->documentElement; + } else if ('' !== ($ns = $this->_getFHIRXMLNamespace())) { + $element->setAttribute('xmlns', $ns); } $node = $this->getNode(); if (null === $node) { return $element; } for ($i = 0; $i < $node->childNodes->length; $i++) { - $element->appendChild($element->ownerDocument->importNode($node->childNodes->item($i))); + $element->appendChild($element->ownerDocument->importNode($node->childNodes->item($i), true)); } return $element; } @@ -148,10 +167,7 @@ public function xmlSerialize(\DOMElement $element = null, ?int $libxmlOpts = getNode(); - if (null === $node) { - return null; - } - return $node->ownerDocument->saveXML($node); + return $node?->ownerDocument->saveXML($node); } /** diff --git a/template/types/serialization/xml/serialize/header.php b/template/types/serialization/xml/serialize/header.php index 70a9397c..c95381df 100644 --- a/template/types/serialization/xml/serialize/header.php +++ b/template/types/serialization/xml/serialize/header.php @@ -23,20 +23,27 @@ /** @var \DCarbone\PHPFHIR\Definition\Type $type */ /** @var \DCarbone\PHPFHIR\Definition\Type $parentType */ +$namespace = $config->getNamespace(false); $typeKind = $type->getKind(); $xmlName = NameUtils::getTypeXMLElementName($type); ob_start(); ?> /** - * @param null|\DOMElement $element - * @param null|int $libxmlOpts - * @return \DOMElement + * @param null|\DOMNode $element + * @param null|int|\ $config XML serialization config. Supports an integer value interpreted as libxml opts for backwards compatibility. + * @return \DOMNode * @throws \DOMException */ - public function xmlSerialize(\DOMElement $element = null, ?int $libxmlOpts = getLibxmlOpts()) ? 'null' : $opts; ?>): \DOMElement + public function xmlSerialize(\DOMNode $element = null, null|int| $config = null): \DOMNode { + if (is_int($config)) { + $libxmlOpts = $config; + $config = null; + } else { + $libxmlOpts = $config?->getLibxmlOpts() ?? ::DEFAULT_LIBXML_OPTS; + } if (null === $element) { $dom = new \DOMDocument(); $dom->loadXML($this->_getFHIRXMLElementDefinition(''), $libxmlOpts); diff --git a/template/types/serialization/xml/serialize/resource_container.php b/template/types/serialization/xml/serialize/resource_container.php index 4a34b518..3720a150 100644 --- a/template/types/serialization/xml/serialize/resource_container.php +++ b/template/types/serialization/xml/serialize/resource_container.php @@ -21,16 +21,23 @@ /** @var \DCarbone\PHPFHIR\Config\VersionConfig $config */ /** @var \DCarbone\PHPFHIR\Definition\Type $type */ +$namespace = $config->getNamespace(false); $localProperties = $type->getLocalProperties()->localPropertiesIterator(); ob_start(); ?> /** - * @param null|\DOMElement $element - * @param null|int $libxmlOpts - * @return \DOMElement + * @param null|\DOMNode $element + * @param null|int|\ $config XML serialization config. Supports an integer value interpreted as libxml opts for backwards compatibility. + * @return \DOMNode */ - public function xmlSerialize(null|\DOMElement $element = null, ?int $libxmlOpts = getLibxmlOpts()) ? 'null' : $opts; ?>): \DOMElement + public function xmlSerialize(null|\DOMNode $element = null, null|int| $config = null): \DOMNode { + if (is_int($config)) { + $libxmlOpts = $config; + $config = null; + } else { + $libxmlOpts = $config?->getLibxmlOpts() ?? ::DEFAULT_LIBXML_OPTS; + } if (null !== ($v = $this->getgetGetterName(); ?>())) { return $v->xmlSerialize($element, $libxmlOpts); diff --git a/template/types/serialization/xml/unserialize/header.php b/template/types/serialization/xml/unserialize/header.php index bdbc611c..e44ee6ca 100644 --- a/template/types/serialization/xml/unserialize/header.php +++ b/template/types/serialization/xml/unserialize/header.php @@ -22,50 +22,56 @@ /** @var null|\DCarbone\PHPFHIR\Definition\Type $parentType */ /** @var string $typeClassName */ +$namespace = $config->getNamespace(false); $versionName = $config->getVersion()->getName(); ob_start(); ?> /** - * @param null|string|\DOMElement $element + * @param null|string|\DOMNode $element * @param null|getFullyQualifiedClassName(true); ?> $type - * @param null|int $libxmlOpts + * @param null|int|\ $config XML serialization config. Supports an integer value interpreted as libxml opts for backwards compatibility. * @return null|getFullyQualifiedClassName(true); ?> */ - public static function xmlUnserialize(null|string|\DOMElement $element, null| $type = null, ?int $libxmlOpts = getLibxmlOpts()) ? 'null' : $opts; ?>): null|self + public static function xmlUnserialize(null|string|\DOMNode $element, null| $type = null, null|int| $config = null): null|self { if (null === $element) { return null; } + if (is_int($config)) { + $libxmlOpts = $config; + $config = null; + } else { + $libxmlOpts = $config?->getLibxmlOpts() ?? ::DEFAULT_LIBXML_OPTS; + } if (is_string($element)) { libxml_use_internal_errors(true); $dom = new \DOMDocument(); if (false === $dom->loadXML($element, $libxmlOpts)) { - throw new \DomainException(sprintf('::xmlUnserialize - String provided is not parseable as XML: %s', implode(', ', array_map(function(\libXMLError $err) { return $err->message; }, libxml_get_errors())))); + throw new \DomainException(sprintf( + '%s::xmlUnserialize - String provided is not parseable as XML: %s', + ltrim(substr(__CLASS__, (int)strrpos(__CLASS__, '\\')), '\\'), + implode(', ', array_map(function(\libXMLError $err) { return $err->message; }, libxml_get_errors())) + )); } libxml_use_internal_errors(false); $element = $dom->documentElement; } isAbstract()) : // abstract types may not be instantiated directly ?> if (null === $type) { - throw new \RuntimeException('::xmlUnserialize: Cannot unserialize directly into root type'); - } else if (!($type instanceof )) { - throw new \RuntimeException(sprintf( - '::xmlUnserialize - $type must be child instance of getFullyQualifiedClassName(true); ?> or null, %s seen.', - get_class($type) - )); - } - + throw new \RuntimeException(sprintf('%s::xmlUnserialize: Cannot unserialize directly into root type', static::class)); + } if (null === $type) { - $type = new (null); - } else if (!($type instanceof )) { + $type = new static(null); + } else if (!($type instanceof )) { throw new \RuntimeException(sprintf( - '::xmlUnserialize - $type must be instance of getFullyQualifiedClassName(true); ?> or null, %s seen.', + '%s::xmlUnserialize - $type must be instance of \\%s or null, %s seen.', + ltrim(substr(__CLASS__, (int)strrpos(__CLASS__, '\\')), '\\'), + static::class, get_class($type) )); } - - if ('' === $type->_getFHIRXMLNamespace() && (null === $element->parentNode || $element->namespaceURI !== $element->parentNode->namespaceURI)) { - $type->_setFHIRXMLNamespace($element->namespaceURI); + if ('' === $type->_getFHIRXMLNamespace() && '' !== ($ens = (string)$element->namespaceURI)) { + $type->_setFHIRXMLNamespace($ens); } getKind(); + +$bundleType = $types->getBundleType(); +$bundleEntryProperty = null; + +// we can only perform integration tests on "Resource" types. +if (!$type->isResourceType()) { + return; +} + +// TODO(@dcarbone): find a more efficient way to do this... +if (null === $bundleType) { + throw ExceptionUtils::createBundleTypeNotFoundException($type); +} + +foreach($bundleType->getAllPropertiesIterator() as $prop) { + if ($prop->getName() === 'entry') { + $bundleEntryProperty = $prop; + break; + } +} + +if (null === $bundleEntryProperty) { + throw ExceptionUtils::createBundleEntryPropertyNotFoundException($type); +} -// TODO: precompile list of ID's to test with? +$testNS = $type->getFullyQualifiedTestNamespace(PHPFHIR_TEST_TYPE_INTEGRATION, false); +$testClassname = $type->getTestClassName(); +$typeNS = $type->getFullyQualifiedClassName(false); +$typeClassname = $type->getClassName(); -ob_start(); ?> +ob_start(); + +echo " + +use getFullyQualifiedClassName(false); ?>; +use getFullyQualifiedClassName(false); ?>; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\TestCase; + +/** + * Class + + * @package \ + + */ +class extends TestCase +{ /** @var array */ private array $_fetchedResources = []; @@ -99,7 +148,7 @@ protected function fetchResource(string $format): string $this->assertEmpty($err, sprintf('curl error seen: %s', $err)); $this->assertIsString($res); $this->_fetchedResources[$format] = $res; - $fname = sprintf('%s/getFHIRName(); ?>--source.%s', PHPFHIR_OUTPUT_TMP_DIR, $format); + $fname = sprintf('%s%sgetFHIRName(); ?>--source.%s', PHPFHIR_OUTPUT_TMP_DIR, DIRECTORY_SEPARATOR, $format); file_put_contents($fname, $res); return $res; } @@ -139,7 +188,7 @@ public function testXML(): void $e ); } - $this->assertInstanceOf('getFullyQualifiedClassName(true); ?>', $bundle); + $this->assertInstanceOf(getClassName(); ?>::class, $bundle); $entry = $bundle->getEntry(); isCollection()) : ?> if (0 === count($entry)) { @@ -172,7 +221,7 @@ public function testXML(): void $e ); } - $this->assertInstanceOf('getFullyQualifiedClassName(true); ?>', $type); + $this->assertInstanceOf(getClassName(); ?>::class, $type); $typeElement = $type->xmlSerialize(); $this->assertEquals($resourceXML, $typeElement->ownerDocument->saveXML($typeElement)); $bundleElement = $bundle->xmlSerialize(); @@ -328,7 +377,7 @@ public function testFHIRValidationXML(): void $resource = $entry->getResource(); - $fname = PHPFHIR_OUTPUT_TMP_DIR . '/' . $resource->_getFHIRTypeName() . '-.xml'; + $fname = PHPFHIR_OUTPUT_TMP_DIR . DIRECTORY_SEPARATOR . $resource->_getFHIRTypeName() . '-.xml'; file_put_contents($fname, $bundle->xmlSerialize()->ownerDocument->saveXML()); $this->assertFileExists($fname); @@ -385,7 +434,7 @@ public function testFHIRValidationJSON() $resource = $entry->getResource(); - $fname = PHPFHIR_OUTPUT_TMP_DIR . '/' . $resource->_getFHIRTypeName() . '-.json'; + $fname = PHPFHIR_OUTPUT_TMP_DIR . DIRECTORY_SEPARATOR . $resource->_getFHIRTypeName() . '-.json'; file_put_contents($fname, json_encode($bundle)); $this->assertFileExists($fname); @@ -408,5 +457,6 @@ public function testFHIRValidationJSON() ); } } +} $config, 'type' => $type, @@ -36,7 +36,7 @@ ); echo require_with( - PHPFHIR_TEMPLATE_TESTS_TYPES_DIR . DIRECTORY_SEPARATOR . $testType . DIRECTORY_SEPARATOR . 'body_base.php', + PHPFHIR_TEMPLATE_TYPE_TESTS_DIR . DIRECTORY_SEPARATOR . $testType . DIRECTORY_SEPARATOR . 'body_base.php', [ 'config' => $config, 'type' => $type, @@ -45,7 +45,7 @@ if ($typeKind === TypeKind::PRIMITIVE) : echo require_with( - PHPFHIR_TEMPLATE_TESTS_TYPES_DIR . DIRECTORY_SEPARATOR . $testType . DIRECTORY_SEPARATOR . 'body_primitive.php', + PHPFHIR_TEMPLATE_TYPE_TESTS_DIR . DIRECTORY_SEPARATOR . $testType . DIRECTORY_SEPARATOR . 'body_primitive.php', [ 'config' => $config, 'type' => $type, diff --git a/template/tests/types/unit/header.php b/template/types/tests/unit/header.php similarity index 100% rename from template/tests/types/unit/header.php rename to template/types/tests/unit/header.php diff --git a/template/types/validation/methods.php b/template/types/validation/methods.php index 942ce5cd..0082211f 100644 --- a/template/types/validation/methods.php +++ b/template/types/validation/methods.php @@ -19,6 +19,8 @@ /** @var \DCarbone\PHPFHIR\Config\VersionConfig $config */ /** @var \DCarbone\PHPFHIR\Definition\Type $type */ +use DCarbone\PHPFHIR\Enum\TypeKind; + $typeNameConst = $type->getTypeNameConst(true); $typeKind = $type->getKind(); @@ -54,34 +56,35 @@ public function _getValidationErrors(): array $errs = []; $validationRules = $this->_getValidationRules(); -getLocalProperties()->localPropertiesIterator() as $property) : +getLocalProperties()->localPropertiesIterator() as $property) { $propertyType = $property->getValueFHIRType(); - if (null === $propertyType) : - if ($property->isCollection()) : + if (null === $propertyType) { + if ($property->isCollection()) { echo require_with( PHPFHIR_TEMPLATE_TYPES_VALIDATION_DIR . DIRECTORY_SEPARATOR . 'methods' . DIRECTORY_SEPARATOR . 'collection_typed.php', $requireArgs + ['property' => $property] ); - else : + } else { echo require_with( PHPFHIR_TEMPLATE_TYPES_VALIDATION_DIR . DIRECTORY_SEPARATOR . 'methods' . DIRECTORY_SEPARATOR . 'primitive.php', $requireArgs + ['property' => $property] ); - endif; - else : - if ($property->isCollection()) : - echo require_with( - PHPFHIR_TEMPLATE_TYPES_VALIDATION_DIR . DIRECTORY_SEPARATOR . 'methods' . DIRECTORY_SEPARATOR . 'collection_typed.php', - $requireArgs + ['property' => $property] - ); - else : - echo require_with( - PHPFHIR_TEMPLATE_TYPES_VALIDATION_DIR . DIRECTORY_SEPARATOR . 'methods' . DIRECTORY_SEPARATOR . 'typed.php', - $requireArgs + ['property' => $property] - ); - endif; - endif; -endforeach; + } + } else if ($propertyType->getKind() === TypeKind::PHPFHIR_XHTML) { + // TODO(@dcarbone): better way to omit validation + continue; + } else if ($property->isCollection()) { + echo require_with( + PHPFHIR_TEMPLATE_TYPES_VALIDATION_DIR . DIRECTORY_SEPARATOR . 'methods' . DIRECTORY_SEPARATOR . 'collection_typed.php', + $requireArgs + ['property' => $property] + ); + } else { + echo require_with( + PHPFHIR_TEMPLATE_TYPES_VALIDATION_DIR . DIRECTORY_SEPARATOR . 'methods' . DIRECTORY_SEPARATOR . 'typed.php', + $requireArgs + ['property' => $property] + ); + } +} if (null !== $type->getParentType()) : $ptype = $type; while (null !== $ptype) : diff --git a/template/utilities/class_response_parser.php b/template/utilities/class_response_parser.php deleted file mode 100644 index fc1e623b..00000000 --- a/template/utilities/class_response_parser.php +++ /dev/null @@ -1,264 +0,0 @@ -getNamespace(false); - -ob_start(); - -echo " - -/** - * Class - - * @package \ - - - */ -class - -{ - /** @var \ $config */ - private $config; - - /** - * Constructor - * @param \|null $config - */ - public function __construct(? $config = null) - { - if (null === $config) { - $config = new ; - } - $this->config = $config; - } - - /** - * @return \ - - */ - public function getConfig(): - - { - return $this->config; - } - - /** - * @param array|string|\SimpleXMLElement|\DOMDocument $input - * @return \|null - */ - public function parse($input): ? - - { - $inputType = gettype($input); - if ('NULL' === $inputType) { - return null; - } elseif ('string' === $inputType) { - return $this->parseStringInput($input); - } elseif ('array' === $inputType) { - return $this->parseArrayInput($input); - } elseif ('object' === $inputType) { - return $this->parseObjectInput($input); - } else { - throw new \InvalidArgumentException(sprintf( - '%s::parse - $input must be XML or JSON encoded string, array, or an object of type \\DOMElement or \\SimpleXMLElement, %s seen.', - get_class($this), - $inputType - )); - } - } - - /** - * @param array $input - * @return \|null - */ - protected function parseArrayInput(array $input): ? - - { - if ([] === $input) { - return null; - } - if (isset($input[::JSON_FIELD_RESOURCE_TYPE])) { - $className = ::getTypeClass($input[::JSON_FIELD_RESOURCE_TYPE]); - if (null === $className) { - throw new \UnexpectedValueException(sprintf( - 'Provided input has "%s" value of "%s", but it does not map to any known type. Other keys: ["%s"]', - ::JSON_FIELD_RESOURCE_TYPE, - $input[::JSON_FIELD_RESOURCE_TYPE], - implode('","', array_keys($input)) - )); - } - return new $className($input); - } - throw new \DomainException(sprintf( - 'Unable to determine FHIR Type from provided array: missing "%s" key. Available keys: ["%s"]', - ::JSON_FIELD_RESOURCE_TYPE, - implode('","', array_keys($input)) - )); - } - - /** - * @param \stdClass $input - * @return \|null - */ - protected function parseObjectStdClassInput(\stdClass $input): ? - - { - return self::parseArrayInput((array)$input); - } - - /** - * @param \SimpleXMLElement $input - * @return \|null - */ - protected function parseObjectSimpleXMLElementInput(\SimpleXMLElement $input): ? - - { - return self::parseObjectDOMDocumentInput(dom_import_simplexml($input)); - } - - /** - * @param \DOMDocument $input - * @return \|null - */ - protected function parseObjectDOMDocumentInput(\DOMDocument $input): ? - - { - $elementName = $input->documentElement->nodeName; - $className = ::getTypeClass($elementName); - if (null === $className) { - throw new \UnexpectedValueException(sprintf( - 'Unable to locate class for root XML element "%s". Input seen: %s', - $elementName, - $this->getPrintableStringInput($input->saveXML()) - )); - } - return $className::xmlUnserialize($input->documentElement); - } - - /** - * @param object $input - * @return \|null - */ - protected function parseObjectInput(object $input): ? - - { - if ($input instanceof ) { - return $input; - } elseif ($input instanceof \stdClass) { - return $this->parseObjectStdClassInput($input); - } elseif ($input instanceof \SimpleXMLElement) { - return $this->parseObjectSimpleXMLElementInput($input); - } elseif ($input instanceof \DOMDocument) { - return $this->parseObjectDOMDocumentInput($input); - } - throw new \UnexpectedValueException(sprintf( - 'Unable parse provided input object of type "%s"', - get_class($input) - )); - } - - /** - * @param string $input - * @param null|int $libxmlOpts - * @return \|null - */ - protected function parseStringXMLInput(string $input, ?int $libxmlOpts = getLibxmlOpts()) ? 'null' : $opts; ?>): ? - - { - libxml_use_internal_errors(true); - $dom = new \DOMDocument(); - $dom->loadXML($input, $libxmlOpts); - $err = libxml_get_last_error(); - libxml_use_internal_errors(false); - if (false === $err) { - return $this->parseObjectDOMDocumentInput($dom); - } - throw new \DomainException(sprintf( - 'Unable to parse provided input as XML. Error: %s; Input: %s', - $err ? $err->message : 'Unknown', - $this->getPrintableStringInput($input) - )); - } - - /** - * @param string $input - * @return \|null - */ - protected function parseStringJSONInput(string $input): ? - - { - $decoded = json_decode($input, true); - $err = json_last_error(); - if (JSON_ERROR_NONE !== $err) { - throw new \DomainException(sprintf( - 'Unable to parse provided input as JSON. Error: %s; Input: %s', - json_last_error_msg(), - $this->getPrintableStringInput($input) - )); - } - - return $this->parseArrayInput($decoded); - } - - /** - * @param string $input - * @return \|null - */ - protected function parseStringInput(string $input): ? - - { - $input = trim($input); - if ('' === $input) { - return null; - } - $chr = $input[0]; - if ('<' === $chr) { - return $this->parseStringXMLInput($input); - } elseif ('{' === $chr) { - return $this->parseStringJSONInput($input); - } else { - throw new \UnexpectedValueException(sprintf( - 'Input string must be either XML or JSON encoded object. Provided: %s', - $this->getPrintableStringInput($input) - )); - } - } - - /** - * @param string $input - * @return string - */ - protected function getPrintableStringInput(string $input): string - { - return strlen($input) > 100 ? substr($input, 0, 100) . '[...]' : $input; - } -} -