1
0
Fork 0
mirror of synced 2025-04-07 19:22:00 +00:00

Compare commits

..

No commits in common. "master" and "v6.9.1" have entirely different histories.

184 changed files with 5798 additions and 8948 deletions

View file

@ -14,33 +14,27 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
php-version: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] php-version: ['7.3', '7.4', '8.0', '8.1']
steps: steps:
- name: Check out code into the workspace - name: Check out code into the workspace
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Setup PHP ${{ matrix.php-version }} - name: Setup PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: ${{ matrix.php-version }} php-version: ${{ matrix.php-version }}
coverage: pcov coverage: pcov
- name: Composer cache - name: Composer cache
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ${{ env.HOME }}/.composer/cache path: ${{ env.HOME }}/.composer/cache
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
- name: Install dependencies - name: Install dependencies
run: composer install -o run: composer install -o
- name: Configure matchers - name: Configure matchers
uses: mheap/phpunit-matcher-action@v1 uses: mheap/phpunit-matcher-action@v1
- name: Run tests - name: Run tests
run: composer run-script phpunit-ci run: composer run-script phpunit-ci
- name: Coverage - name: Coverage
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v2
with: with:
verbose: true verbose: true

View file

@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out code into the workspace - name: Check out code into the workspace
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Run PHPCS - name: Run PHPCS
uses: chekalsky/phpcs-action@v1 uses: chekalsky/phpcs-action@v1
phpmd: phpmd:
@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out code into the workspace - name: Check out code into the workspace
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Run PHPMD - name: Run PHPMD
uses: GeneaLabs/action-reviewdog-phpmd@1.0.0 uses: GeneaLabs/action-reviewdog-phpmd@1.0.0
with: with:
@ -32,11 +32,11 @@ jobs:
target_directory: 'src' target_directory: 'src'
phpstan: phpstan:
name: PHPStan name: PHPStan
runs-on: ubuntu-latest runs-on: ubuntu-18.04
steps: steps:
- name: Check out code into the workspace - name: Check out code into the workspace
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Run PHPStan - name: Run PHPStan
uses: docker://oskarstark/phpstan-ga:1.8.0 uses: docker://oskarstark/phpstan-ga:1.0.1
with: with:
args: analyse src -c phpstan.neon --memory-limit=1G --no-progress args: analyse src -c phpstan.neon --memory-limit=1G --no-progress

View file

@ -8,7 +8,7 @@ on:
- 'v*' - 'v*'
jobs: jobs:
documentation: test:
name: "phpDocumentor" name: "phpDocumentor"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -19,21 +19,21 @@ jobs:
pages_threshold: major_outage pages_threshold: major_outage
- name: Check out code into the workspace - name: Check out code into the workspace
if: success() && ${{ github.ref != 'refs/heads/master' }} if: success() && ${{ github.ref != 'refs/heads/master' }}
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Setup PHP 8.3 - name: Setup PHP 7.4
if: ${{ github.ref != 'refs/heads/master' }} if: ${{ github.ref != 'refs/heads/master' }}
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: "8.3" php-version: "7.4"
- name: Cache phpDocumentor - name: Cache phpDocumentor
id: cache-phpdocumentor id: cache-phpdocumentor
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: phpDocumentor.phar path: phpDocumentor.phar
key: phpdocumentor key: phpdocumentor
- name: Download latest phpDocumentor - name: Download latest phpDocumentor
if: steps.cache-phpdocumentor.outputs.cache-hit != 'true' if: steps.cache-phpdocumentor.outputs.cache-hit != 'true'
run: curl -O -L https://github.com/phpDocumentor/phpDocumentor/releases/download/v3.4.3/phpDocumentor.phar run: curl -O -L https://phpdoc.org/phpDocumentor.phar
- name: Generate documentation - name: Generate documentation
if: ${{ github.ref != 'refs/heads/master' }} if: ${{ github.ref != 'refs/heads/master' }}
run: php phpDocumentor.phar run: php phpDocumentor.phar

View file

@ -37,27 +37,31 @@ Follow those steps to install the library:
```bash ```bash
composer require retailcrm/api-client-php:"~6.0" composer require retailcrm/api-client-php:"~6.0"
``` ```
During the installation you will see this message. Press `'y'` when you do: During the installation, you'll see a message which will look like this:
```sh
civicrm/composer-compile-plugin contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins
Do you trust "civicrm/composer-compile-plugin" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?]
```
After that, you may see a message which will look like this:
```sh ```sh
The following packages have new compilation tasks: The following packages have new compilation tasks:
- retailcrm/api-client-php has 1 task - retailcrm/api-client-php has 1 task
Allow these packages to compile? ([y]es, [a]lways, [n]o, [l]ist, [h]elp) Allow these packages to compile? ([y]es, [a]lways, [n]o, [l]ist, [h]elp)
``` ```
That's because the Client uses code generation to speed up serialization and deserialization of models in production. This code should be generated during installation or update. Without that code, the library itself will not work at all.
Choose `[a]lways` by typing `a` and pressing Enter. Choose `[a]lways` by typing `a` and pressing Enter if you don't want to see this message anymore. If you want to approve the compilation task every time - use `[y]es` option. The DTO cache will be generated after that.
**Note:** You should choose `'y'` and `[a]lways` if your application is using CI/CD pipeline because the interactive terminal is not available **Note:** You should choose `[a]lways` if your application is using CI/CD pipeline because the interactive terminal is not available
in that environment which will result in failure during the dependencies installation. in that environment which will result in failure during the dependencies installation.
If you chose something else during the installation and API client doesn't work properly - please follow [these instructions](doc/compilation_prompt.md#ive-chosen-something-else-now-api-client-doesnt-work) to fix the problem. If you skipped the compilation task - don't worry, it can be executed manually at any time with this command:
```sh
composer compile --all
```
3. Include the autoloader if it's not included, or you didn't use Composer before. 3. **Optional.** Disable compilation prompt that you have seen in the previous step.
If you wish to disable the compilation prompt but didn't do that at the previous step - you can disable the prompt manually.
Read the [documentation](doc/compilation_prompt.md) to learn how to do that.
4. Include the autoloader if it's not included, or you didn't use Composer before.
```php ```php
require 'path/to/vendor/autoload.php'; require 'path/to/vendor/autoload.php';
``` ```

View file

@ -16,33 +16,33 @@
} }
], ],
"require": { "require": {
"php": ">=7.3", "php": ">=7.3.0",
"ext-json": "*", "ext-json": "*",
"psr/log": "^1|^2|^3", "psr/log": "^1|^2|^3",
"psr/http-client": "^1.0", "psr/http-client": "^1.0",
"psr/http-message": "^1.0 || ^2.0", "psr/http-message": "^1.0",
"psr/http-message-implementation": "^1.0", "psr/http-message-implementation": "^1.0",
"php-http/client-implementation": "^1.0", "php-http/client-implementation": "^1.0",
"php-http/message-factory": "^1.0",
"php-http/discovery": "^1.13", "php-http/discovery": "^1.13",
"doctrine/annotations": "^1.13|^2.0", "doctrine/annotations": "^1.13",
"liip/serializer": "2.2.* || 2.6.*", "liip/serializer": "2.0.*",
"php-http/httplug": "^2.2", "php-http/httplug": "^2.2",
"civicrm/composer-compile-plugin": "^0.20", "civicrm/composer-compile-plugin": "^0.18.0",
"symfony/console": "^4.0|^5.0|^6.0|^7.0", "symfony/console": "^4.0|^5.0|^6.0",
"psr/event-dispatcher": "^1.0", "psr/event-dispatcher": "^1.0",
"neur0toxine/psr.http-client-implementation.php-http-curl": "*", "neur0toxine/psr.http-client-implementation.php-http-curl": "*",
"neur0toxine/psr.http-factory-implementation.nyholm": "*", "neur0toxine/psr.http-factory-implementation.nyholm": "*",
"neur0toxine/psr.http-message-implementation.nyholm": "*", "neur0toxine/psr.http-message-implementation.nyholm": "*",
"psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/cache": "^1.0 || ^2.0 || ^3.0",
"symfony/cache": ">=v3.1.0", "symfony/cache": ">=v3.1.0"
"psr/http-factory": "^1.1"
}, },
"require-dev": { "require-dev": {
"squizlabs/php_codesniffer": "^3.5", "squizlabs/php_codesniffer": "^3.5",
"phpmd/phpmd": "^2.10", "phpmd/phpmd": "^2.10",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.1", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1",
"phpcompatibility/php-compatibility": "^9.3", "phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "1.9.14", "phpstan/phpstan": "1.0.1",
"vlucas/phpdotenv": "^5.3", "vlucas/phpdotenv": "^5.3",
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.5",
"php-http/curl-client": "^2.2", "php-http/curl-client": "^2.2",
@ -67,7 +67,7 @@
"phpunit": "./vendor/bin/phpunit -c phpunit.xml.dist --coverage-text", "phpunit": "./vendor/bin/phpunit -c phpunit.xml.dist --coverage-text",
"phpunit-ci": "@php -dpcov.enabled=1 -dpcov.directory=. -dpcov.exclude=\"~vendor~\" ./vendor/bin/phpunit --teamcity -c phpunit.xml.dist", "phpunit-ci": "@php -dpcov.enabled=1 -dpcov.directory=. -dpcov.exclude=\"~vendor~\" ./vendor/bin/phpunit --teamcity -c phpunit.xml.dist",
"phpmd": "./vendor/bin/phpmd src text ./phpmd.xml", "phpmd": "./vendor/bin/phpmd src text ./phpmd.xml",
"phpcs": "./vendor/bin/phpcs -p src --runtime-set testVersion 7.3-8.3 && ./vendor/bin/phpcs -p tests --runtime-set testVersion 7.3-8.3 --warning-severity=0", "phpcs": "./vendor/bin/phpcs -p src --runtime-set testVersion 7.3-8.0 && ./vendor/bin/phpcs -p tests --runtime-set testVersion 7.3-8.0 --warning-severity=0",
"phpstan": "./vendor/bin/phpstan analyse -c phpstan.neon src --memory-limit=-1", "phpstan": "./vendor/bin/phpstan analyse -c phpstan.neon src --memory-limit=-1",
"phpstan-dockerized-ci": "docker run --rm -it -w=/app -v ${PWD}:/app oskarstark/phpstan-ga:1.0.1 analyse src -c phpstan.neon --memory-limit=1G --no-progress", "phpstan-dockerized-ci": "docker run --rm -it -w=/app -v ${PWD}:/app oskarstark/phpstan-ga:1.0.1 analyse src -c phpstan.neon --memory-limit=1G --no-progress",
"lint:fix": "./vendor/bin/phpcbf src", "lint:fix": "./vendor/bin/phpcbf src",
@ -114,8 +114,7 @@
"process-timeout": 600, "process-timeout": 600,
"allow-plugins": { "allow-plugins": {
"civicrm/composer-compile-plugin": true, "civicrm/composer-compile-plugin": true,
"dealerdirect/phpcodesniffer-composer-installer": true, "dealerdirect/phpcodesniffer-composer-installer": true
"php-http/discovery": true
} }
} }
} }

View file

@ -1,12 +1,6 @@
## Dealing with `civicrm/composer-compile-plugin` prompts ## Compilation prompt
During installation you will see this prompt: After almost every Composer operation you will see this prompt:
```sh
civicrm/composer-compile-plugin contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins
Do you trust "civicrm/composer-compile-plugin" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?]
```
And after almost any Composer operation you will see this prompt:
```sh ```sh
The following packages have new compilation tasks: The following packages have new compilation tasks:
- retailcrm/api-client-php has 1 task - retailcrm/api-client-php has 1 task
@ -15,7 +9,7 @@ Allow these packages to compile? ([y]es, [a]lways, [n]o, [l]ist, [h]elp)
``` ```
That's because the API client utilizes code generation to speed up the serialization and deserialization of the requests. However, That's because the API client utilizes code generation to speed up the serialization and deserialization of the requests. However,
these prompts may be annoying and sometimes can even break the application lifecycle pipeline (in the CI/CD environment). We can't just this prompt may be annoying and sometimes can even break the application lifecycle pipeline (in the CI/CD environment). We can't just
disable it for everyone [because of security concerns](https://github.com/composer/composer/issues/1193). But you can disable it for your project. disable it for everyone [because of security concerns](https://github.com/composer/composer/issues/1193). But you can disable it for your project.
There are three ways of disabling this prompt: There are three ways of disabling this prompt:
@ -23,65 +17,54 @@ There are three ways of disabling this prompt:
2. Automated way. 2. Automated way.
3. Manual way. 3. Manual way.
### Disable compilation prompts during the installation ### Disable compilation prompt during the installation
Press `'y'` when you see this message: Just choose `[a]lways` option by pressing `a` followed by Enter. This will automatically edit your `composer.json` and will disable
```sh the compilation prompt for you. No additional steps are needed.
civicrm/composer-compile-plugin contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins
Do you trust "civicrm/composer-compile-plugin" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?]
```
And when you see this prompt, press `'a'`: ### Disable or enable compilation prompt via CLI
```sh
The following packages have new compilation tasks:
- retailcrm/api-client-php has 1 task
Allow these packages to compile? ([y]es, [a]lways, [n]o, [l]ist, [h]elp) Alternatively, you can use `retailcrm-client` CLI utility. It will be in your binary directory. By default, it'll be in the
``` `vendor/bin` directory if not defined otherwise in the composer.json `config.bin-dir` entry.
That's it. Code generation is now enabled. The only benefit of this utility is the fact that it also can enable the prompt again.
### I've chosen something else, now API client doesn't work! #### Disabling compilation prompt
You can disable the compiler prompt by running this command:
That happens. We provide special CLI utility which will automatically configure your `composer.json` to enable code generation.
Just run this command inside your project after API client installation:
```sh ```sh
./vendor/bin/retailcrm-client compiler:prompt ./vendor/bin/retailcrm-client compiler:prompt
``` ```
You should see this message after running the command: Replace `vendor/bin` with your bin directory path if it's different from the default.
```sh
✓ Done, code generation has been enabled.
```
You may also want to run code generation manually once. It can be achieved by running this command: You should see this message after that:
```sh ```sh
composer compile --all ✓ Done, generator prompt is now enabled.
``` ```
**Note:** `retailcrm-client` should be in your binary directory. By default it is set to `vendor/bin`. You can check `config.bin-dir` #### Enabling compilation prompt
value in your `composer.json` and update paths in the commands above accordingly.
**Note (2):** `compiler:prompt` command has `--revert` flag. You can use it if you want to disable automatic code generation for some reason.
### Disabling compilation prompts manually If you want to revert this change and enable the compilation prompt then just run this command again with the `--activate` flag:
It is possible to replicate the same actions manually. First, you will need to enable compiler plugin. Add the plugin ```sh
to the `config.allow-plugins` segment of your `composer.json` file: ./vendor/bin/retailcrm-client compiler:prompt --activate
```json
"allow-plugins": {
"civicrm/composer-compile-plugin": true
}
``` ```
After that add these params into the `extra` segment of your `composer.json`: ### Disable or enable compilation prompt manually
#### Enabling compilation prompt
It is possible to replicate the same actions manually. Add these params into the `extra` segment of your `composer.json` if
you want to execute code generation automatically after library installation or update.
```json ```json
"compile-mode": "whitelist", "compile-mode": "whitelist",
"compile-whitelist": ["retailcrm/api-client-php"] "compile-whitelist": ["retailcrm/api-client-php"]
``` ```
Your `composer.json` file should look like this: Your `composer.json` file will look like this:
```json ```json
{ {
"name": "author/some-project", "name": "author/some-project",
@ -94,11 +77,6 @@ Your `composer.json` file should look like this:
"nyholm/psr7": "^1.4", "nyholm/psr7": "^1.4",
"retailcrm/api-client-php": "~6.0" "retailcrm/api-client-php": "~6.0"
}, },
"config": {
"allow-plugins": {
"civicrm/composer-compile-plugin": true
}
},
"extra": { "extra": {
"compile-mode": "whitelist", "compile-mode": "whitelist",
"compile-whitelist": ["retailcrm/api-client-php"] "compile-whitelist": ["retailcrm/api-client-php"]
@ -107,3 +85,7 @@ Your `composer.json` file should look like this:
``` ```
Voilà! You won't see the annoying prompt again. Voilà! You won't see the annoying prompt again.
#### Enabling compilation prompt
Just remove `extra.compile-mode` and `extra.compile-whitelist` params from your `composer.json`.

View file

@ -1,9 +1,8 @@
# Documentation # Documentation
* [Dealing with `civicrm/composer-compile-plugin` prompts](compilation_prompt.md) * [Compilation prompt](compilation_prompt.md)
+ [Disable compilation prompts during the installation](compilation_prompt.md#disable-compilation-prompts-during-the-installation) + [Disable or enable compilation prompt via CLI](compilation_prompt.md#disable-or-enable-compilation-prompt-via-cli)
+ [I've chosen something else, now API client doesn't work!](compilation_prompt.md#ive-chosen-something-else-now-api-client-doesnt-work) + [Disable or enable compilation prompt manually](compilation_prompt.md#disable-or-enable-compilation-prompt-manually)
+ [Disabling compilation prompts manually](compilation_prompt.md#disabling-compilation-prompts-manually)
* [Client structure](structure.md) * [Client structure](structure.md)
+ [Design principles](structure.md#design-principles) + [Design principles](structure.md#design-principles)
+ [Resource groups](structure.md#resource-groups) + [Resource groups](structure.md#resource-groups)

View file

@ -24,11 +24,9 @@ The resource groups list into which client is separated:
* `customers` * `customers`
* `customersCorporate` * `customersCorporate`
* `delivery` * `delivery`
* `features`
* `files` * `files`
* `integration` * `integration`
* `loyalty` * `loyalty`
* `notifications`
* `orders` * `orders`
* `packs` * `packs`
* `payments` * `payments`
@ -41,8 +39,7 @@ The resource groups list into which client is separated:
* `users` * `users`
* `verification` * `verification`
* `statistics` * `statistics`
* `webAnalytics`
There is also a special `customMethods` group that is used for custom API methods. Each group except this one implements corresponding API documentation block: Every group implements corresponding API documentation block:
* [English](https://docs.retailcrm.pro/Developers/API/APIVersions/APIv5) * [English](https://docs.retailcrm.pro/Developers/API/APIVersions/APIv5)
* [Русский](https://docs.retailcrm.ru/Developers/API/APIVersions/APIv5) * [Русский](https://docs.retailcrm.ru/Developers/API/APIVersions/APIv5)

View file

@ -10,8 +10,4 @@
<file>src/</file> <file>src/</file>
<file>tests/</file> <file>tests/</file>
<exclude-pattern>src/Component/Serializer/Generator/*</exclude-pattern>
<exclude-pattern>src/Component/Serializer/Parser/*</exclude-pattern>
<exclude-pattern>src/Component/Serializer/ArraySupportDecorator.php</exclude-pattern>
</ruleset> </ruleset>

View file

@ -44,6 +44,4 @@
</rule> </rule>
<exclude-pattern>tests/*</exclude-pattern> <exclude-pattern>tests/*</exclude-pattern>
<exclude-pattern>src/Component/Serializer/Generator/*</exclude-pattern>
<exclude-pattern>src/Component/Serializer/Parser/*</exclude-pattern>
</ruleset> </ruleset>

View file

@ -1,246 +0,0 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$config of static method Liip\\\\Serializer\\\\Configuration\\\\GeneratorConfiguration\\:\\:createFomArray\\(\\) expects array\\{default_group_combinations\\?\\: array\\<int, array\\<int, string\\>\\>\\|null, default_versions\\?\\: array\\<int, string\\>\\|null, classes\\?\\: array\\<class\\-string, array\\<string, mixed\\>\\>\\|null, options\\?\\: array\\<string, mixed\\>\\}, array\\{default_group_combinations\\: array\\{\\}, default_versions\\: array\\{\\}, classes\\: non\\-empty\\-array\\<string, array\\{\\}\\>\\} given\\.$#"
count: 1
path: src/Component/ModelsGenerator.php
-
message: "#^Parameter \\#3 \\$classesToGenerate of class RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Generator\\\\DeserializerGenerator constructor expects array\\<int, class\\-string\\>, array\\<string\\> given\\.$#"
count: 1
path: src/Component/ModelsGenerator.php
-
message: "#^Parameter \\#2 \\$method of method Liip\\\\Serializer\\\\Template\\\\Deserialization\\:\\:renderSetter\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/Component/Serializer/Generator/DeserializerGenerator.php
-
message: "#^Parameter \\#4 \\$stack of method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Generator\\\\DeserializerGenerator\\:\\:generateCodeForClass\\(\\) expects array\\<string, int\\<1, max\\>\\>, array given\\.$#"
count: 2
path: src/Component/Serializer/Generator/DeserializerGenerator.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Generator\\\\SerializerGenerator\\:\\:buildSerializerFunctionName\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/Component/Serializer/Generator/SerializerGenerator.php
-
message: "#^Parameter \\#2 \\$method of method Liip\\\\Serializer\\\\Template\\\\Serialization\\:\\:renderGetter\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/Component/Serializer/Generator/SerializerGenerator.php
-
message: "#^Parameter \\#3 \\$serializerGroups of method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Generator\\\\SerializerGenerator\\:\\:generateCodeForClass\\(\\) expects array\\<int, string\\>, array given\\.$#"
count: 4
path: src/Component/Serializer/Generator/SerializerGenerator.php
-
message: "#^Class RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Lexer extends generic class Doctrine\\\\Common\\\\Lexer\\\\AbstractLexer but does not specify its types\\: T, V$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Lexer.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Lexer\\:\\:getType\\(\\) has parameter \\$value with no type specified\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Lexer.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Lexer\\:\\:parse\\(\\) has no return type specified\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Lexer.php
-
message: "#^Parameter \\#1 \\$haystack of function stripos expects string, float\\|int\\|string given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Lexer.php
-
message: "#^Parameter \\#1 \\$haystack of function strpos expects string, float\\|int\\|string given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Lexer.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:getConstant\\(\\) should return string but returns string\\|false\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:parse\\(\\) should return array but returns mixed\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:visitArrayType\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:visitCompoundType\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:visitSimpleType\\(\\) never returns string so it can be removed from the return type\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\ParserInterface\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/ParserInterface.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:gatherClassAnnotations\\(\\) has parameter \\$reflectionClass with generic class ReflectionClass but does not specify its types\\: T$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:getMethodName\\(\\) has parameter \\$annotations with no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:getProperty\\(\\) has parameter \\$annotations with no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:getReturnType\\(\\) has parameter \\$reflClass with generic class ReflectionClass but does not specify its types\\: T$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:getSerializedName\\(\\) has parameter \\$annotations with no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:isPostDeserializeMethod\\(\\) has parameter \\$annotations with no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:isVirtualProperty\\(\\) has parameter \\$annotations with no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:parseClass\\(\\) has parameter \\$reflClass with generic class ReflectionClass but does not specify its types\\: T$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:parseMethods\\(\\) has parameter \\$reflClass with generic class ReflectionClass but does not specify its types\\: T$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:parseProperties\\(\\) has parameter \\$reflClass with generic class ReflectionClass but does not specify its types\\: T$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSParser\\:\\:parsePropertyAnnotations\\(\\) has parameter \\$annotations with no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Parameter \\#1 \\$objectOrClass of class ReflectionClass constructor expects class\\-string\\<T of object\\>\\|T of object, string given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Class Doctrine\\\\Common\\\\Collections\\\\ArrayCollection not found\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Class Doctrine\\\\Common\\\\Collections\\\\Collection not found\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSTypeParser\\:\\:parseType\\(\\) has parameter \\$typeInfo with no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#4 \\$traversableClass of class Liip\\\\MetadataParser\\\\Metadata\\\\PropertyTypeIterable constructor expects class\\-string\\<Traversable\\>\\|null, string\\|null given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Cannot access property \\$value on RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Token\\|null\\.$#"
count: 2
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Parameter \\#1 \\$string of function strlen expects string, int\\|string given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Parameter \\#1 \\$value of method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:getConstant\\(\\) expects int, int\\<min, 0\\>\\|int\\<4, 8\\>\\|int\\<11, max\\>\\|string\\|null given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Parameter \\#1 \\$value of method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:getConstant\\(\\) expects int, int\\|string\\|null given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Property RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Parser\\:\\:\\$token with generic class RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Token does not specify its types\\: T, V$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Parser.php
-
message: "#^Access to an undefined property object\\:\\:\\$position\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Token.php
-
message: "#^Access to an undefined property object\\:\\:\\$type\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Token.php
-
message: "#^Access to an undefined property object\\:\\:\\$value\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Token.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Token\\:\\:fromArray\\(\\) has parameter \\$source with no value type specified in iterable type array\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Token.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Token\\:\\:fromArray\\(\\) return type with generic class RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Token does not specify its types\\: T, V$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Token.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Token\\:\\:fromObject\\(\\) return type with generic class RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Token does not specify its types\\: T, V$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Token.php
-
message: "#^Property RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSCore\\\\Type\\\\Token\\<T of int\\|string,V of int\\|string\\>\\:\\:\\$type \\(\\(T of int\\|string\\)\\|null\\) does not accept int\\|string\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSCore/Type/Token.php
-
message: "#^Cannot call method getParameters\\(\\) on ReflectionMethod\\|null\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Property RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSTypeParser\\:\\:\\$useArrayDateFormat has no type specified\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php

View file

@ -1,6 +1,4 @@
parameters: parameters:
excludePaths:
- src/Component/Serializer/ArraySupportDecorator.php
ignoreErrors: ignoreErrors:
- -
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Builder\\\\ClientBuilder\\:\\:buildHandlersChain\\(\\) through static\\:\\:\\.$#" message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Builder\\\\ClientBuilder\\:\\:buildHandlersChain\\(\\) through static\\:\\:\\.$#"
@ -18,37 +16,37 @@ parameters:
path: src/Client.php path: src/Client.php
- -
message: "#^Cannot assign new offset to array\\<string\\>\\|string\\.$#" message: "#^Cannot access offset 'compile\\-mode' on mixed\\.$#"
count: 2
path: src/Command/CompilerPromptCommand.php
-
message: "#^Cannot access offset 'compile\\-whitelist' on mixed\\.$#"
count: 2
path: src/Command/CompilerPromptCommand.php
-
message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#"
count: 1 count: 1
path: src/Command/CompilerPromptCommand.php path: src/Command/CompilerPromptCommand.php
- -
message: "#^Parameter \\#1 \\$array of function array_filter expects array, string given\\.$#" message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#"
count: 1
path: src/Command/CompilerPromptCommand.php
-
message: "#^Parameter \\#2 \\$haystack of function in_array expects array, array\\<string\\>\\|string given\\.$#"
count: 1
path: src/Command/CompilerPromptCommand.php
-
message: "#^Unsafe access to private constant RetailCrm\\\\Api\\\\Command\\\\CompilerPromptCommand\\:\\:COMPILER_PLUGIN through static\\:\\:\\.$#"
count: 3 count: 3
path: src/Command/CompilerPromptCommand.php path: src/Command/CompilerPromptCommand.php
- -
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Command\\\\CompilerPromptCommand\\:\\:activateAutoCompiler\\(\\) through static\\:\\:\\.$#" message: "#^Unsafe access to private constant RetailCrm\\\\Api\\\\Command\\\\CompilerPromptCommand\\:\\:PACKAGE_NAME through static\\:\\:\\.$#"
count: 5
path: src/Command/CompilerPromptCommand.php
-
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Command\\\\CompilerPromptCommand\\:\\:activatePrompt\\(\\) through static\\:\\:\\.$#"
count: 1 count: 1
path: src/Command/CompilerPromptCommand.php path: src/Command/CompilerPromptCommand.php
- -
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Command\\\\CompilerPromptCommand\\:\\:activatePlugin\\(\\) through static\\:\\:\\.$#" message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Command\\\\CompilerPromptCommand\\:\\:deactivatePrompt\\(\\) through static\\:\\:\\.$#"
count: 1
path: src/Command/CompilerPromptCommand.php
-
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Command\\\\CompilerPromptCommand\\:\\:deactivateAutoCompiler\\(\\) through static\\:\\:\\.$#"
count: 1 count: 1
path: src/Command/CompilerPromptCommand.php path: src/Command/CompilerPromptCommand.php
@ -97,6 +95,11 @@ parameters:
count: 1 count: 1
path: src/Component/FormData/FormEncoder.php path: src/Component/FormData/FormEncoder.php
-
message: "#^Parameter \\#2 \\$result of method RetailCrm\\\\Api\\\\Component\\\\FormData\\\\FormEncoder\\:\\:processPostSerialize\\(\\) expects array, array given\\.$#"
count: 1
path: src/Component/FormData/FormEncoder.php
- -
message: "#^Parameter \\#1 \\$object of function get_class expects object, mixed given\\.$#" message: "#^Parameter \\#1 \\$object of function get_class expects object, mixed given\\.$#"
count: 1 count: 1
@ -114,7 +117,7 @@ parameters:
- -
message: "#^Cannot cast mixed to float\\.$#" message: "#^Cannot cast mixed to float\\.$#"
count: 1 count: 2
path: src/Component/FormData/Strategy/Encode/SimpleTypeStrategy.php path: src/Component/FormData/Strategy/Encode/SimpleTypeStrategy.php
- -
@ -122,6 +125,11 @@ parameters:
count: 1 count: 1
path: src/Component/FormData/Strategy/Encode/SimpleTypeStrategy.php path: src/Component/FormData/Strategy/Encode/SimpleTypeStrategy.php
-
message: "#^Cannot cast mixed to string\\.$#"
count: 1
path: src/Component/FormData/Strategy/Encode/SimpleTypeStrategy.php
- -
message: "#^Unsafe access to private property RetailCrm\\\\Api\\\\Component\\\\FormData\\\\Strategy\\\\Encode\\\\TypedArrayStrategy\\:\\:\\$innerTypesMatcher through static\\:\\:\\.$#" message: "#^Unsafe access to private property RetailCrm\\\\Api\\\\Component\\\\FormData\\\\Strategy\\\\Encode\\\\TypedArrayStrategy\\:\\:\\$innerTypesMatcher through static\\:\\:\\.$#"
count: 1 count: 1
@ -172,11 +180,6 @@ parameters:
count: 1 count: 1
path: src/Component/ModelsGenerator.php path: src/Component/ModelsGenerator.php
-
message: "#^Property RetailCrm\\\\Api\\\\Component\\\\PhpFilesIterator\\:\\:\\$parent \\(Iterator\\<int\\|string, array\\|string\\>\\) does not accept RegexIterator\\<mixed, mixed, Traversable\\<TKey, TValue\\>\\>\\.$#"
count: 1
path: src/Component/PhpFilesIterator.php
- -
message: "#^Property RetailCrm\\\\Api\\\\Component\\\\PhpFilesIterator\\:\\:\\$parent type has no value type specified in iterable type array\\.$#" message: "#^Property RetailCrm\\\\Api\\\\Component\\\\PhpFilesIterator\\:\\:\\$parent type has no value type specified in iterable type array\\.$#"
count: 1 count: 1
@ -193,12 +196,12 @@ parameters:
path: src/Component/Serializer/ArraySupportDecorator.php path: src/Component/Serializer/ArraySupportDecorator.php
- -
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\ArraySupportDecorator\\:\\:getArrayValueType\\(\\) should return string but returns array\\<string\\>\\.$#" message: "#^Parameter \\#1 \\$data of method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\ArraySupportDecorator\\:\\:decodeArray\\(\\) expects array, mixed given\\.$#"
count: 2 count: 1
path: src/Component/Serializer/ArraySupportDecorator.php path: src/Component/Serializer/ArraySupportDecorator.php
- -
message: "#^Parameter \\#1 \\$data of method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\ArraySupportDecorator\\:\\:decodeArray\\(\\) expects array, mixed given\\.$#" message: "#^Parameter \\#1 \\$data of method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\ArraySupportDecorator\\:\\:encodeArray\\(\\) expects array, mixed given\\.$#"
count: 1 count: 1
path: src/Component/Serializer/ArraySupportDecorator.php path: src/Component/Serializer/ArraySupportDecorator.php
@ -212,16 +215,61 @@ parameters:
count: 2 count: 2
path: src/Component/Serializer/ArraySupportDecorator.php path: src/Component/Serializer/ArraySupportDecorator.php
-
message: "#^Unsafe access to private constant RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Generator\\\\DeserializerGenerator\\:\\:FILENAME_PREFIX through static\\:\\:\\.$#"
count: 1
path: src/Component/Serializer/Generator/DeserializerGenerator.php
-
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
count: 1
path: src/Component/Serializer/Generator/SerializerGenerator.php
-
message: "#^Cannot call method getClassName\\(\\) on mixed\\.$#"
count: 1
path: src/Component/Serializer/Generator/SerializerGenerator.php
-
message: "#^Cannot call method getGroups\\(\\) on mixed\\.$#"
count: 3
path: src/Component/Serializer/Generator/SerializerGenerator.php
-
message: "#^Cannot call method getVersions\\(\\) on mixed\\.$#"
count: 1
path: src/Component/Serializer/Generator/SerializerGenerator.php
-
message: "#^Unsafe access to private constant RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Generator\\\\SerializerGenerator\\:\\:FILENAME_PREFIX through static\\:\\:\\.$#"
count: 1
path: src/Component/Serializer/Generator/SerializerGenerator.php
- -
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\ModelsChecksumGenerator\\:\\:getStoredChecksums\\(\\) should return array\\<string, string\\> but returns mixed\\.$#" message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\ModelsChecksumGenerator\\:\\:getStoredChecksums\\(\\) should return array\\<string, string\\> but returns mixed\\.$#"
count: 1 count: 1
path: src/Component/Serializer/ModelsChecksumGenerator.php path: src/Component/Serializer/ModelsChecksumGenerator.php
-
message: "#^Parameter \\#1 \\$iterator of class RetailCrm\\\\Api\\\\Component\\\\FilesIteratorChecksumGenerator constructor expects Iterator\\<mixed, mixed\\>, RetailCrm\\\\Api\\\\Component\\\\PhpFilesIterator given\\.$#"
count: 1
path: src/Component/Serializer/ModelsChecksumGenerator.php
- -
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\ModelsChecksumGenerator\\:\\:getChecksumFileName\\(\\) through static\\:\\:\\.$#" message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\ModelsChecksumGenerator\\:\\:getChecksumFileName\\(\\) through static\\:\\:\\.$#"
count: 4 count: 4
path: src/Component/Serializer/ModelsChecksumGenerator.php path: src/Component/Serializer/ModelsChecksumGenerator.php
-
message: "#^Method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\BaseJMSParser\\:\\:parse\\(\\) should return array but returns mixed\\.$#"
count: 1
path: src/Component/Serializer/Parser/BaseJMSParser.php
-
message: "#^Parameter \\#1 \\$object of function get_class expects object, mixed given\\.$#"
count: 2
path: src/Component/Serializer/Parser/JMSParser.php
- -
message: "#^Parameter \\#2 \\$string2 of function strncmp expects string, class\\-string\\|false given\\.$#" message: "#^Parameter \\#2 \\$string2 of function strncmp expects string, class\\-string\\|false given\\.$#"
count: 1 count: 1
@ -232,26 +280,86 @@ parameters:
count: 1 count: 1
path: src/Component/Serializer/Parser/JMSParser.php path: src/Component/Serializer/Parser/JMSParser.php
-
message: "#^Cannot access offset 0 on mixed\\.$#"
count: 2
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Cannot access offset 1 on mixed\\.$#"
count: 2
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Cannot access offset 2 on mixed\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#1 \\$className of class Liip\\\\MetadataParser\\\\Metadata\\\\PropertyTypeClass constructor expects string, mixed given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#1 \\$className of static method Liip\\\\MetadataParser\\\\Metadata\\\\PropertyTypeDateTime\\:\\:fromDateTimeClass\\(\\) expects string, mixed given\\.$#"
count: 2
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#1 \\$format of class Liip\\\\MetadataParser\\\\Metadata\\\\DateTimeOptions constructor expects string\\|null, mixed given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#1 \\$typeInfo of method RetailCrm\\\\Api\\\\Component\\\\Serializer\\\\Parser\\\\JMSTypeParser\\:\\:parseType\\(\\) expects array, mixed given\\.$#"
count: 2
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#1 \\$typeName of class Liip\\\\MetadataParser\\\\Metadata\\\\PropertyTypePrimitive constructor expects string, mixed given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#1 \\$typeName of static method Liip\\\\MetadataParser\\\\Metadata\\\\PropertyTypeDateTime\\:\\:isTypeDateTime\\(\\) expects string, mixed given\\.$#"
count: 2
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#1 \\$typeName of static method Liip\\\\MetadataParser\\\\Metadata\\\\PropertyTypePrimitive\\:\\:isTypePrimitive\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#"
count: 3
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#2 \\$zone of class Liip\\\\MetadataParser\\\\Metadata\\\\DateTimeOptions constructor expects string\\|null, mixed given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
-
message: "#^Parameter \\#3 \\$deserializeFormat of class Liip\\\\MetadataParser\\\\Metadata\\\\DateTimeOptions constructor expects string\\|null, mixed given\\.$#"
count: 1
path: src/Component/Serializer/Parser/JMSTypeParser.php
- -
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Component\\\\Transformer\\\\DateTimeTransformer\\:\\:createFromFormat\\(\\) through static\\:\\:\\.$#" message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Component\\\\Transformer\\\\DateTimeTransformer\\:\\:createFromFormat\\(\\) through static\\:\\:\\.$#"
count: 3 count: 3
path: src/Component/Transformer/DateTimeTransformer.php path: src/Component/Transformer/DateTimeTransformer.php
-
message: "#^Method RetailCrm\\\\Api\\\\Event\\\\AbstractRequestEvent\\:\\:getApiKey\\(\\) should return string but returns array\\|string\\.$#"
count: 1
path: src/Event/AbstractRequestEvent.php
-
message: "#^Property RetailCrm\\\\Api\\\\Event\\\\AbstractRequestEvent\\:\\:\\$apiKey \\(string\\) does not accept array\\|string\\.$#"
count: 1
path: src/Event/AbstractRequestEvent.php
- -
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Exception\\\\ApiException\\:\\:getErrorMessage\\(\\) through static\\:\\:\\.$#" message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Exception\\\\ApiException\\:\\:getErrorMessage\\(\\) through static\\:\\:\\.$#"
count: 1 count: 1
path: src/Exception/ApiException.php path: src/Exception/ApiException.php
-
message: "#^Parameter \\#3 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
count: 1
path: src/Exception/ClientException.php
- -
message: "#^Property RetailCrm\\\\Api\\\\Model\\\\Response\\\\ErrorResponse\\:\\:\\$errorMsg \\(string\\) on left side of \\?\\? is not nullable\\.$#" message: "#^Property RetailCrm\\\\Api\\\\Model\\\\Response\\\\ErrorResponse\\:\\:\\$errorMsg \\(string\\) on left side of \\?\\? is not nullable\\.$#"
count: 2 count: 2
@ -267,6 +375,11 @@ parameters:
count: 1 count: 1
path: src/Handler/Request/PsrRequestHandler.php path: src/Handler/Request/PsrRequestHandler.php
-
message: "#^Parameter \\#2 \\$code of class RetailCrm\\\\Api\\\\Exception\\\\Client\\\\HandlerException constructor expects int, mixed given\\.$#"
count: 1
path: src/Handler/Request/RequestDataHandler.php
- -
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Handler\\\\Request\\\\RequestDataHandler\\:\\:queryShouldBeUsed\\(\\) through static\\:\\:\\.$#" message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Handler\\\\Request\\\\RequestDataHandler\\:\\:queryShouldBeUsed\\(\\) through static\\:\\:\\.$#"
count: 1 count: 1
@ -307,11 +420,6 @@ parameters:
count: 1 count: 1
path: src/Handler/Response/FilesDownloadResponseHandler.php path: src/Handler/Response/FilesDownloadResponseHandler.php
-
message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Handler\\\\Response\\\\FilesDownloadResponseHandler\\:\\:isFileRequest\\(\\) through static\\:\\:\\.$#"
count: 1
path: src/Handler/Response/FilesDownloadResponseHandler.php
- -
message: "#^Property RetailCrm\\\\Api\\\\Model\\\\ResponseData\\:\\:\\$responseArray \\(array\\<int\\|string, mixed\\>\\) on left side of \\?\\? is not nullable\\.$#" message: "#^Property RetailCrm\\\\Api\\\\Model\\\\ResponseData\\:\\:\\$responseArray \\(array\\<int\\|string, mixed\\>\\) on left side of \\?\\? is not nullable\\.$#"
count: 1 count: 1
@ -327,3 +435,8 @@ parameters:
count: 1 count: 1
path: src/ResourceGroup/AbstractApiResourceGroup.php path: src/ResourceGroup/AbstractApiResourceGroup.php
-
message: "#^Parameter \\#2 \\$code of class RetailCrm\\\\Api\\\\Exception\\\\Client\\\\HttpClientException constructor expects int, mixed given\\.$#"
count: 1
path: src/ResourceGroup/AbstractApiResourceGroup.php

View file

@ -1,5 +1,4 @@
includes: includes:
- phpstan-baseline-serializer.neon
- phpstan-baseline.neon # TODO: This should be removed eventually. - phpstan-baseline.neon # TODO: This should be removed eventually.
parameters: parameters:

View file

@ -18,18 +18,15 @@ use RetailCrm\Api\Interfaces\RequestTransformerInterface;
use RetailCrm\Api\Interfaces\ResponseTransformerInterface; use RetailCrm\Api\Interfaces\ResponseTransformerInterface;
use RetailCrm\Api\ResourceGroup\Api; use RetailCrm\Api\ResourceGroup\Api;
use RetailCrm\Api\ResourceGroup\Costs; use RetailCrm\Api\ResourceGroup\Costs;
use RetailCrm\Api\ResourceGroup\CustomerInteraction;
use RetailCrm\Api\ResourceGroup\Customers; use RetailCrm\Api\ResourceGroup\Customers;
use RetailCrm\Api\ResourceGroup\CustomersCorporate; use RetailCrm\Api\ResourceGroup\CustomersCorporate;
use RetailCrm\Api\ResourceGroup\CustomFields; use RetailCrm\Api\ResourceGroup\CustomFields;
use RetailCrm\Api\ResourceGroup\CustomMethods; use RetailCrm\Api\ResourceGroup\CustomMethods;
use RetailCrm\Api\ResourceGroup\Delivery; use RetailCrm\Api\ResourceGroup\Delivery;
use RetailCrm\Api\ResourceGroup\Features;
use RetailCrm\Api\ResourceGroup\Files; use RetailCrm\Api\ResourceGroup\Files;
use RetailCrm\Api\ResourceGroup\Integration; use RetailCrm\Api\ResourceGroup\Integration;
use RetailCrm\Api\ResourceGroup\Inventories; use RetailCrm\Api\ResourceGroup\Inventories;
use RetailCrm\Api\ResourceGroup\Loyalty; use RetailCrm\Api\ResourceGroup\Loyalty;
use RetailCrm\Api\ResourceGroup\Notifications;
use RetailCrm\Api\ResourceGroup\Orders; use RetailCrm\Api\ResourceGroup\Orders;
use RetailCrm\Api\ResourceGroup\Packs; use RetailCrm\Api\ResourceGroup\Packs;
use RetailCrm\Api\ResourceGroup\Payments; use RetailCrm\Api\ResourceGroup\Payments;
@ -42,7 +39,6 @@ use RetailCrm\Api\ResourceGroup\Tasks;
use RetailCrm\Api\ResourceGroup\Telephony; use RetailCrm\Api\ResourceGroup\Telephony;
use RetailCrm\Api\ResourceGroup\Users; use RetailCrm\Api\ResourceGroup\Users;
use RetailCrm\Api\ResourceGroup\Verification; use RetailCrm\Api\ResourceGroup\Verification;
use RetailCrm\Api\ResourceGroup\WebAnalytics;
/** /**
* Class Client * Class Client
@ -68,9 +64,6 @@ class Client
/** @var \RetailCrm\Api\ResourceGroup\CustomFields */ /** @var \RetailCrm\Api\ResourceGroup\CustomFields */
public $customFields; public $customFields;
/** @var \RetailCrm\Api\ResourceGroup\CustomerInteraction */
public $customerInteraction;
/** @var \RetailCrm\Api\ResourceGroup\Customers */ /** @var \RetailCrm\Api\ResourceGroup\Customers */
public $customers; public $customers;
@ -80,9 +73,6 @@ class Client
/** @var \RetailCrm\Api\ResourceGroup\Delivery */ /** @var \RetailCrm\Api\ResourceGroup\Delivery */
public $delivery; public $delivery;
/** @var \RetailCrm\Api\ResourceGroup\Features */
public $features;
/** @var \RetailCrm\Api\ResourceGroup\Files */ /** @var \RetailCrm\Api\ResourceGroup\Files */
public $files; public $files;
@ -92,9 +82,6 @@ class Client
/** @var \RetailCrm\Api\ResourceGroup\Loyalty */ /** @var \RetailCrm\Api\ResourceGroup\Loyalty */
public $loyalty; public $loyalty;
/** @var \RetailCrm\Api\ResourceGroup\Notifications */
public $notifications;
/** @var \RetailCrm\Api\ResourceGroup\Orders */ /** @var \RetailCrm\Api\ResourceGroup\Orders */
public $orders; public $orders;
@ -134,9 +121,6 @@ class Client
/** @var \RetailCrm\Api\ResourceGroup\CustomMethods */ /** @var \RetailCrm\Api\ResourceGroup\CustomMethods */
public $customMethods; public $customMethods;
/** @var \RetailCrm\Api\ResourceGroup\WebAnalytics */
public $webAnalytics;
/** @var StreamFactoryInterface */ /** @var StreamFactoryInterface */
private $streamFactory; private $streamFactory;
@ -191,14 +175,6 @@ class Client
$eventDispatcher, $eventDispatcher,
$logger $logger
); );
$this->customerInteraction = new CustomerInteraction(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->customers = new Customers( $this->customers = new Customers(
$url, $url,
$httpClient, $httpClient,
@ -223,14 +199,6 @@ class Client
$eventDispatcher, $eventDispatcher,
$logger $logger
); );
$this->features = new Features(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->files = new Files( $this->files = new Files(
$url, $url,
$httpClient, $httpClient,
@ -255,14 +223,6 @@ class Client
$eventDispatcher, $eventDispatcher,
$logger $logger
); );
$this->notifications = new Notifications(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->orders = new Orders( $this->orders = new Orders(
$url, $url,
$httpClient, $httpClient,
@ -367,14 +327,6 @@ class Client
$eventDispatcher, $eventDispatcher,
$logger $logger
); );
$this->webAnalytics = new WebAnalytics(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
} }
/** /**

View file

@ -25,7 +25,6 @@ use Symfony\Component\Console\Output\OutputInterface;
class CompilerPromptCommand extends Command class CompilerPromptCommand extends Command
{ {
private const PACKAGE_NAME = 'retailcrm/api-client-php'; private const PACKAGE_NAME = 'retailcrm/api-client-php';
private const COMPILER_PLUGIN = 'civicrm/composer-compile-plugin';
/** /**
* Sets description and help for a command. * Sets description and help for a command.
@ -33,14 +32,20 @@ class CompilerPromptCommand extends Command
protected function configure(): void protected function configure(): void
{ {
$this->setName('compiler:prompt') $this->setName('compiler:prompt')
->setDescription('Enable or disable code generation during client installation & update.') ->setDescription('Enable or disable composer compiler prompt.')
->setHelp( ->setHelp('Use this command to suppress the compiler message and enable automatic compilation.')
'Use this command to enable or disable automatic code generation.' ->addOption(
)->addOption( 'deactivate',
'revert', 'd',
'r',
InputOption::VALUE_OPTIONAL, InputOption::VALUE_OPTIONAL,
'You will need to run ./vendor/bin/retailcrm-client models:generate -a after each update.', 'Hide compiler prompt and run compiler task automatically. This mode is used by default.',
false
)
->addOption(
'activate',
'a',
InputOption::VALUE_OPTIONAL,
'Show compiler prompt and only run compiler task if user allows it.',
false false
); );
} }
@ -73,13 +78,12 @@ class CompilerPromptCommand extends Command
return -1; return -1;
} }
$revert = false !== $input->getOption('revert'); $activatePrompt = false !== $input->getOption('activate');
if ($revert) { if ($activatePrompt) {
static::deactivateAutoCompiler($json); static::activatePrompt($json);
} else { } else {
static::activateAutoCompiler($json); static::deactivatePrompt($json);
static::activatePlugin($json);
} }
try { try {
@ -97,110 +101,73 @@ class CompilerPromptCommand extends Command
} }
$output->writeln(sprintf( $output->writeln(sprintf(
'<fg=black;bg=green> ✓ Done, code generation has been %s.</>', '<fg=black;bg=green> ✓ Done, generator prompt is now %s.</>',
$revert ? 'disabled' : 'enabled' $activatePrompt ? 'enabled' : 'disabled'
)); ));
return 0; return 0;
} }
/** /**
* Activate plugin in the provided composer.json * Activate prompt in the provided composer.json
* *
* @param array<string, array<string, array<string>|string>> $composerJson * @param array<string, mixed> $composerJson
*/ */
private static function activatePlugin(array &$composerJson): void private static function activatePrompt(array &$composerJson): void
{
if (!array_key_exists('config', $composerJson)) {
$composerJson['config'] = [
'allow-plugins' => [
static::COMPILER_PLUGIN => true
]
];
return;
}
if (!array_key_exists('allow-plugins', $composerJson['config'])) {
$composerJson['config']['allow-plugins'] = [
static::COMPILER_PLUGIN => true
];
return;
}
$composerJson['config']['allow-plugins'][static::COMPILER_PLUGIN] = true;
}
/**
* Activate auto compiler in the provided composer.json
*
* @param array<string, array<string, array<string>|string>> $composerJson
*/
private static function activateAutoCompiler(array &$composerJson): void
{ {
if (!array_key_exists('extra', $composerJson)) { if (!array_key_exists('extra', $composerJson)) {
$composerJson['extra'] = [ $composerJson['extra'] = [];
'compile-mode' => 'whitelist',
'compile-whitelist' => [self::PACKAGE_NAME]
];
return;
} }
if (array_key_exists('compile-mode', $composerJson['extra'])) { if (
if ( array_key_exists('compile-whitelist', $composerJson['extra']) &&
'prompt' === $composerJson['extra']['compile-mode'] || is_array($composerJson['extra']['compile-whitelist']) &&
'none' === $composerJson['extra']['compile-mode'] in_array(static::PACKAGE_NAME, $composerJson['extra']['compile-whitelist'], true)
) { ) {
$composerJson['extra']['compile-mode'] = 'whitelist'; $composerJson['extra']['compile-whitelist'] = array_filter(
} $composerJson['extra']['compile-whitelist'],
static function ($value) {
if ('all' === $composerJson['extra']['compile-mode']) { return static::PACKAGE_NAME !== $value;
return; }
} );
} }
$composerJson['extra']['compile-mode'] = 'whitelist'; if (
empty($composerJson['extra']['compile-whitelist']) &&
if (!array_key_exists('compile-whitelist', $composerJson['extra'])) { array_key_exists('compile-mode', $composerJson['extra']) &&
$composerJson['extra']['compile-whitelist'] = [self::PACKAGE_NAME]; 'whitelist' === $composerJson['extra']['compile-mode']
) {
return; unset($composerJson['extra']['compile-whitelist'], $composerJson['extra']['compile-mode']);
} }
if (!in_array(self::PACKAGE_NAME, $composerJson['extra']['compile-whitelist'])) { if (1 === count($composerJson['extra'])) {
$composerJson['extra']['compile-whitelist'][] = self::PACKAGE_NAME; unset($composerJson['extra']);
} }
} }
/** /**
* Deactivate prompt in the provided composer.json * Deactivate prompt in the provided composer.json
* *
* @param array<string, array<string>> $composerJson * @param array<string, mixed> $composerJson
* *
* @SuppressWarnings(PHPMD.ElseExpression) * @SuppressWarnings(PHPMD.ElseExpression)
*/ */
private static function deactivateAutoCompiler(array &$composerJson): void private static function deactivatePrompt(array &$composerJson): void
{ {
if (!array_key_exists('extra', $composerJson)) { if (!array_key_exists('extra', $composerJson)) {
return; $composerJson['extra'] = [];
} }
if ( if (
array_key_exists('compile-whitelist', $composerJson['extra']) && array_key_exists('compile-whitelist', $composerJson['extra']) &&
null !== $composerJson['extra']['compile-whitelist'] is_array($composerJson['extra']['compile-whitelist']) &&
!in_array(static::PACKAGE_NAME, $composerJson['extra']['compile-whitelist'], true)
) { ) {
$composerJson['extra']['compile-whitelist'] = array_filter( $composerJson['extra']['compile-whitelist'][] = static::PACKAGE_NAME;
$composerJson['extra']['compile-whitelist'], } else {
static function (string $item) { $composerJson['extra']['compile-whitelist'] = [static::PACKAGE_NAME];
return $item !== self::PACKAGE_NAME;
}
);
if (0 === count($composerJson['extra']['compile-whitelist'])) {
unset($composerJson['extra']['compile-whitelist']);
}
} }
$composerJson['extra']['compile-mode'] = 'whitelist';
} }
} }

View file

@ -13,7 +13,6 @@ use RetailCrm\Api\Component\ModelsGenerator;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/** /**
* Class GenerateModelsCommand * Class GenerateModelsCommand
@ -83,15 +82,7 @@ class GenerateModelsCommand extends AbstractModelsProcessorCommand
$output->writeln(''); $output->writeln('');
} }
try { $generator->generate();
$generator->generate();
} catch (\Throwable $throwable) {
$styled = new SymfonyStyle($input, $output);
$styled->error($throwable->getMessage());
$styled->writeln($throwable->getTraceAsString());
return -1;
}
$output->writeln(sprintf( $output->writeln(sprintf(
'<fg=black;bg=green> ✓ Done, generated code for %d models.</>', '<fg=black;bg=green> ✓ Done, generated code for %d models.</>',

View file

@ -67,7 +67,7 @@ class ComposerLocator
$counter++; $counter++;
$dir = dirname($dir); $dir = dirname($dir);
if (2 < $counter) { if (5 < $counter) {
break; break;
} }
} }
@ -80,9 +80,7 @@ class ComposerLocator
*/ */
private static function getBaseDirectory(): string private static function getBaseDirectory(): string
{ {
$cwd = getcwd(); return (string) realpath(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', '..']));
return false === $cwd ? (string) realpath(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', '..'])) : $cwd;
} }
/** /**

View file

@ -11,12 +11,12 @@ namespace RetailCrm\Api\Component;
use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationReader;
use Liip\MetadataParser\Builder; use Liip\MetadataParser\Builder;
use Liip\MetadataParser\ModelParser\RawMetadata\PropertyCollection;
use Liip\MetadataParser\Parser; use Liip\MetadataParser\Parser;
use Liip\MetadataParser\RecursionChecker; use Liip\MetadataParser\RecursionChecker;
use Liip\Serializer\Configuration\GeneratorConfiguration; use Liip\Serializer\Configuration\GeneratorConfiguration;
use Liip\Serializer\Template\Deserialization; use Liip\Serializer\Template\Deserialization;
use Liip\Serializer\Template\Serialization; use Liip\Serializer\Template\Serialization;
use RetailCrm\Api\Component\Utils;
use RetailCrm\Api\Component\Serializer\Generator\DeserializerGenerator; use RetailCrm\Api\Component\Serializer\Generator\DeserializerGenerator;
use RetailCrm\Api\Component\Serializer\Generator\SerializerGenerator; use RetailCrm\Api\Component\Serializer\Generator\SerializerGenerator;
use RetailCrm\Api\Component\Serializer\ModelsChecksumGenerator; use RetailCrm\Api\Component\Serializer\ModelsChecksumGenerator;
@ -177,7 +177,6 @@ class ModelsGenerator
$configurationArray['classes'][$class] = []; $configurationArray['classes'][$class] = [];
} }
PropertyCollection::useIdenticalNamingStrategy();
$configuration = GeneratorConfiguration::createFomArray($configurationArray); $configuration = GeneratorConfiguration::createFomArray($configurationArray);
$parsers = [new JMSParser(new AnnotationReader())]; $parsers = [new JMSParser(new AnnotationReader())];
$builder = new Builder(new Parser($parsers), new RecursionChecker(null, [])); $builder = new Builder(new Parser($parsers), new RecursionChecker(null, []));

View file

@ -86,7 +86,6 @@ class PhpFilesIterator implements Iterator
* *
* @return int|string * @return int|string
*/ */
#[\ReturnTypeWillChange]
public function key() public function key()
{ {
return $this->parent->key(); return $this->parent->key();

View file

@ -16,424 +16,212 @@ use Liip\Serializer\Exception\UnsupportedFormatException;
use Liip\Serializer\SerializerInterface; use Liip\Serializer\SerializerInterface;
use Pnz\JsonException\Json; use Pnz\JsonException\Json;
if (PHP_VERSION_ID >= 80000) { /**
* Class ArraySupportDecorator
*
* @category ArraySupportDecorator
* @package RetailCrm\Api\Component\Serializer
*/
class ArraySupportDecorator implements SerializerInterface
{
/** @var \Liip\Serializer\SerializerInterface */
private $serializer;
/** /**
* Class ArraySupportDecorator * ArraySupportDecorator constructor.
* *
* @category ArraySupportDecorator * @param \Liip\Serializer\SerializerInterface $serializer
* @package RetailCrm\Api\Component\Serializer
*/ */
class ArraySupportDecorator implements SerializerInterface public function __construct(SerializerInterface $serializer)
{ {
/** @var \Liip\Serializer\SerializerInterface */ $this->serializer = $serializer;
private $serializer;
/**
* ArraySupportDecorator constructor.
*
* @param \Liip\Serializer\SerializerInterface $serializer
*/
public function __construct(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}
/**
* @inheritDoc
* @throws \JsonException
*/
public function serialize($data, string $format, ?Context $context = null): string
{
if ('json' !== $format) {
throw new UnsupportedFormatException('Liip serializer only supports JSON for now');
}
if (is_array($data)) {
try {
return Json::encode($this->encodeArray($data, $context), JSON_UNESCAPED_SLASHES);
} catch (JsonException $exception) {
throw new Exception(
sprintf(
'Failed to JSON encode data for %s. This is not supposed to happen.',
// @phpstan-ignore-next-line
is_object($data) ? get_class($data) : gettype($data)
),
0,
$exception
);
}
}
return $this->serializer->serialize($data, $format, $context);
}
/**
* @inheritDoc
*/
public function deserialize(string $data, string $type, string $format, ?Context $context = null): mixed
{
if ('json' !== $format) {
throw new UnsupportedFormatException('Liip serializer only supports JSON for now');
}
if (static::isArrayType($type)) {
try {
$array = Json::decode($data, true);
} catch (JsonException $exception) {
throw new Exception('Failed to JSON decode data. This is not supposed to happen.', 0, $exception);
}
return $this->serializer->fromArray($this->decodeArray($array, $type, $context), $type, $context);
}
return $this->serializer->deserialize($data, $type, $format, $context);
}
/**
* @inheritDoc
*
* @return array<int|string, mixed>
*/
public function toArray($data, ?Context $context = null): array
{
if (is_array($data)) {
return $this->encodeArray($data, $context);
}
return $this->serializer->toArray($data, $context);
}
/**
* @inheritDoc
*
* @param array<int|string, mixed> $data
*
* @return array<int|string, mixed>|object
*/
public function fromArray(array $data, string $type, ?Context $context = null): mixed
{
if (static::isArrayType($type)) {
return $this->decodeArray($data, $type, $context);
}
return $this->serializer->fromArray($data, $type, $context);
}
/**
* Encodes array of objects into simple multidimensional array.
*
* @param mixed[] $data
* @param \Liip\Serializer\Context|null $context
*
* @return mixed[]
* @throws \Liip\Serializer\Exception\Exception
* @throws \Liip\Serializer\Exception\UnsupportedTypeException
*/
private function encodeArray(array $data, ?Context $context = null): array
{
$result = [];
foreach ($data as $key => $value) {
switch (gettype($value)) {
case 'array':
$result[$key] = $this->encodeArray($value, $context);
break;
case 'object':
$result[$key] = $this->serializer->toArray($value, $context);
break;
default:
$result[$key] = $value;
break;
}
}
return $result;
}
/**
* Decodes array of arrays to array of objects.
*
* @param mixed[] $data
* @param string $type
* @param \Liip\Serializer\Context|null $context
*
* @return array<int|string, mixed>
* @throws \Liip\Serializer\Exception\Exception
* @throws \Liip\Serializer\Exception\UnsupportedTypeException
*/
private function decodeArray(array $data, string $type, ?Context $context = null): array
{
$result = [];
$subtype = static::getArrayValueType($type);
if (class_exists($subtype)) {
foreach ($data as $key => $item) {
if (is_array($item)) {
$result[$key] = $this->decodeArray($item, $subtype, $context);
continue;
}
$result[$key] = $item;
}
return $result;
}
return $data;
}
/**
* Returns true if provided type is an array.
*
* @param string $type
*
* @return bool
*/
private static function isArrayType(string $type): bool
{
return false !== strpos($type, 'array');
}
/**
* Returns array value type from types like 'array<string|int, Class\Name>' or 'array<string>'.
*
* @param string $type
*
* @return string
*/
private static function getArrayValueType(string $type): string
{
$matches = [];
preg_match_all(
'/array(\s+)?\<([\w\|\\\\]+)\s+\,\s+([\w\|\\\\]+)\>/m',
$type,
$matches,
PREG_SET_ORDER,
0
);
if (count($matches) > 0) {
return $matches[count($matches) - 1];
}
preg_match_all('/array(\s+)?\<([\w\|\\\\]+)\>/m', $type, $matches, PREG_SET_ORDER, 0);
if (count($matches) > 0) {
return $matches[count($matches) - 1];
}
return 'mixed';
}
} }
} else {
/** /**
* Class ArraySupportDecorator * @inheritDoc
* * @throws \JsonException
* @category ArraySupportDecorator
* @package RetailCrm\Api\Component\Serializer
*/ */
class ArraySupportDecorator implements SerializerInterface public function serialize($data, string $format, ?Context $context = null): string
{ {
/** @var \Liip\Serializer\SerializerInterface */ if ('json' !== $format) {
private $serializer; throw new UnsupportedFormatException('Liip serializer only supports JSON for now');
/**
* ArraySupportDecorator constructor.
*
* @param \Liip\Serializer\SerializerInterface $serializer
*/
public function __construct(SerializerInterface $serializer)
{
$this->serializer = $serializer;
} }
/** if (is_array($data)) {
* @inheritDoc try {
* @throws \JsonException return Json::encode($this->encodeArray($data, $context), JSON_UNESCAPED_SLASHES);
*/ } catch (JsonException $exception) {
public function serialize($data, string $format, ?Context $context = null): string throw new Exception(
{ sprintf(
if ('json' !== $format) { 'Failed to JSON encode data for %s. This is not supposed to happen.',
throw new UnsupportedFormatException('Liip serializer only supports JSON for now'); // @phpstan-ignore-next-line
is_object($data) ? get_class($data) : gettype($data)
),
0,
$exception
);
} }
if (is_array($data)) {
try {
return Json::encode($this->encodeArray($data, $context), JSON_UNESCAPED_SLASHES);
} catch (JsonException $exception) {
throw new Exception(
sprintf(
'Failed to JSON encode data for %s. This is not supposed to happen.',
// @phpstan-ignore-next-line
is_object($data) ? get_class($data) : gettype($data)
),
0,
$exception
);
}
}
return $this->serializer->serialize($data, $format, $context);
} }
/** return $this->serializer->serialize($data, $format, $context);
* @inheritDoc }
*/
public function deserialize(string $data, string $type, string $format, ?Context $context = null) /**
{ * @inheritDoc
if ('json' !== $format) { */
throw new UnsupportedFormatException('Liip serializer only supports JSON for now'); public function deserialize(string $data, string $type, string $format, ?Context $context = null)
{
if ('json' !== $format) {
throw new UnsupportedFormatException('Liip serializer only supports JSON for now');
}
if (static::isArrayType($type)) {
try {
$array = Json::decode($data, true);
} catch (JsonException $exception) {
throw new Exception('Failed to JSON decode data. This is not supposed to happen.', 0, $exception);
} }
if (static::isArrayType($type)) { return $this->serializer->fromArray($this->decodeArray($array, $type, $context), $type, $context);
try { }
$array = Json::decode($data, true);
} catch (JsonException $exception) { return $this->serializer->deserialize($data, $type, $format, $context);
throw new Exception('Failed to JSON decode data. This is not supposed to happen.', 0, $exception); }
/**
* @inheritDoc
*
* @return array<int|string, mixed>
*/
public function toArray($data, ?Context $context = null): array
{
if (is_array($data)) {
return $this->encodeArray($data, $context);
}
return $this->serializer->toArray($data, $context);
}
/**
* @inheritDoc
*
* @param array<int|string, mixed> $data
*
* @return array<int|string, mixed>|object
*/
public function fromArray(array $data, string $type, ?Context $context = null)
{
if (static::isArrayType($type)) {
return $this->decodeArray($data, $type, $context);
}
return $this->serializer->fromArray($data, $type, $context);
}
/**
* Encodes array of objects into simple multidimensional array.
*
* @param mixed[] $data
* @param \Liip\Serializer\Context|null $context
*
* @return mixed[]
* @throws \Liip\Serializer\Exception\Exception
* @throws \Liip\Serializer\Exception\UnsupportedTypeException
*/
private function encodeArray(array $data, ?Context $context = null): array
{
$result = [];
foreach ($data as $key => $value) {
switch (gettype($value)) {
case 'array':
$result[$key] = $this->encodeArray($value, $context);
break;
case 'object':
$result[$key] = $this->serializer->toArray($value, $context);
break;
default:
$result[$key] = $value;
break;
}
}
return $result;
}
/**
* Decodes array of arrays to array of objects.
*
* @param mixed[] $data
* @param string $type
* @param \Liip\Serializer\Context|null $context
*
* @return array<int|string, mixed>
* @throws \Liip\Serializer\Exception\Exception
* @throws \Liip\Serializer\Exception\UnsupportedTypeException
*/
private function decodeArray(array $data, string $type, ?Context $context = null): array
{
$result = [];
$subtype = static::getArrayValueType($type);
if (class_exists($subtype)) {
foreach ($data as $key => $item) {
if (is_array($item)) {
$result[$key] = $this->decodeArray($item, $subtype, $context);
continue;
} }
return $this->serializer->fromArray($this->decodeArray($array, $type, $context), $type, $context); $result[$key] = $item;
}
return $this->serializer->deserialize($data, $type, $format, $context);
}
/**
* @inheritDoc
*
* @return array<int|string, mixed>
*/
public function toArray($data, ?Context $context = null): array
{
if (is_array($data)) {
return $this->encodeArray($data, $context);
}
return $this->serializer->toArray($data, $context);
}
/**
* @inheritDoc
*
* @param array<int|string, mixed> $data
*
* @return array<int|string, mixed>|object
*/
public function fromArray(array $data, string $type, ?Context $context = null)
{
if (static::isArrayType($type)) {
return $this->decodeArray($data, $type, $context);
}
return $this->serializer->fromArray($data, $type, $context);
}
/**
* Encodes array of objects into simple multidimensional array.
*
* @param mixed[] $data
* @param \Liip\Serializer\Context|null $context
*
* @return mixed[]
* @throws \Liip\Serializer\Exception\Exception
* @throws \Liip\Serializer\Exception\UnsupportedTypeException
*/
private function encodeArray(array $data, ?Context $context = null): array
{
$result = [];
foreach ($data as $key => $value) {
switch (gettype($value)) {
case 'array':
$result[$key] = $this->encodeArray($value, $context);
break;
case 'object':
$result[$key] = $this->serializer->toArray($value, $context);
break;
default:
$result[$key] = $value;
break;
}
} }
return $result; return $result;
} }
/** return $data;
* Decodes array of arrays to array of objects. }
*
* @param mixed[] $data
* @param string $type
* @param \Liip\Serializer\Context|null $context
*
* @return array<int|string, mixed>
* @throws \Liip\Serializer\Exception\Exception
* @throws \Liip\Serializer\Exception\UnsupportedTypeException
*/
private function decodeArray(array $data, string $type, ?Context $context = null): array
{
$result = [];
$subtype = static::getArrayValueType($type);
if (class_exists($subtype)) { /**
foreach ($data as $key => $item) { * Returns true if provided type is an array.
if (is_array($item)) { *
$result[$key] = $this->decodeArray($item, $subtype, $context); * @param string $type
continue; *
} * @return bool
*/
private static function isArrayType(string $type): bool
{
return false !== strpos($type, 'array');
}
$result[$key] = $item; /**
} * Returns array value type from types like 'array<string|int, Class\Name>' or 'array<string>'.
*
* @param string $type
*
* @return string
*/
private static function getArrayValueType(string $type): string
{
$matches = [];
return $result; preg_match_all(
} '/array(\s+)?\<([\w\|\\\\]+)\s+\,\s+([\w\|\\\\]+)\>/m',
$type,
$matches,
PREG_SET_ORDER,
0
);
return $data; if (count($matches) > 0) {
return $matches[count($matches) - 1];
} }
/** preg_match_all('/array(\s+)?\<([\w\|\\\\]+)\>/m', $type, $matches, PREG_SET_ORDER, 0);
* Returns true if provided type is an array.
* if (count($matches) > 0) {
* @param string $type return $matches[count($matches) - 1];
*
* @return bool
*/
private static function isArrayType(string $type): bool
{
return false !== strpos($type, 'array');
} }
/** return 'mixed';
* Returns array value type from types like 'array<string|int, Class\Name>' or 'array<string>'.
*
* @param string $type
*
* @return string
*/
private static function getArrayValueType(string $type): string
{
$matches = [];
preg_match_all(
'/array(\s+)?\<([\w\|\\\\]+)\s+\,\s+([\w\|\\\\]+)\>/m',
$type,
$matches,
PREG_SET_ORDER,
0
);
if (count($matches) > 0) {
return $matches[count($matches) - 1];
}
preg_match_all('/array(\s+)?\<([\w\|\\\\]+)\>/m', $type, $matches, PREG_SET_ORDER, 0);
if (count($matches) > 0) {
return $matches[count($matches) - 1];
}
return 'mixed';
}
} }
} }

View file

@ -1,6 +1,11 @@
<?php <?php
declare(strict_types=1); /**
* PHP version 7.3
*
* @category DeserializerGenerator
* @package RetailCrm\Api\Component\Serializer\Generator
*/
namespace RetailCrm\Api\Component\Serializer\Generator; namespace RetailCrm\Api\Component\Serializer\Generator;
@ -13,8 +18,6 @@ use Liip\MetadataParser\Metadata\PropertyTypeDateTime;
use Liip\MetadataParser\Metadata\PropertyTypePrimitive; use Liip\MetadataParser\Metadata\PropertyTypePrimitive;
use Liip\MetadataParser\Metadata\PropertyTypeUnknown; use Liip\MetadataParser\Metadata\PropertyTypeUnknown;
use Liip\MetadataParser\Reducer\TakeBestReducer; use Liip\MetadataParser\Reducer\TakeBestReducer;
use Liip\Serializer\Configuration\ClassToGenerate;
use Liip\Serializer\Configuration\GeneratorConfiguration;
use Liip\Serializer\Path\ArrayPath; use Liip\Serializer\Path\ArrayPath;
use Liip\Serializer\Path\ModelPath; use Liip\Serializer\Path\ModelPath;
use Liip\Serializer\Template\Deserialization; use Liip\Serializer\Template\Deserialization;
@ -23,75 +26,129 @@ use RetailCrm\Api\Component\Serializer\Type\PropertyTypeMixed;
use RetailCrm\Api\Interfaces\Orders\CustomerInterface; use RetailCrm\Api\Interfaces\Orders\CustomerInterface;
use RetailCrm\Api\Model\Entity\Customers\Customer; use RetailCrm\Api\Model\Entity\Customers\Customer;
use RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate; use RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate;
use RuntimeException;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
final class DeserializerGenerator /**
* Class DeserializerGenerator
*
* @category DeserializerGenerator
* @package RetailCrm\Api\Component\Serializer\Generator
* @license https://github.com/liip/serializer/blob/master/LICENSE MIT License
* @author Liip <https://github.com/liip>
* @author Pavel Kovalenko
* @see https://github.com/liip/serializer
* @internal
*
* @SuppressWarnings(PHPMD)
*/
class DeserializerGenerator
{ {
private const FILENAME_PREFIX = 'deserialize'; private const FILENAME_PREFIX = 'deserialize';
/** @var \Symfony\Component\Filesystem\Filesystem */ /**
private $filesystem; * @var Deserialization
*/
/** @var \Liip\Serializer\Configuration\GeneratorConfiguration */
private $configuration;
/** @var \Liip\Serializer\Template\Deserialization */
private $templating; private $templating;
/** @var \RetailCrm\Api\Component\Serializer\Template\CustomDeserialization */ /**
* @var \RetailCrm\Api\Component\Serializer\Template\CustomDeserialization
*/
private $customTemplating; private $customTemplating;
/** @var string */ /**
private $cacheDirectory; * @var Filesystem
*/
private $filesystem;
/** @var \Liip\MetadataParser\Builder */ /**
* @var Builder
*/
private $metadataBuilder; private $metadataBuilder;
/** /**
* @param list<class-string> $classesToGenerate This is a list of FQCN classnames * This is a list of fqn classnames
*
* I.e.
*
* [
* Product::class,
* ];
*
* @var string[]
*/
private $classesToGenerate;
/**
* @var string
*/
private $cacheDirectory;
/**
* @param \Liip\Serializer\Template\Deserialization $templating
* @param \RetailCrm\Api\Component\Serializer\Template\CustomDeserialization $customTemplating
* @param string[] $classesToGenerate
* @param string $cacheDirectory
*/ */
public function __construct( public function __construct(
Deserialization $templating, Deserialization $templating,
CustomDeserialization $customTemplating, CustomDeserialization $customTemplating,
array $classesToGenerate, array $classesToGenerate,
string $cacheDirectory, string $cacheDirectory
GeneratorConfiguration $configuration = null
) { ) {
$this->cacheDirectory = $cacheDirectory;
$this->templating = $templating; $this->templating = $templating;
$this->customTemplating = $customTemplating; $this->customTemplating = $customTemplating;
$this->classesToGenerate = $classesToGenerate;
$this->cacheDirectory = $cacheDirectory;
$this->filesystem = new Filesystem(); $this->filesystem = new Filesystem();
$this->configuration = $this->createGeneratorConfiguration($configuration, $classesToGenerate);
} }
/**
* @param string $className
*
* @return string
*/
public static function buildDeserializerFunctionName(string $className): string public static function buildDeserializerFunctionName(string $className): string
{ {
return self::FILENAME_PREFIX.'_'.str_replace('\\', '_', $className); return static::FILENAME_PREFIX . '_' . str_replace('\\', '_', $className);
} }
/**
* @param \Liip\MetadataParser\Builder $metadataBuilder
*
* @throws \Exception
*/
public function generate(Builder $metadataBuilder): void public function generate(Builder $metadataBuilder): void
{ {
$this->metadataBuilder = $metadataBuilder; $this->metadataBuilder = $metadataBuilder;
$this->filesystem->mkdir($this->cacheDirectory); $this->filesystem->mkdir($this->cacheDirectory);
/** @var ClassToGenerate $classToGenerate */ foreach ($this->classesToGenerate as $className) {
foreach ($this->configuration as $classToGenerate) {
// we do not use the oldest version reducer here and hope for the best // we do not use the oldest version reducer here and hope for the best
// otherwise we end up with generated property names for accessor methods // otherwise we end up with generated property names for accessor methods
$classMetadata = $metadataBuilder->build($classToGenerate->getClassName(), [ $classMetadata = $metadataBuilder->build($className, [
new TakeBestReducer(), new TakeBestReducer(),
]); ]);
$this->writeFile($classMetadata); $this->writeFile($classMetadata);
} }
} }
/**
* @param \Liip\MetadataParser\Metadata\ClassMetadata $classMetadata
*
* @throws \Exception
*/
private function writeFile(ClassMetadata $classMetadata): void private function writeFile(ClassMetadata $classMetadata): void
{ {
if (\count($classMetadata->getConstructorParameters())) { if (count($classMetadata->getConstructorParameters())) {
throw new \Exception(sprintf('We currently do not support deserializing when the root class has a non-empty constructor. Class %s', $classMetadata->getClassName())); throw new RuntimeException(sprintf(
'We currently do not support deserializing when the root class has a non-empty constructor. Class %s',
$classMetadata->getClassName()
));
} }
$functionName = self::buildDeserializerFunctionName($classMetadata->getClassName()); $functionName = static::buildDeserializerFunctionName($classMetadata->getClassName());
$arrayPath = new ArrayPath('jsonData'); $arrayPath = new ArrayPath('jsonData');
$code = $this->templating->renderFunction( $code = $this->templating->renderFunction(
@ -105,7 +162,13 @@ final class DeserializerGenerator
} }
/** /**
* @param array<string, positive-int> $stack * @param \Liip\MetadataParser\Metadata\ClassMetadata $classMetadata
* @param \Liip\Serializer\Path\ArrayPath $arrayPath
* @param \Liip\Serializer\Path\ModelPath $modelPath
* @param mixed[] $stack
*
* @return string
* @throws \Exception
*/ */
private function generateCodeForClass( private function generateCodeForClass(
ClassMetadata $classMetadata, ClassMetadata $classMetadata,
@ -116,9 +179,9 @@ final class DeserializerGenerator
$stack[$classMetadata->getClassName()] = ($stack[$classMetadata->getClassName()] ?? 0) + 1; $stack[$classMetadata->getClassName()] = ($stack[$classMetadata->getClassName()] ?? 0) + 1;
$constructorArgumentNames = []; $constructorArgumentNames = [];
$overwrittenNames = [];
$initCode = ''; $initCode = '';
$code = ''; $code = '';
foreach ($classMetadata->getProperties() as $propertyMetadata) { foreach ($classMetadata->getProperties() as $propertyMetadata) {
$propertyArrayPath = $arrayPath->withFieldName($propertyMetadata->getSerializedName()); $propertyArrayPath = $arrayPath->withFieldName($propertyMetadata->getSerializedName());
@ -126,9 +189,6 @@ final class DeserializerGenerator
$argument = $classMetadata->getConstructorParameter($propertyMetadata->getName()); $argument = $classMetadata->getConstructorParameter($propertyMetadata->getName());
$default = var_export($argument->isRequired() ? null : $argument->getDefaultValue(), true); $default = var_export($argument->isRequired() ? null : $argument->getDefaultValue(), true);
$tempVariable = ModelPath::tempVariable([(string) $modelPath, $propertyMetadata->getName()]); $tempVariable = ModelPath::tempVariable([(string) $modelPath, $propertyMetadata->getName()]);
if (\array_key_exists($propertyMetadata->getName(), $constructorArgumentNames)) {
$overwrittenNames[$propertyMetadata->getName()] = true;
}
$constructorArgumentNames[$propertyMetadata->getName()] = (string) $tempVariable; $constructorArgumentNames[$propertyMetadata->getName()] = (string) $tempVariable;
$initCode .= $this->templating->renderArgument( $initCode .= $this->templating->renderArgument(
@ -146,21 +206,26 @@ final class DeserializerGenerator
} }
$constructorArguments = []; $constructorArguments = [];
foreach ($classMetadata->getConstructorParameters() as $definition) { foreach ($classMetadata->getConstructorParameters() as $definition) {
if (\array_key_exists($definition->getName(), $constructorArgumentNames)) { if (array_key_exists($definition->getName(), $constructorArgumentNames)) {
$constructorArguments[] = $constructorArgumentNames[$definition->getName()]; $constructorArguments[] = $constructorArgumentNames[$definition->getName()];
continue; continue;
} }
if ($definition->isRequired()) { if ($definition->isRequired()) {
$msg = sprintf('Unknown constructor argument "%s". Class %s only has properties that tell how to handle %s.', $definition->getName(), $classMetadata->getClassName(), implode(', ', array_keys($constructorArgumentNames))); throw new RuntimeException(sprintf(
if ($overwrittenNames) { 'Unknown constructor argument "%s" in "%s(%s)"',
$msg .= sprintf(' Multiple definitions for fields %s seen - the last one overwrites previous ones.', implode(', ', array_keys($overwrittenNames))); $definition->getName(),
} $classMetadata->getClassName(),
throw new \Exception($msg); implode(', ', array_keys($constructorArgumentNames))
));
} }
$constructorArguments[] = var_export($definition->getDefaultValue(), true); $constructorArguments[] = var_export($definition->getDefaultValue(), true);
} }
if (\count($constructorArgumentNames) > 0) {
if (count($constructorArgumentNames) > 0) {
$code .= $this->templating->renderUnset(array_values($constructorArgumentNames)); $code .= $this->templating->renderUnset(array_values($constructorArgumentNames));
} }
@ -168,7 +233,13 @@ final class DeserializerGenerator
return $this->generateCustomerInterface($classMetadata, $arrayPath, $modelPath, $initCode, $stack); return $this->generateCustomerInterface($classMetadata, $arrayPath, $modelPath, $initCode, $stack);
} }
return $this->templating->renderClass((string) $modelPath, $classMetadata->getClassName(), $constructorArguments, $code, $initCode); return $this->templating->renderClass(
(string) $modelPath,
$classMetadata->getClassName(),
$constructorArguments,
$code,
$initCode
);
} }
/** /**
@ -211,7 +282,13 @@ final class DeserializerGenerator
} }
/** /**
* @param array<string, positive-int> $stack * @param \Liip\MetadataParser\Metadata\PropertyMetadata $propertyMetadata
* @param \Liip\Serializer\Path\ArrayPath $arrayPath
* @param \Liip\Serializer\Path\ModelPath $modelPath
* @param mixed[] $stack
*
* @return string
* @throws \Exception
*/ */
private function generateCodeForProperty( private function generateCodeForProperty(
PropertyMetadata $propertyMetadata, PropertyMetadata $propertyMetadata,
@ -223,16 +300,16 @@ final class DeserializerGenerator
return ''; return '';
} }
if (Recursion::hasMaxDepthReached($propertyMetadata, $stack)) {
return '';
}
if ($propertyMetadata->getAccessor()->hasSetterMethod()) { if ($propertyMetadata->getAccessor()->hasSetterMethod()) {
$tempVariable = ModelPath::tempVariable([(string) $modelPath, $propertyMetadata->getName()]); $tempVariable = ModelPath::tempVariable([(string) $modelPath, $propertyMetadata->getName()]);
$code = $this->generateCodeForField($propertyMetadata, $arrayPath, $tempVariable, $stack); $code = $this->generateCodeForField($propertyMetadata, $arrayPath, $tempVariable, $stack);
$code .= $this->templating->renderConditional( $code .= $this->templating->renderConditional(
(string) $tempVariable, (string) $tempVariable,
$this->templating->renderSetter((string) $modelPath, $propertyMetadata->getAccessor()->getSetterMethod(), (string) $tempVariable) $this->templating->renderSetter(
(string) $modelPath,
(string) $propertyMetadata->getAccessor()->getSetterMethod(),
(string) $tempVariable
)
); );
$code .= $this->templating->renderUnset([(string) $tempVariable]); $code .= $this->templating->renderUnset([(string) $tempVariable]);
@ -245,7 +322,13 @@ final class DeserializerGenerator
} }
/** /**
* @param array<string, positive-int> $stack * @param \Liip\MetadataParser\Metadata\PropertyMetadata $propertyMetadata
* @param \Liip\Serializer\Path\ArrayPath $arrayPath
* @param \Liip\Serializer\Path\ModelPath $modelPath
* @param mixed[] $stack
*
* @return string
* @throws \Exception
*/ */
private function generateCodeForField( private function generateCodeForField(
PropertyMetadata $propertyMetadata, PropertyMetadata $propertyMetadata,
@ -259,17 +342,14 @@ final class DeserializerGenerator
); );
} }
private function isArrayTraversable(PropertyTypeArray $array): bool
{
if (method_exists($array, 'isCollection')) {
return $array->isCollection();
}
return $array->isTraversable();
}
/** /**
* @param array<string, positive-int> $stack * @param \Liip\MetadataParser\Metadata\PropertyMetadata $propertyMetadata
* @param \Liip\Serializer\Path\ArrayPath $arrayPath
* @param \Liip\Serializer\Path\ModelPath $modelPropertyPath
* @param mixed[] $stack
*
* @return string
* @throws \Exception
*/ */
private function generateInnerCodeForFieldType( private function generateInnerCodeForFieldType(
PropertyMetadata $propertyMetadata, PropertyMetadata $propertyMetadata,
@ -279,50 +359,64 @@ final class DeserializerGenerator
): string { ): string {
$type = $propertyMetadata->getType(); $type = $propertyMetadata->getType();
if ($type instanceof PropertyTypeArray) {
if ($type->getSubType() instanceof PropertyTypePrimitive) {
// for arrays of scalars, copy the field even when its an empty array
return $this->templating->renderAssignJsonDataToField((string) $modelPropertyPath, (string) $arrayPath);
}
// either array or hashmap with second param the type of values
// the index works the same whether its numeric or hashmap
return $this->generateCodeForArray($type, $arrayPath, $modelPropertyPath, $stack);
}
switch ($type) { switch ($type) {
case $type instanceof PropertyTypeArray:
if ($this->isArrayTraversable($type)) {
return $this->generateCodeForArrayCollection($propertyMetadata, $type, $arrayPath, $modelPropertyPath, $stack);
}
return $this->generateCodeForArray($type, $arrayPath, $modelPropertyPath, $stack);
case $type instanceof PropertyTypeDateTime: case $type instanceof PropertyTypeDateTime:
if (method_exists($type, 'getDeserializeFormat')) { if (null !== $type->getZone()) {
$format = $type->getDeserializeFormat(); throw new RuntimeException('Timezone support is not implemented');
if (null !== $format) {
return $this->templating->renderAssignDateTimeFromFormat($type->isImmutable(), (string) $modelPropertyPath, (string) $arrayPath, $format, $type->getZone());
}
return $this->templating->renderAssignDateTimeToField($type->isImmutable(), (string) $modelPropertyPath, (string) $arrayPath);
} }
$formats = $type->getDeserializeFormats() ?: (\is_string($type->getFormat()) ? [$type->getFormat()] : $type->getFormat()); $format = $type->getDeserializeFormat() ?: $type->getFormat();
if (null !== $formats) {
return $this->templating->renderAssignDateTimeFromFormat($type->isImmutable(), (string) $modelPropertyPath, (string) $arrayPath, $formats, $type->getZone()); if (null !== $format) {
return $this->templating->renderAssignDateTimeFromFormat(
$type->isImmutable(),
(string) $modelPropertyPath,
(string) $arrayPath,
$format
);
} }
return $this->templating->renderAssignDateTimeToField($type->isImmutable(), (string) $modelPropertyPath, (string) $arrayPath); return $this->templating->renderAssignDateTimeToField(
$type->isImmutable(),
(string) $modelPropertyPath,
(string) $arrayPath
);
case $type instanceof PropertyTypePrimitive && 'float' === $type->getTypeName(): case $type instanceof PropertyTypePrimitive && 'float' === $type->getTypeName():
return $this->templating->renderAssignJsonDataToFieldWithCasting((string) $modelPropertyPath, (string) $arrayPath, 'float'); return $this->templating->renderAssignJsonDataToFieldWithCasting(
(string) $modelPropertyPath,
(string) $arrayPath,
'float'
);
case $type instanceof PropertyTypePrimitive: case $type instanceof PropertyTypePrimitive:
case $type instanceof PropertyTypeUnknown: case $type instanceof PropertyTypeUnknown:
case $type instanceof PropertyTypeMixed: case $type instanceof PropertyTypeMixed:
return $this->templating->renderAssignJsonDataToField((string) $modelPropertyPath, (string) $arrayPath); return $this->templating->renderAssignJsonDataToField((string) $modelPropertyPath, (string) $arrayPath);
case $type instanceof PropertyTypeClass: case $type instanceof PropertyTypeClass:
return $this->generateCodeForClass($type->getClassMetadata(), $arrayPath, $modelPropertyPath, $stack); return $this->generateCodeForClass($type->getClassMetadata(), $arrayPath, $modelPropertyPath, $stack);
default: default:
throw new \Exception('Unexpected type '. get_class($type) .' at '.$modelPropertyPath); throw new RuntimeException('Unexpected type ' . get_class($type) . ' at ' . $modelPropertyPath);
} }
} }
/** /**
* @param array<string, positive-int> $stack * @param \Liip\MetadataParser\Metadata\PropertyTypeArray $type
* @param \Liip\Serializer\Path\ArrayPath $arrayPath
* @param \Liip\Serializer\Path\ModelPath $modelPath
* @param mixed[] $stack
*
* @return string
* @throws \Exception
*/ */
private function generateCodeForArray( private function generateCodeForArray(
PropertyTypeArray $type, PropertyTypeArray $type,
@ -330,11 +424,6 @@ final class DeserializerGenerator
ModelPath $modelPath, ModelPath $modelPath,
array $stack array $stack
): string { ): string {
if ($type->getSubType() instanceof PropertyTypePrimitive) {
// for arrays of scalars, copy the field even when its an empty array
return $this->templating->renderAssignJsonDataToField((string) $modelPath, (string) $arrayPath);
}
$index = ModelPath::indexVariable((string) $arrayPath); $index = ModelPath::indexVariable((string) $arrayPath);
$arrayPropertyPath = $arrayPath->withVariable((string) $index); $arrayPropertyPath = $arrayPath->withVariable((string) $index);
$modelPropertyPath = $modelPath->withArray((string) $index); $modelPropertyPath = $modelPath->withArray((string) $index);
@ -344,16 +433,22 @@ final class DeserializerGenerator
case $subType instanceof PropertyTypeArray: case $subType instanceof PropertyTypeArray:
$innerCode = $this->generateCodeForArray($subType, $arrayPropertyPath, $modelPropertyPath, $stack); $innerCode = $this->generateCodeForArray($subType, $arrayPropertyPath, $modelPropertyPath, $stack);
break; break;
case $subType instanceof PropertyTypeClass: case $subType instanceof PropertyTypeClass:
$innerCode = $this->generateCodeForClass($subType->getClassMetadata(), $arrayPropertyPath, $modelPropertyPath, $stack); $innerCode = $this->generateCodeForClass(
$subType->getClassMetadata(),
$arrayPropertyPath,
$modelPropertyPath,
$stack
);
break; break;
case $subType instanceof PropertyTypeUnknown: case $subType instanceof PropertyTypeUnknown:
return $this->templating->renderAssignJsonDataToField((string) $modelPath, (string) $arrayPath); $innerCode = $this->templating->renderAssignJsonDataToField(
$modelPropertyPath,
$arrayPropertyPath
);
break;
default: default:
throw new \Exception('Unexpected array subtype '. get_class($subType)); throw new RuntimeException('Unexpected array subtype ' . get_class($subType));
} }
if ('' === $innerCode) { if ('' === $innerCode) {
@ -365,42 +460,4 @@ final class DeserializerGenerator
return $code; return $code;
} }
/**
* @param array<string, positive-int> $stack
*/
private function generateCodeForArrayCollection(
PropertyMetadata $propertyMetadata,
PropertyTypeArray $type,
ArrayPath $arrayPath,
ModelPath $modelPath,
array $stack
): string {
$tmpVariable = ModelPath::tempVariable([(string) $modelPath, $propertyMetadata->getName()]);
$innerCode = $this->generateCodeForArray($type, $arrayPath, $tmpVariable, $stack);
if ('' === $innerCode) {
return '';
}
return $innerCode.$this->templating->renderArrayCollection((string) $modelPath, (string) $tmpVariable);
}
/**
* @param list<class-string> $classesToGenerate
*/
private function createGeneratorConfiguration(
?GeneratorConfiguration $configuration,
array $classesToGenerate
): GeneratorConfiguration {
if (null === $configuration) {
$configuration = new GeneratorConfiguration([], []);
}
foreach ($classesToGenerate as $className) {
$configuration->addClassToGenerate(new ClassToGenerate($configuration, $className));
}
return $configuration;
}
} }

View file

@ -1,57 +0,0 @@
<?php
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Generator;
use Liip\MetadataParser\Metadata\PropertyMetadata;
use Liip\MetadataParser\Metadata\PropertyTypeArray;
use Liip\MetadataParser\Metadata\PropertyTypeClass;
abstract class Recursion
{
/**
* @param array<string, positive-int> $stack
*/
public static function check(string $className, array $stack, string $modelPath): bool
{
if (\array_key_exists($className, $stack) && $stack[$className] > 1) {
throw new \Exception(sprintf('recursion for %s at %s', key($stack), $modelPath));
}
return false;
}
/**
* @param array<string, positive-int> $stack
*/
public static function hasMaxDepthReached(PropertyMetadata $propertyMetadata, array $stack): bool
{
if (null === $propertyMetadata->getMaxDepth()) {
return false;
}
$className = self::getClassNameFromProperty($propertyMetadata);
if (null === $className) {
return false;
}
$classStackCount = $stack[$className] ?? 0;
return $classStackCount > $propertyMetadata->getMaxDepth();
}
private static function getClassNameFromProperty(PropertyMetadata $propertyMetadata): ?string
{
$type = $propertyMetadata->getType();
if ($type instanceof PropertyTypeArray) {
$type = $type->getLeafType();
}
if (!($type instanceof PropertyTypeClass)) {
return null;
}
return $type->getClassName();
}
}

View file

@ -1,9 +1,17 @@
<?php <?php
/**
* PHP version 7.3
*
* @category SerializerGenerator
* @package RetailCrm\Api\Component\Serializer\Generator
*/
declare(strict_types=1); declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Generator; namespace RetailCrm\Api\Component\Serializer\Generator;
use DateTime;
use Liip\MetadataParser\Builder; use Liip\MetadataParser\Builder;
use Liip\MetadataParser\Metadata\ClassMetadata; use Liip\MetadataParser\Metadata\ClassMetadata;
use Liip\MetadataParser\Metadata\PropertyMetadata; use Liip\MetadataParser\Metadata\PropertyMetadata;
@ -19,99 +27,140 @@ use Liip\MetadataParser\Reducer\TakeBestReducer;
use Liip\MetadataParser\Reducer\VersionReducer; use Liip\MetadataParser\Reducer\VersionReducer;
use Liip\Serializer\Configuration\GeneratorConfiguration; use Liip\Serializer\Configuration\GeneratorConfiguration;
use Liip\Serializer\Template\Serialization; use Liip\Serializer\Template\Serialization;
use RetailCrm\Api\Component\Serializer\Template\CustomSerialization;
use RetailCrm\Api\Component\Serializer\Type\PropertyTypeMixed;
use RetailCrm\Api\Interfaces\Orders\CustomerInterface; use RetailCrm\Api\Interfaces\Orders\CustomerInterface;
use RetailCrm\Api\Model\Entity\Customers\Customer; use RetailCrm\Api\Model\Entity\Customers\Customer;
use RetailCrm\Api\Model\Entity\Customers\CustomerTag; use RetailCrm\Api\Model\Entity\Customers\CustomerTag;
use RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate; use RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate;
use RetailCrm\Api\Component\Serializer\Template\CustomSerialization;
use RetailCrm\Api\Component\Serializer\Type\PropertyTypeMixed;
use RetailCrm\Api\Model\Entity\CustomersCorporate\SerializedRelationAbstractCustomer; use RetailCrm\Api\Model\Entity\CustomersCorporate\SerializedRelationAbstractCustomer;
use RetailCrm\Api\Model\Entity\Orders\SerializedRelationCustomer; use RetailCrm\Api\Model\Entity\Orders\SerializedRelationCustomer;
use RuntimeException;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
final class SerializerGenerator /**
* Class SerializerGenerator
*
* @category SerializerGenerator
* @package RetailCrm\Api\Component\Serializer\Generator
* @license https://github.com/liip/serializer/blob/master/LICENSE MIT License
* @author Liip <https://github.com/liip>
* @author Pavel Kovalenko
* @see https://github.com/liip/serializer
* @internal
*
* @SuppressWarnings(PHPMD)
*/
class SerializerGenerator
{ {
private const FILENAME_PREFIX = 'serialize'; private const FILENAME_PREFIX = 'serialize';
/** @var \Symfony\Component\Filesystem\Filesystem */ /**
private $filesystem; * @var Serialization
*/
/** @var \Liip\Serializer\Template\Serialization */
private $templating; private $templating;
/** @var \Liip\Serializer\Configuration\GeneratorConfiguration */ /**
private $configuration; * @var \RetailCrm\Api\Component\Serializer\Template\CustomSerialization
*/
/** @var string */
private $cacheDirectory;
/** @var \RetailCrm\Api\Component\Serializer\Template\CustomSerialization */
private $customTemplating; private $customTemplating;
/** @var \Liip\MetadataParser\Builder */ /**
* @var Builder
*/
private $metadataBuilder; private $metadataBuilder;
/**
* @var GeneratorConfiguration<mixed>
*/
private $configuration;
/**
* @var string
*/
private $cacheDirectory;
/**
* @var Filesystem
*/
private $filesystem;
/**
* SerializerGenerator constructor.
*
* @param \Liip\Serializer\Template\Serialization $templating
* @param \RetailCrm\Api\Component\Serializer\Template\CustomSerialization $customTemplating
* @param \Liip\Serializer\Configuration\GeneratorConfiguration<mixed> $configuration
* @param string $cacheDirectory
*/
public function __construct( public function __construct(
Serialization $templating, Serialization $templating,
CustomSerialization $customTemplating, CustomSerialization $customTemplating,
GeneratorConfiguration $configuration, GeneratorConfiguration $configuration,
string $cacheDirectory string $cacheDirectory
) { ) {
$this->cacheDirectory = $cacheDirectory; $this->templating = $templating;
$this->configuration = $configuration;
$this->templating = $templating;
$this->customTemplating = $customTemplating; $this->customTemplating = $customTemplating;
$this->configuration = $configuration;
$this->cacheDirectory = $cacheDirectory;
$this->filesystem = new Filesystem(); $this->filesystem = new Filesystem();
} }
/** /**
* @param list<string> $serializerGroups * @param string $className
* @param string|null $apiVersion
* @param array<mixed> $serializerGroups
*
* @return string
*/ */
public static function buildSerializerFunctionName(string $className, ?string $apiVersion, array $serializerGroups): string public static function buildSerializerFunctionName(
{ string $className,
$functionName = self::FILENAME_PREFIX.'_'.$className; ?string $apiVersion,
if (\count($serializerGroups)) { array $serializerGroups
$functionName .= '_'.implode('_', $serializerGroups); ): string {
} $functionName = static::FILENAME_PREFIX . '_' . $className;
if (null !== $apiVersion) {
$functionName .= '_'.$apiVersion; if (count($serializerGroups)) {
$functionName .= '_' . implode('_', $serializerGroups);
} }
return preg_replace('/[^a-zA-Z0-9_]/', '_', $functionName); if (null !== $apiVersion) {
$functionName .= '_' . $apiVersion;
}
return (string) preg_replace('/[^a-zA-Z0-9_]/', '_', $functionName);
} }
/**
* @param \Liip\MetadataParser\Builder $metadataBuilder
*/
public function generate(Builder $metadataBuilder): void public function generate(Builder $metadataBuilder): void
{ {
$this->metadataBuilder = $metadataBuilder; $this->metadataBuilder = $metadataBuilder;
$this->filesystem->mkdir($this->cacheDirectory); $this->filesystem->mkdir($this->cacheDirectory);
foreach ($this->configuration as $classToGenerate) { foreach ($this->configuration as $classToGenerate) {
foreach ($classToGenerate as $groupCombination) { foreach ($classToGenerate as $groupCombination) {
$className = $classToGenerate->getClassName(); $className = $classToGenerate->getClassName();
foreach ($groupCombination->getVersions() as $version) { foreach ($groupCombination->getVersions() as $version) {
$groups = $groupCombination->getGroups();
if ('' === $version) { if ('' === $version) {
if ([] === $groups) { $metadata = $metadataBuilder->build($className, [
$metadata = $metadataBuilder->build($className, [ new PreferredReducer(),
new PreferredReducer(), new TakeBestReducer(),
new TakeBestReducer(), ]);
]);
$this->writeFile($className, null, [], $metadata); $this->writeFile($className, null, $groupCombination->getGroups(), $metadata);
} else {
$metadata = $metadataBuilder->build($className, [
new GroupReducer($groups),
new PreferredReducer(),
new TakeBestReducer(),
]);
$this->writeFile($className, null, $groups, $metadata);
}
} else { } else {
$metadata = $metadataBuilder->build($className, [ $metadata = $metadataBuilder->build($className, [
new VersionReducer($version), new VersionReducer($version),
new GroupReducer($groups), new GroupReducer($groupCombination->getGroups()),
new TakeBestReducer(), new TakeBestReducer(),
]); ]);
$this->writeFile($className, $version, $groups, $metadata);
$this->writeFile($className, $version, $groupCombination->getGroups(), $metadata);
} }
} }
} }
@ -119,7 +168,10 @@ final class SerializerGenerator
} }
/** /**
* @param list<string> $serializerGroups * @param string $className
* @param string|null $apiVersion
* @param array<mixed> $serializerGroups
* @param \Liip\MetadataParser\Metadata\ClassMetadata $classMetadata
*/ */
private function writeFile( private function writeFile(
string $className, string $className,
@ -127,7 +179,8 @@ final class SerializerGenerator
array $serializerGroups, array $serializerGroups,
ClassMetadata $classMetadata ClassMetadata $classMetadata
): void { ): void {
$functionName = self::buildSerializerFunctionName($className, $apiVersion, $serializerGroups); sort($serializerGroups);
$functionName = static::buildSerializerFunctionName($className, $apiVersion, $serializerGroups);
$code = $this->templating->renderFunction( $code = $this->templating->renderFunction(
$functionName, $functionName,
@ -139,8 +192,15 @@ final class SerializerGenerator
} }
/** /**
* @param list<string> $serializerGroups * @param \Liip\MetadataParser\Metadata\ClassMetadata $classMetadata
* @param array<string, positive-int> $stack * @param string|null $apiVersion
* @param array<mixed> $serializerGroups
* @param string $arrayPath
* @param string $modelPath
* @param array<mixed> $stack
*
* @return string
* @throws \Exception
*/ */
private function generateCodeForClass( private function generateCodeForClass(
ClassMetadata $classMetadata, ClassMetadata $classMetadata,
@ -166,10 +226,17 @@ final class SerializerGenerator
} }
$stack[$classMetadata->getClassName()] = ($stack[$classMetadata->getClassName()] ?? 0) + 1; $stack[$classMetadata->getClassName()] = ($stack[$classMetadata->getClassName()] ?? 0) + 1;
$code = ''; $code = '';
foreach ($classMetadata->getProperties() as $propertyMetadata) { foreach ($classMetadata->getProperties() as $propertyMetadata) {
$code .= $this->generateCodeForField($propertyMetadata, $apiVersion, $serializerGroups, $arrayPath, $modelPath, $stack); $code .= $this->generateCodeForField(
$propertyMetadata,
$apiVersion,
$serializerGroups,
$arrayPath,
$modelPath,
$stack
);
} }
return $this->templating->renderClass($arrayPath, $code); return $this->templating->renderClass($arrayPath, $code);
@ -267,8 +334,15 @@ final class SerializerGenerator
} }
/** /**
* @param list<string> $serializerGroups * @param \Liip\MetadataParser\Metadata\PropertyMetadata $propertyMetadata
* @param array<string, positive-int> $stack * @param string|null $apiVersion
* @param array<mixed> $serializerGroups
* @param string $arrayPath
* @param string $modelPath
* @param array<mixed> $stack
*
* @return string
* @throws \Exception
*/ */
private function generateCodeForField( private function generateCodeForField(
PropertyMetadata $propertyMetadata, PropertyMetadata $propertyMetadata,
@ -278,34 +352,61 @@ final class SerializerGenerator
string $modelPath, string $modelPath,
array $stack array $stack
): string { ): string {
if (Recursion::hasMaxDepthReached($propertyMetadata, $stack)) { $modelPropertyPath = $modelPath . '->' . $propertyMetadata->getName();
return ''; $fieldPath = $arrayPath . '["' . $propertyMetadata->getSerializedName() . '"]';
}
$modelPropertyPath = $modelPath.'->'.$propertyMetadata->getName();
$fieldPath = $arrayPath.'["'.$propertyMetadata->getSerializedName().'"]';
if ($propertyMetadata->getAccessor()->hasGetterMethod()) { if ($propertyMetadata->getAccessor()->hasGetterMethod()) {
$tempVariable = str_replace(['->', '[', ']', '$'], '', $modelPath).ucfirst($propertyMetadata->getName()); $tempVariable = str_replace(['->', '[', ']', '$'], '', $modelPath) . ucfirst($propertyMetadata->getName());
return $this->templating->renderConditional( return $this->templating->renderConditional(
$this->templating->renderTempVariable($tempVariable, $this->templating->renderGetter($modelPath, $propertyMetadata->getAccessor()->getGetterMethod())), $this->templating->renderTempVariable(
$this->generateCodeForFieldType($propertyMetadata->getType(), $apiVersion, $serializerGroups, $fieldPath, '$'.$tempVariable, $stack) $tempVariable,
$this->templating->renderGetter(
$modelPath,
(string) $propertyMetadata->getAccessor()->getGetterMethod()
)
),
$this->generateCodeForFieldType(
$propertyMetadata->getType(),
$apiVersion,
$serializerGroups,
$fieldPath,
'$' . $tempVariable,
$stack
)
); );
} }
if (!$propertyMetadata->isPublic()) { if (!$propertyMetadata->isPublic()) {
throw new \Exception(sprintf('Property %s is not public and no getter has been defined. Stack %s', $modelPropertyPath, var_export($stack, true))); throw new RuntimeException(sprintf(
'Property %s is not public and no getter has been defined. Stack %s',
$modelPropertyPath,
var_export($stack, true)
));
} }
return $this->templating->renderConditional( return $this->templating->renderConditional(
$modelPropertyPath, $modelPropertyPath,
$this->generateCodeForFieldType($propertyMetadata->getType(), $apiVersion, $serializerGroups, $fieldPath, $modelPropertyPath, $stack) $this->generateCodeForFieldType(
$propertyMetadata->getType(),
$apiVersion,
$serializerGroups,
$fieldPath,
$modelPropertyPath,
$stack
)
); );
} }
/** /**
* @param list<string> $serializerGroups * @param \Liip\MetadataParser\Metadata\PropertyType $type
* @param array<string, positive-int> $stack * @param string|null $apiVersion
* @param array<mixed> $serializerGroups
* @param string $fieldPath
* @param string $modelPropertyPath
* @param array<mixed> $stack
*
* @return string
* @throws \Exception
*/ */
private function generateCodeForFieldType( private function generateCodeForFieldType(
PropertyType $type, PropertyType $type,
@ -315,9 +416,31 @@ final class SerializerGenerator
string $modelPropertyPath, string $modelPropertyPath,
array $stack array $stack
): string { ): string {
if ($type instanceof PropertyTypeArray) {
if ($type->getSubType() instanceof PropertyTypePrimitive) {
// for arrays of scalars, copy the field even when its an empty array
return $this->templating->renderAssign($fieldPath, $modelPropertyPath);
}
// either array or hashmap with second param the type of values
// the index works the same whether its numeric or hashmap
return $this->generateCodeForArray(
$type,
$apiVersion,
$serializerGroups,
$fieldPath,
$modelPropertyPath,
$stack
);
}
switch ($type) { switch ($type) {
case $type instanceof PropertyTypeDateTime: case $type instanceof PropertyTypeDateTime:
$dateFormat = $type->getFormat() ?: \DateTimeInterface::ISO8601; if (null !== $type->getZone()) {
throw new \RuntimeException('Timezone support is not implemented');
}
$dateFormat = $type->getFormat() ?: DateTime::ATOM;
return $this->templating->renderAssign( return $this->templating->renderAssign(
$fieldPath, $fieldPath,
@ -331,19 +454,30 @@ final class SerializerGenerator
return $this->templating->renderAssign($fieldPath, $modelPropertyPath); return $this->templating->renderAssign($fieldPath, $modelPropertyPath);
case $type instanceof PropertyTypeClass: case $type instanceof PropertyTypeClass:
return $this->generateCodeForClass($type->getClassMetadata(), $apiVersion, $serializerGroups, $fieldPath, $modelPropertyPath, $stack); return $this->generateCodeForClass(
$type->getClassMetadata(),
case $type instanceof PropertyTypeArray: $apiVersion,
return $this->generateCodeForArray($type, $apiVersion, $serializerGroups, $fieldPath, $modelPropertyPath, $stack); $serializerGroups,
$fieldPath,
$modelPropertyPath,
$stack
);
default: default:
throw new \Exception('Unexpected type '. get_class($type) .' at '.$modelPropertyPath); throw new RuntimeException('Unexpected type ' . \get_class($type) . ' at ' . $modelPropertyPath);
} }
} }
/** /**
* @param list<string> $serializerGroups * @param \Liip\MetadataParser\Metadata\PropertyTypeArray $type
* @param array<string, positive-int> $stack * @param string|null $apiVersion
* @param array<mixed> $serializerGroups
* @param string $arrayPath
* @param string $modelPath
* @param array<mixed> $stack
*
* @return string
* @throws \Exception
*/ */
private function generateCodeForArray( private function generateCodeForArray(
PropertyTypeArray $type, PropertyTypeArray $type,
@ -353,25 +487,35 @@ final class SerializerGenerator
string $modelPath, string $modelPath,
array $stack array $stack
): string { ): string {
$index = '$index'.mb_strlen($arrayPath); $index = '$index' . \mb_strlen($arrayPath);
$subType = $type->getSubType(); $subType = $type->getSubType();
switch ($subType) { switch ($subType) {
case $subType instanceof PropertyTypePrimitive:
case $subType instanceof PropertyTypeArray && self::isArrayForPrimitive($subType):
case $subType instanceof PropertyTypeUnknown:
return $this->templating->renderArrayAssign($arrayPath, $modelPath);
case $subType instanceof PropertyTypeArray: case $subType instanceof PropertyTypeArray:
$innerCode = $this->generateCodeForArray($subType, $apiVersion, $serializerGroups, $arrayPath.'['.$index.']', $modelPath.'['.$index.']', $stack); $innerCode = $this->generateCodeForArray(
$subType,
$apiVersion,
$serializerGroups,
$arrayPath . '[' . $index . ']',
$modelPath . '[' . $index . ']',
$stack
);
break; break;
case $subType instanceof PropertyTypeClass: case $subType instanceof PropertyTypeClass:
$innerCode = $this->generateCodeForClass($subType->getClassMetadata(), $apiVersion, $serializerGroups, $arrayPath.'['.$index.']', $modelPath.'['.$index.']', $stack); $innerCode = $this->generateCodeForClass(
$subType->getClassMetadata(),
$apiVersion,
$serializerGroups,
$arrayPath . '[' . $index . ']',
$modelPath . '[' . $index . ']',
$stack
);
break;
case $subType instanceof PropertyTypeUnknown:
$innerCode = $this->templating->renderAssign($arrayPath, $modelPath);
break; break;
default: default:
throw new \Exception('Unexpected array subtype '. get_class($subType)); throw new RuntimeException('Unexpected array subtype ' . get_class($subType));
} }
if ('' === $innerCode) { if ('' === $innerCode) {
@ -388,16 +532,4 @@ final class SerializerGenerator
return $this->templating->renderLoopArray($arrayPath, $modelPath, $index, $innerCode); return $this->templating->renderLoopArray($arrayPath, $modelPath, $index, $innerCode);
} }
private static function isArrayForPrimitive(PropertyTypeArray $type): bool
{
do {
$type = $type->getSubType();
if ($type instanceof PropertyTypePrimitive) {
return true;
}
} while ($type instanceof PropertyTypeArray);
return false;
}
} }

View file

@ -0,0 +1,197 @@
<?php
/**
* PHP version 7.3
*
* @category BaseJMSParser
* @package RetailCrm\Api\Component\Serializer\Parser
*/
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser;
use ReflectionClass;
use RetailCrm\Api\Component\Serializer\Exception\SyntaxError;
/**
* Class Parser
*
* @package RetailCrm\Api\Component\Serializer\Parser
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @internal
*/
final class BaseJMSParser
{
/**
* @var \RetailCrm\Api\Component\Serializer\Parser\JMSLexer
*/
private $lexer;
/**
* @var bool
*/
private $root = true;
/**
* @param string $string
*
* @return array|mixed[]
*/
public function parse(string $string): array
{
$this->lexer = new JMSLexer();
$this->lexer->setInput($string);
$this->lexer->moveNext();
return $this->visit();
}
/**
* @return mixed
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
private function visit()
{
$this->lexer->moveNext();
if (!$this->lexer->token) {
throw new SyntaxError(
'Syntax error, unexpected end of stream'
);
}
if (JMSLexer::T_FLOAT === $this->lexer->token['type']) {
return (float)$this->lexer->token['value'];
} elseif (JMSLexer::T_INTEGER === $this->lexer->token['type']) {
return (int)$this->lexer->token['value'];
} elseif (JMSLexer::T_NULL === $this->lexer->token['type']) {
return null;
} elseif (JMSLexer::T_STRING === $this->lexer->token['type']) {
return $this->lexer->token['value'];
} elseif (JMSLexer::T_IDENTIFIER === $this->lexer->token['type']) {
if ($this->lexer->isNextToken(JMSLexer::T_TYPE_START)) {
return $this->visitCompoundType();
} elseif ($this->lexer->isNextToken(JMSLexer::T_ARRAY_START)) {
return $this->visitArrayType();
}
return $this->visitSimpleType();
} elseif (!$this->root && JMSLexer::T_ARRAY_START === $this->lexer->token['type']) {
return $this->visitArrayType();
}
throw new SyntaxError(sprintf(
'Syntax error, unexpected "%s" (%s)',
$this->lexer->token['value'],
// @phpstan-ignore-next-line
$this->getConstant($this->lexer->token['type'])
));
}
/**
* @return mixed[]
*/
private function visitSimpleType(): array
{
$value = $this->lexer->token['value']; // @phpstan-ignore-line
return ['name' => $value, 'params' => []];
}
/**
* @return array<string, mixed>
*/
private function visitCompoundType(): array
{
$this->root = false;
$name = $this->lexer->token['value']; // @phpstan-ignore-line
$this->match(JMSLexer::T_TYPE_START);
$params = [];
if (!$this->lexer->isNextToken(JMSLexer::T_TYPE_END)) {
while (true) {
$params[] = $this->visit();
if ($this->lexer->isNextToken(JMSLexer::T_TYPE_END)) {
break;
}
$this->match(JMSLexer::T_COMMA);
}
}
$this->match(JMSLexer::T_TYPE_END);
return [
'name' => $name,
'params' => $params,
];
}
/**
* @return array<int, mixed>
*/
private function visitArrayType(): array
{
/*
* Here we should call $this->match(JMSLexer::T_ARRAY_START); to make it clean
* but the token has already been consumed by moveNext() in visit()
*/
$params = [];
if (!$this->lexer->isNextToken(JMSLexer::T_ARRAY_END)) {
while (true) {
$params[] = $this->visit();
if ($this->lexer->isNextToken(JMSLexer::T_ARRAY_END)) {
break;
}
$this->match(JMSLexer::T_COMMA);
}
}
$this->match(JMSLexer::T_ARRAY_END);
return $params;
}
/**
* @param int $token
*/
private function match(int $token): void
{
if (!$this->lexer->lookahead) {
throw new SyntaxError(
sprintf('Syntax error, unexpected end of stream, expected %s', $this->getConstant($token))
);
}
if ($this->lexer->lookahead['type'] === $token) {
$this->lexer->moveNext();
return;
}
throw new SyntaxError(sprintf(
'Syntax error, unexpected "%s" (%s), expected was %s',
$this->lexer->lookahead['value'],
$this->getConstant($this->lexer->lookahead['type']), // @phpstan-ignore-line
$this->getConstant($token)
));
}
/**
* @param int $value
*
* @return string
*/
private function getConstant(int $value): string
{
$oClass = new ReflectionClass(JMSLexer::class);
return (string) array_search($value, $oClass->getConstants());
}
}

View file

@ -1,14 +0,0 @@
<?php
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Exception;
/**
* Base exception for the Serializer.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface Exception extends \Throwable
{
}

View file

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Exception;
use RetailCrm\Api\Component\Serializer\Parser\JMSCore\Exception\Exception as BaseException;
interface Exception extends BaseException
{
}

View file

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Exception;
final class InvalidNode extends \LogicException implements Exception
{
}

View file

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Exception;
final class SyntaxError extends \RuntimeException implements Exception
{
}

View file

@ -1,220 +0,0 @@
<?php
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type;
use RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Exception\SyntaxError;
/**
* @internal
*/
final class Parser implements ParserInterface
{
/** @var \RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Lexer */
private $lexer;
/** @var \RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Token|null */
private $token = null;
/** @var string */
private $input;
/** @var bool */
private $root = true;
public function parse(string $type): array
{
$this->input = $type;
$this->lexer = new Lexer();
$this->lexer->setInput($type);
$this->lexer->moveNext();
return $this->visit();
}
/**
* @return mixed
*/
private function visit(bool $fetchingParam = false)
{
$this->lexer->moveNext();
if (!$this->lexer->token) {
throw new SyntaxError(
'Syntax error, unexpected end of stream',
);
}
if (is_array($this->lexer->token)) {
$this->token = Token::fromArray($this->lexer->token);
} else {
$this->token = Token::fromObject($this->lexer->token);
}
/*
* There is a difference in the behavior of the tokenizer depending on the version of the lexer. This affects
* how the lexer interprets date formats for compound date types. In our case it has a huge impact because
* we're using `DateTime<'Y-m-d H:i:s'>` in the API. Newer lexer versions will treat the whole format
* (`Y-m-d H:i:s`) as a token of string type. Older lexer versions will treat each symbol of the format as
* a single token, and this will inevitably break type parsing during model compilation (it will say something
* about unexpected token).
*
* The condition below is used to work around this, albeit in a very hacky way. The token it tries to detect is
* actually a single quote ('). After detecting this token and determining that this time parser has been
* called recursively for a compound type, it will try to determine the parameter length and extract it as
* a single token. In other words, we're trying to mimic newer lexer behavior while using an older version.
*
* This implementation is very limited, as it may fail abruptly with more complex compound types, also it
* may fail to extract the type correctly if it has different whitespace symbols / multiple whitespace symbols.
* It was tested only with the compound date type mentioned above.
*
* A special fallback parser would be a better solution, but it would take much more time and effort to create.
*/
if ("" === $this->token->value && $fetchingParam) {
$len = 0;
$this->lexer->moveNext();
while (true) {
if (is_array($this->lexer->token)) {
$this->token = Token::fromArray($this->lexer->token);
} else {
$this->token = Token::fromObject($this->lexer->token);
}
if ("" === $this->token->value) {
$len++;
break;
}
$len += strlen($this->token->value);
$this->lexer->moveNext();
}
return substr($this->input, 9, $len + substr_count($this->input, ' '));
}
if (Lexer::T_FLOAT === $this->token->type) {
return floatval($this->token->value);
} elseif (Lexer::T_INTEGER === $this->token->type) {
return intval($this->token->value);
} elseif (Lexer::T_NULL === $this->token->type) {
return null;
} elseif (Lexer::T_STRING === $this->token->type) {
return $this->token->value;
} elseif (Lexer::T_IDENTIFIER === $this->token->type) {
if ($this->lexer->isNextToken(Lexer::T_TYPE_START)) {
return $this->visitCompoundType();
} elseif ($this->lexer->isNextToken(Lexer::T_ARRAY_START)) {
return $this->visitArrayType();
}
return $this->visitSimpleType();
} elseif (!$this->root && Lexer::T_ARRAY_START === $this->token->type) {
return $this->visitArrayType();
}
throw new SyntaxError(sprintf(
'Syntax error, unexpected "%s" (%s)',
$this->token->value,
$this->getConstant($this->token->type),
));
}
/**
* @return string|mixed[]
*/
private function visitSimpleType()
{
$value = $this->token->value;
return ['name' => $value, 'params' => []];
}
private function visitCompoundType(): array
{
$this->root = false;
$name = $this->token->value;
$this->match(Lexer::T_TYPE_START);
$params = [];
if (!$this->lexer->isNextToken(Lexer::T_TYPE_END)) {
while (true) {
$params[] = $this->visit(true);
if ($this->lexer->isNextToken(Lexer::T_TYPE_END)) {
break;
}
$this->match(Lexer::T_COMMA);
}
}
$this->match(Lexer::T_TYPE_END);
return [
'name' => $name,
'params' => $params,
];
}
private function visitArrayType(): array
{
/*
* Here we should call $this->match(Lexer::T_ARRAY_START); to make it clean
* but the token has already been consumed by moveNext() in visit()
*/
$params = [];
if (!$this->lexer->isNextToken(Lexer::T_ARRAY_END)) {
while (true) {
$params[] = $this->visit();
if ($this->lexer->isNextToken(Lexer::T_ARRAY_END)) {
break;
}
$this->match(Lexer::T_COMMA);
}
}
$this->match(Lexer::T_ARRAY_END);
return $params;
}
private function match(int $token): void
{
if (!$this->lexer->lookahead) {
throw new SyntaxError(
sprintf('Syntax error, unexpected end of stream, expected %s', $this->getConstant($token)),
);
}
if (is_array($this->lexer->lookahead)) {
$lookahead = Token::fromArray($this->lexer->lookahead);
} else {
$lookahead = Token::fromObject($this->lexer->lookahead);
}
if ($lookahead->type === $token) {
$this->lexer->moveNext();
return;
}
throw new SyntaxError(sprintf(
'Syntax error, unexpected "%s" (%s), expected was %s',
$lookahead->value,
$this->getConstant($lookahead->type),
$this->getConstant($token),
));
}
private function getConstant(int $value): string
{
$oClass = new \ReflectionClass(Lexer::class);
return array_search($value, $oClass->getConstants());
}
}

View file

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type;
interface ParserInterface
{
public function parse(string $type): array;
}

View file

@ -1,66 +0,0 @@
<?php
declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type;
use function in_array;
/**
* @template T of string|int
* @template V of string|int
*/
final class Token
{
public static function fromArray(array $source): Token
{
return new self($source['value'] ?? '', $source['type'] ?? '', $source['position'] ?? '');
}
public static function fromObject(object $source): Token
{
return new self($source->value, $source->type, $source->position);
}
/**
* The string value of the token in the input string
*
* @readonly
* @var string|int
*/
public $value;
/**
* The type of the token (identifier, numeric, string, input parameter, none)
*
* @readonly
* @var T|null
*/
public $type;
/**
* The position of the token in the input string
*
* @var int
*
* @readonly
*/
public $position;
/**
* @param string|int $value
* @param string|int $type
*/
public function __construct($value, $type, int $position)
{
$this->value = $value;
$this->type = $type;
$this->position = $position;
}
/** @param T ...$types */
public function isA(...$types): bool
{
return in_array($this->type, $types, true);
}
}

View file

@ -1,16 +1,24 @@
<?php <?php
/**
* PHP version 7.3
*
* @category BaseJMSParser
* @package RetailCrm\Api\Component\Serializer\Parser
*/
declare(strict_types=1); declare(strict_types=1);
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type; namespace RetailCrm\Api\Component\Serializer\Parser;
use Doctrine\Common\Lexer\AbstractLexer; use Doctrine\Common\Lexer\AbstractLexer;
use RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Exception\SyntaxError; use RetailCrm\Api\Component\Serializer\Exception\SyntaxError;
use Throwable;
/** /**
* @internal * @internal
*/ */
final class Lexer extends AbstractLexer final class JMSLexer extends AbstractLexer
{ {
public const T_UNKNOWN = 0; public const T_UNKNOWN = 0;
public const T_INTEGER = 1; public const T_INTEGER = 1;
@ -24,15 +32,23 @@ final class Lexer extends AbstractLexer
public const T_IDENTIFIER = 9; public const T_IDENTIFIER = 9;
public const T_NULL = 10; public const T_NULL = 10;
/**
* @param string $type
*
* @return int|string|null
*/
public function parse(string $type) public function parse(string $type)
{ {
try { try {
return $this->getType($type); return $this->getType($type);
} catch (\Throwable $e) { } catch (Throwable $e) {
throw new SyntaxError($e->getMessage(), 0, $e); throw new SyntaxError($e->getMessage(), 0, $e);
} }
} }
/**
* @return string[]
*/
protected function getCatchablePatterns(): array protected function getCatchablePatterns(): array
{ {
return [ return [
@ -47,15 +63,18 @@ final class Lexer extends AbstractLexer
]; ];
} }
/**
* @return string[]
*/
protected function getNonCatchablePatterns(): array protected function getNonCatchablePatterns(): array
{ {
return ['\s+']; return ['\s+'];
} }
/** /**
* {@inheritDoc} * {{@inheritDoc}}
* *
* @return int|string|null * @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
protected function getType(&$value) protected function getType(&$value)
{ {

View file

@ -1,18 +1,21 @@
<?php <?php
declare(strict_types=1); /**
* PHP version 7.3
*
* @category JMSParser
* @package RetailCrm\Api\Component\Serializer\Parser
*/
namespace RetailCrm\Api\Component\Serializer\Parser; namespace RetailCrm\Api\Component\Serializer\Parser;
use Doctrine\Common\Annotations\AnnotationException; use Doctrine\Common\Annotations\AnnotationException;
use Doctrine\Common\Annotations\Reader; use Doctrine\Common\Annotations\Reader;
use Liip\MetadataParser\ModelParser\ModelParserInterface;
use RetailCrm\Api\Component\Serializer\Annotation\Accessor; use RetailCrm\Api\Component\Serializer\Annotation\Accessor;
use RetailCrm\Api\Component\Serializer\Annotation\AccessorOrder; use RetailCrm\Api\Component\Serializer\Annotation\AccessorOrder;
use RetailCrm\Api\Component\Serializer\Annotation\Exclude; use RetailCrm\Api\Component\Serializer\Annotation\Exclude;
use RetailCrm\Api\Component\Serializer\Annotation\ExclusionPolicy; use RetailCrm\Api\Component\Serializer\Annotation\ExclusionPolicy;
use RetailCrm\Api\Component\Serializer\Annotation\Groups; use RetailCrm\Api\Component\Serializer\Annotation\Groups;
use RetailCrm\Api\Component\Serializer\Annotation\MaxDepth;
use RetailCrm\Api\Component\Serializer\Annotation\PostDeserialize; use RetailCrm\Api\Component\Serializer\Annotation\PostDeserialize;
use RetailCrm\Api\Component\Serializer\Annotation\SerializedName; use RetailCrm\Api\Component\Serializer\Annotation\SerializedName;
use RetailCrm\Api\Component\Serializer\Annotation\Since; use RetailCrm\Api\Component\Serializer\Annotation\Since;
@ -24,32 +27,54 @@ use Liip\MetadataParser\Exception\ParseException;
use Liip\MetadataParser\Metadata\PropertyAccessor; use Liip\MetadataParser\Metadata\PropertyAccessor;
use Liip\MetadataParser\Metadata\PropertyType; use Liip\MetadataParser\Metadata\PropertyType;
use Liip\MetadataParser\Metadata\PropertyTypeUnknown; use Liip\MetadataParser\Metadata\PropertyTypeUnknown;
use Liip\MetadataParser\ModelParser\ModelParserInterface;
use Liip\MetadataParser\ModelParser\RawMetadata\PropertyCollection; use Liip\MetadataParser\ModelParser\RawMetadata\PropertyCollection;
use Liip\MetadataParser\ModelParser\RawMetadata\PropertyVariationMetadata; use Liip\MetadataParser\ModelParser\RawMetadata\PropertyVariationMetadata;
use Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata; use Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata;
use Liip\MetadataParser\TypeParser\PhpTypeParser; use Liip\MetadataParser\TypeParser\PhpTypeParser;
use RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Exception\SyntaxError; use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use ReflectionProperty;
use UnexpectedValueException;
/** /**
* Parse JMSSerializer annotations. * Class JMSParser
*
* Run this parser *after* the PHPDoc parser as JMS annotations are more precise.
* *
* @category JMSParser
* @package RetailCrm\Api\Component\Serializer\Parser
* @license https://github.com/liip/metadata-parser/blob/master/LICENSE MIT License
* @author Liip <https://github.com/liip>
* @author Pavel Kovalenko
* @see https://github.com/liip/metadata-parser
* @internal * @internal
*
* @SuppressWarnings(PHPMD)
*/ */
class JMSParser implements ModelParserInterface class JMSParser implements ModelParserInterface
{ {
private const ACCESS_ORDER_CUSTOM = 'custom'; private const ACCESS_ORDER_CUSTOM = 'custom';
/** @var \Doctrine\Common\Annotations\Reader */ /**
* @var Reader
*/
private $annotationsReader; private $annotationsReader;
/** @var \Liip\MetadataParser\TypeParser\PhpTypeParser */ /**
* @var PhpTypeParser
*/
private $phpTypeParser; private $phpTypeParser;
/** @var \RetailCrm\Api\Component\Serializer\Parser\JMSTypeParser */ /**
protected $jmsTypeParser; * @var JMSTypeParser
*/
private $jmsTypeParser;
/**
* JMSParser constructor.
*
* @param \Doctrine\Common\Annotations\Reader $annotationsReader
*/
public function __construct(Reader $annotationsReader) public function __construct(Reader $annotationsReader)
{ {
$this->annotationsReader = $annotationsReader; $this->annotationsReader = $annotationsReader;
@ -57,99 +82,124 @@ class JMSParser implements ModelParserInterface
$this->jmsTypeParser = new JMSTypeParser(); $this->jmsTypeParser = new JMSTypeParser();
} }
/**
* @param \Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata $classMetadata
*/
public function parse(RawClassMetadata $classMetadata): void public function parse(RawClassMetadata $classMetadata): void
{ {
try { try {
$reflClass = new \ReflectionClass($classMetadata->getClassName()); $refClass = new ReflectionClass($classMetadata->getClassName()); // @phpstan-ignore-line
} catch (\ReflectionException $e) { } catch (ReflectionException $exception) {
throw ParseException::classNotFound($classMetadata->getClassName(), $e); throw ParseException::classNotFound($classMetadata->getClassName(), $exception);
} }
try { $this->parseProperties($refClass, $classMetadata);
$this->parseProperties($reflClass, $classMetadata); $this->parseMethods($refClass, $classMetadata);
$this->parseMethods($reflClass, $classMetadata); $this->parseClass($refClass, $classMetadata);
$this->parseClass($reflClass, $classMetadata);
} catch (SyntaxError $exception) {
throw new ParseException($exception->getMessage(), $exception->getCode(), $exception);
}
} }
private function parseProperties(\ReflectionClass $reflClass, RawClassMetadata $classMetadata): void /**
* @param \ReflectionClass $refClass
* @param \Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata $classMetadata
*
* @phpstan-ignore-next-line
*/
private function parseProperties(ReflectionClass $refClass, RawClassMetadata $classMetadata): void
{ {
if ($reflParentClass = $reflClass->getParentClass()) { if ($refParentClass = $refClass->getParentClass()) {
$this->parseProperties($reflParentClass, $classMetadata); $this->parseProperties($refParentClass, $classMetadata);
} }
foreach ($reflClass->getProperties() as $reflProperty) { foreach ($refClass->getProperties() as $refProperty) {
try { try {
$annotations = $this->annotationsReader->getPropertyAnnotations($reflProperty); $annotations = $this->annotationsReader->getPropertyAnnotations($refProperty);
} catch (AnnotationException $e) { } catch (AnnotationException $exception) {
throw ParseException::propertyError((string) $classMetadata, $reflProperty->getName(), $e); throw ParseException::propertyError((string) $classMetadata, $refProperty->getName(), $exception);
} }
$property = $this->getProperty($classMetadata, $reflProperty, $annotations); $property = $this->getProperty($classMetadata, $refProperty, $annotations);
$this->parsePropertyAnnotations($classMetadata, $property, $annotations); $this->parsePropertyAnnotations($classMetadata, $property, $annotations);
} }
} }
private function parseMethods(\ReflectionClass $reflClass, RawClassMetadata $classMetadata): void /**
* @param \ReflectionClass $refClass
* @param \Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata $classMetadata
*
* @phpstan-ignore-next-line
*/
private function parseMethods(ReflectionClass $refClass, RawClassMetadata $classMetadata): void
{ {
if ($reflParentClass = $reflClass->getParentClass()) { if ($refParentClass = $refClass->getParentClass()) {
$this->parseMethods($reflParentClass, $classMetadata); $this->parseMethods($refParentClass, $classMetadata);
} }
foreach ($reflClass->getMethods() as $reflMethod) { foreach ($refClass->getMethods() as $refMethod) {
if (false === $refMethod->getDocComment()) {
continue;
}
try { try {
$annotations = $this->annotationsReader->getMethodAnnotations($reflMethod); $annotations = $this->annotationsReader->getMethodAnnotations($refMethod);
} catch (AnnotationException $e) { } catch (AnnotationException $exception) {
throw ParseException::propertyError((string) $classMetadata, $reflMethod->getName(), $e); throw ParseException::propertyError((string) $classMetadata, $refMethod->getName(), $exception);
} }
if ($this->isVirtualProperty($annotations)) { if ($this->isVirtualProperty($annotations)) {
if (!$reflMethod->isPublic()) { if (!$refMethod->isPublic()) {
throw ParseException::nonPublicMethod((string) $classMetadata, $reflMethod->getName()); throw ParseException::nonPublicMethod((string) $classMetadata, $refMethod->getName());
} }
$methodName = $this->getMethodName($annotations, $reflMethod); $methodName = $this->getMethodName($annotations, $refMethod);
$name = $this->getSerializedName($annotations) ?: $methodName; $name = $this->getSerializedName($annotations) ?: $methodName;
$property = new PropertyVariationMetadata($methodName, true, true); $property = new PropertyVariationMetadata($methodName, true, true);
$classMetadata->addPropertyVariation($name, $property); $classMetadata->addPropertyVariation($name, $property);
$property->setType($this->getReturnType($property, $reflMethod, $reflClass)); $property->setType($this->getReturnType($property, $refMethod, $refClass));
$property->setAccessor(new PropertyAccessor($reflMethod->getName(), null)); $property->setAccessor(new PropertyAccessor($refMethod->getName(), null));
$this->parsePropertyAnnotations($classMetadata, $property, $annotations); $this->parsePropertyAnnotations($classMetadata, $property, $annotations);
} }
if ($this->isPostDeserializeMethod($annotations)) { if ($this->isPostDeserializeMethod($annotations)) {
if (!$reflMethod->isPublic()) { if (!$refMethod->isPublic()) {
throw ParseException::nonPublicMethod((string) $classMetadata, $reflMethod->getName()); throw ParseException::nonPublicMethod((string) $classMetadata, $refMethod->getName());
} }
$classMetadata->addPostDeserializeMethod($reflMethod->getName()); $classMetadata->addPostDeserializeMethod($refMethod->getName());
} }
} }
} }
private function parseClass(\ReflectionClass $reflClass, RawClassMetadata $classMetadata): void /**
* @param \ReflectionClass $refClass
* @param \Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata $classMetadata
*
* @phpstan-ignore-next-line
*/
private function parseClass(ReflectionClass $refClass, RawClassMetadata $classMetadata): void
{ {
try { try {
$annotations = $this->gatherClassAnnotations($reflClass); $annotations = $this->gatherClassAnnotations($refClass);
} catch (AnnotationException $e) { } catch (AnnotationException $e) {
throw ParseException::classError($reflClass->getName(), $e); throw ParseException::classError($refClass->getName(), $e);
} }
foreach ($annotations as $annotation) { foreach ($annotations as $annotation) {
switch (true) { switch (true) {
case $annotation instanceof AccessorOrder: case $annotation instanceof AccessorOrder:
if (self::ACCESS_ORDER_CUSTOM !== $annotation->order) { if (self::ACCESS_ORDER_CUSTOM !== $annotation->order) {
throw ParseException::unsupportedClassAnnotation((string) $classMetadata, 'AccessorOrder::'.$annotation->order); throw ParseException::unsupportedClassAnnotation(
(string) $classMetadata,
'AccessorOrder::' . $annotation->order
);
} }
// usort is not stable for the same result. we want to preserve order of the fields that are not explicitly mentioned // usort is not stable for the same result. we want to preserve order of
// the fields that are not explicitly mentioned
$order = []; $order = [];
$init = \count($annotation->custom); $init = count($annotation->custom);
foreach ($classMetadata->getPropertyCollections() as $property) { foreach ($classMetadata->getPropertyCollections() as $property) {
$position = $property->getPosition($annotation->custom); $position = $property->getPosition($annotation->custom);
if (null === $position) { if (null === $position) {
@ -158,17 +208,21 @@ class JMSParser implements ModelParserInterface
$order[$property->getSerializedName()] = $position; $order[$property->getSerializedName()] = $position;
} }
$classMetadata->sortProperties(static function (PropertyCollection $propA, PropertyCollection $propB) use ($order): int { $classMetadata->sortProperties(static function (
PropertyCollection $propA,
PropertyCollection $propB
) use ($order): int {
return $order[$propA->getSerializedName()] <=> $order[$propB->getSerializedName()]; return $order[$propA->getSerializedName()] <=> $order[$propB->getSerializedName()];
}); });
break; break;
case $annotation instanceof ExclusionPolicy: case $annotation instanceof ExclusionPolicy:
if (ExclusionPolicy::NONE !== $annotation->policy) { if (ExclusionPolicy::NONE !== $annotation->policy) {
throw ParseException::unsupportedClassAnnotation((string) $classMetadata, 'ExclusionPolicy::'.$annotation->policy); throw ParseException::unsupportedClassAnnotation(
(string) $classMetadata,
'ExclusionPolicy::' . $annotation->policy
);
} }
break; break;
default: default:
if ( if (
0 === strncmp( 0 === strncmp(
@ -178,7 +232,10 @@ class JMSParser implements ModelParserInterface
) )
) { ) {
// if there are annotations we can safely ignore, we need to explicitly ignore them // if there are annotations we can safely ignore, we need to explicitly ignore them
throw ParseException::unsupportedClassAnnotation((string) $classMetadata, \get_class($annotation)); throw ParseException::unsupportedClassAnnotation(
(string) $classMetadata,
get_class($annotation)
);
} }
} }
} }
@ -187,36 +244,51 @@ class JMSParser implements ModelParserInterface
/** /**
* Find the annotations we care about by looking through all ancestors of $reflectionClass. * Find the annotations we care about by looking through all ancestors of $reflectionClass.
* *
* @return object[] Hashmap of annotation class => annotation object * @param \ReflectionClass $reflectionClass
* *
* @throws AnnotationException * @return object[] Hashmap of annotation class => annotation object
* @throws \Doctrine\Common\Annotations\AnnotationException
*
* @phpstan-ignore-next-line
*/ */
private function gatherClassAnnotations(\ReflectionClass $reflectionClass): array private function gatherClassAnnotations(ReflectionClass $reflectionClass): array
{ {
$map = []; $map = [];
if ($parent = $reflectionClass->getParentClass()) { if ($parent = $reflectionClass->getParentClass()) {
$map = $this->gatherClassAnnotations($parent); $map = $this->gatherClassAnnotations($parent);
} }
$annotations = $this->annotationsReader->getClassAnnotations($reflectionClass); $annotations = $this->annotationsReader->getClassAnnotations($reflectionClass);
foreach ($annotations as $annotation) { foreach ($annotations as $annotation) {
$map[\get_class($annotation)] = $annotation; $map[get_class($annotation)] = $annotation;
} }
return $map; return $map;
} }
private function parsePropertyAnnotations(RawClassMetadata $classMetadata, PropertyVariationMetadata $property, array $annotations): void /**
{ * @param \Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata $classMetadata
* @param \Liip\MetadataParser\ModelParser\RawMetadata\PropertyVariationMetadata $property
* @param array<mixed> $annotations
*/
private function parsePropertyAnnotations(
RawClassMetadata $classMetadata,
PropertyVariationMetadata $property,
array $annotations
): void {
foreach ($annotations as $annotation) { foreach ($annotations as $annotation) {
switch (true) { switch (true) {
case $annotation instanceof Type: case $annotation instanceof Type:
if (null === $annotation->name) {
throw ParseException::propertyTypeNameNull((string) $classMetadata, (string) $property);
}
try { try {
$type = $this->jmsTypeParser->parse($annotation->name); $type = $this->jmsTypeParser->parse($annotation->name);
} catch (InvalidTypeException $e) { } catch (InvalidTypeException $exception) {
throw ParseException::propertyTypeError((string) $classMetadata, (string) $property, $e); throw ParseException::propertyTypeError(
(string) $classMetadata,
(string) $property,
$exception
);
} }
if ($property->getType() instanceof PropertyTypeUnknown) { if ($property->getType() instanceof PropertyTypeUnknown) {
@ -224,49 +296,52 @@ class JMSParser implements ModelParserInterface
} else { } else {
try { try {
$property->setType($property->getType()->merge($type)); $property->setType($property->getType()->merge($type));
} catch (\UnexpectedValueException $e) { } catch (UnexpectedValueException $exception) {
throw ParseException::propertyTypeConflict((string) $classMetadata, (string) $property, (string) $property->getType(), (string) $type, $e); throw ParseException::propertyTypeConflict(
(string) $classMetadata,
(string) $property,
(string) $property->getType(),
(string) $type,
$exception
);
} }
} }
break; break;
case $annotation instanceof Exclude: case $annotation instanceof Exclude:
if (null !== $annotation->if) { if (null !== $annotation->if) {
throw ParseException::unsupportedPropertyAnnotation((string) $classMetadata, (string) $property, 'Exclude::if'); throw ParseException::unsupportedPropertyAnnotation(
(string) $classMetadata,
(string) $property,
'Exclude::if'
);
} }
$classMetadata->removePropertyVariation((string) $property); $classMetadata->removePropertyVariation((string) $property);
break; break;
case $annotation instanceof Groups: case $annotation instanceof Groups:
$property->setGroups($annotation->groups); $property->setGroups($annotation->groups);
break; break;
case $annotation instanceof Accessor: case $annotation instanceof Accessor:
$property->setAccessor(new PropertyAccessor($annotation->getter, $annotation->setter)); $property->setAccessor(new PropertyAccessor($annotation->getter, $annotation->setter));
break; break;
case $annotation instanceof Since: case $annotation instanceof Since:
$property->setVersionRange($property->getVersionRange()->withSince($annotation->version)); $property->setVersionRange($property->getVersionRange()->withSince($annotation->version));
break; break;
case $annotation instanceof Until: case $annotation instanceof Until:
$property->setVersionRange($property->getVersionRange()->withUntil($annotation->version)); $property->setVersionRange($property->getVersionRange()->withUntil($annotation->version));
break; break;
case $annotation instanceof MaxDepth:
$property->setMaxDepth($annotation->depth);
break;
case $annotation instanceof VirtualProperty:
// we handle this separately
case $annotation instanceof SerializedName: case $annotation instanceof SerializedName:
// we handle this separately // we handle this separately
case $annotation instanceof VirtualProperty:
// we handle this separately
break; break;
default: default:
if (0 === strncmp('JMS\Serializer\\', \get_class($annotation), mb_strlen('JMS\Serializer\\'))) { if (0 === strncmp('JMS\Serializer\\', get_class($annotation), mb_strlen('JMS\Serializer\\'))) {
// if there are annotations we can safely ignore, we need to explicitly ignore them // if there are annotations we can safely ignore, we need to explicitly ignore them
throw ParseException::unsupportedPropertyAnnotation((string) $classMetadata, (string) $property, \get_class($annotation)); throw ParseException::unsupportedPropertyAnnotation(
(string) $classMetadata,
(string) $property,
get_class($annotation)
);
} }
break; break;
} }
@ -277,38 +352,96 @@ class JMSParser implements ModelParserInterface
* Returns the property metadata for the specified property. * Returns the property metadata for the specified property.
* *
* If the property already exists on the class metadata this is returned. * If the property already exists on the class metadata this is returned.
* If the property has a serialized name that overrides the name of an existing property, it will be renamed and merged. * If the property has a serialized name that overrides the name of an existing property,
* it will be renamed and merged.
*
* @param \Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata $classMetadata
* @param \ReflectionProperty $refProperty
* @param array<mixed> $annotations
*
* @return \Liip\MetadataParser\ModelParser\RawMetadata\PropertyVariationMetadata
* @throws \ReflectionException
*/ */
private function getProperty(RawClassMetadata $classMetadata, \ReflectionProperty $reflProperty, array $annotations): PropertyVariationMetadata private function getProperty(
{ RawClassMetadata $classMetadata,
$defaultName = PropertyCollection::serializedName($reflProperty->getName()); ReflectionProperty $refProperty,
array $annotations
): PropertyVariationMetadata {
$defaultName = PropertyCollection::serializedName($refProperty->getName());
$name = $this->getSerializedName($annotations) ?: $defaultName; $name = $this->getSerializedName($annotations) ?: $defaultName;
if ($classMetadata->hasPropertyVariation($reflProperty->getName())) {
$property = $classMetadata->getPropertyVariation($reflProperty->getName()); if ($classMetadata->hasPropertyVariation($refProperty->getName())) {
$property = $classMetadata->getPropertyVariation($refProperty->getName());
if ($defaultName !== $name && $classMetadata->hasPropertyCollection($defaultName)) { if ($defaultName !== $name && $classMetadata->hasPropertyCollection($defaultName)) {
$classMetadata->renameProperty($defaultName, $name); $classMetadata->removePropertyVariation($defaultName);
$this->addPropertyVariation($defaultName, $name, $property, $classMetadata);
} }
} else { } else {
$property = PropertyVariationMetadata::fromReflection($reflProperty); $property = PropertyVariationMetadata::fromReflection($refProperty);
$classMetadata->addPropertyVariation($name, $property); $this->addPropertyVariation($defaultName, $name, $property, $classMetadata);
} }
return $property; return $property;
} }
private function getReturnType(PropertyVariationMetadata $property, \ReflectionMethod $reflMethod, \ReflectionClass $reflClass): PropertyType /**
{ * This workaround helps to avoid unnecessary camelCase to snake_case conversion while
$type = new PropertyTypeUnknown(true); * using default property metadata classes. This allows us to produce code we expect
* without rewriting the whole metadata parsing library.
*
* @param string $defaultName
* @param string $name
* @param \Liip\MetadataParser\ModelParser\RawMetadata\PropertyVariationMetadata $property
* @param \Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata $classMetadata
*
* @throws \ReflectionException
*/
private function addPropertyVariation(
string $defaultName,
string $name,
PropertyVariationMetadata $property,
RawClassMetadata $classMetadata
): void {
if ($classMetadata->hasPropertyCollection($defaultName)) {
$prop = $classMetadata->getPropertyCollection($defaultName);
} else {
$prop = new PropertyCollection($name);
$classMetadata->addPropertyCollection($prop);
}
$reflType = $reflMethod->getReturnType(); $propName = new ReflectionProperty(get_class($prop), 'serializedName');
if (null !== $reflType) { $propName->setAccessible(true);
$type = $this->phpTypeParser->parseReflectionType($reflType); $propName->setValue($prop, $name);
$prop->addVariation($property);
}
/**
* @param \Liip\MetadataParser\ModelParser\RawMetadata\PropertyVariationMetadata $property
* @param \ReflectionMethod $refMethod
* @param \ReflectionClass $refClass
*
* @return \Liip\MetadataParser\Metadata\PropertyType
*
* @phpstan-ignore-next-line
*/
private function getReturnType(
PropertyVariationMetadata $property,
ReflectionMethod $refMethod,
ReflectionClass $refClass
): PropertyType {
$type = new PropertyTypeUnknown(true);
$refType = $refMethod->getReturnType();
if (null !== $refType) {
$type = $this->phpTypeParser->parseReflectionType($refType);
} }
try { try {
$docBlockType = $this->getReturnTypeOfMethod($reflMethod); $docBlockType = $this->getReturnTypeOfMethod($refMethod, $refClass);
} catch (InvalidTypeException $e) { } catch (InvalidTypeException $exception) {
throw ParseException::propertyTypeError($reflClass->getName(), (string) $property, $e); throw ParseException::propertyTypeError($refClass->getName(), (string) $property, $exception);
} }
if (null === $docBlockType) { if (null === $docBlockType) {
@ -317,27 +450,47 @@ class JMSParser implements ModelParserInterface
try { try {
return $type->merge($docBlockType); return $type->merge($docBlockType);
} catch (\UnexpectedValueException $e) { } catch (UnexpectedValueException $exception) {
throw ParseException::propertyTypeConflict($reflClass->getName(), (string) $property, (string) $type, (string) $docBlockType, $e); throw ParseException::propertyTypeConflict(
$refClass->getName(),
(string) $property,
(string) $type,
(string) $docBlockType,
$exception
);
} }
} }
private function getReturnTypeOfMethod(\ReflectionMethod $reflMethod): ?PropertyType /**
* @param \ReflectionMethod $refMethod
* @param \ReflectionClass $refClass
*
* @return \Liip\MetadataParser\Metadata\PropertyType|null
*
* @phpstan-ignore-next-line
*/
private function getReturnTypeOfMethod(ReflectionMethod $refMethod, ReflectionClass $refClass): ?PropertyType
{ {
$docComment = $reflMethod->getDocComment(); $docComment = $refMethod->getDocComment();
if (false === $docComment) { if (false === $docComment) {
return null; return null;
} }
foreach (explode("\n", $docComment) as $line) { foreach (explode("\n", $docComment) as $line) {
if (1 === preg_match('/@return ([^ ]+)/', $line, $matches)) { if (1 === preg_match('/@return ([^ ]+)/', $line, $matches)) {
return $this->phpTypeParser->parseAnnotationType($matches[1], $reflMethod->getDeclaringClass()); return $this->phpTypeParser->parseAnnotationType($matches[1], $refClass);
} }
} }
return null; return null;
} }
/**
* @param array<mixed> $annotations
*
* @return string|null
*/
private function getSerializedName(array $annotations): ?string private function getSerializedName(array $annotations): ?string
{ {
foreach ($annotations as $annotation) { foreach ($annotations as $annotation) {
@ -349,6 +502,11 @@ class JMSParser implements ModelParserInterface
return null; return null;
} }
/**
* @param array<mixed> $annotations
*
* @return bool
*/
private function isVirtualProperty(array $annotations): bool private function isVirtualProperty(array $annotations): bool
{ {
foreach ($annotations as $annotation) { foreach ($annotations as $annotation) {
@ -360,6 +518,11 @@ class JMSParser implements ModelParserInterface
return false; return false;
} }
/**
* @param array<mixed> $annotations
*
* @return bool
*/
private function isPostDeserializeMethod(array $annotations): bool private function isPostDeserializeMethod(array $annotations): bool
{ {
foreach ($annotations as $annotation) { foreach ($annotations as $annotation) {
@ -371,9 +534,16 @@ class JMSParser implements ModelParserInterface
return false; return false;
} }
private function getMethodName(array $annotations, \ReflectionMethod $reflMethod): string /**
* @param array<mixed> $annotations
* @param \ReflectionMethod $refMethod
*
* @return string
*/
private function getMethodName(array $annotations, ReflectionMethod $refMethod): string
{ {
$name = $reflMethod->getName(); $name = $refMethod->getName();
foreach ($annotations as $annotation) { foreach ($annotations as $annotation) {
if ($annotation instanceof VirtualProperty && null !== $annotation->name) { if ($annotation instanceof VirtualProperty && null !== $annotation->name) {
$name = $annotation->name; $name = $annotation->name;

View file

@ -1,42 +1,59 @@
<?php <?php
declare(strict_types=1); /**
* PHP version 7.3
*
* @category JMSTypeParser
* @package RetailCrm\Api\Component\Serializer\Parser
*/
namespace RetailCrm\Api\Component\Serializer\Parser; namespace RetailCrm\Api\Component\Serializer\Parser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Liip\MetadataParser\Exception\InvalidTypeException; use Liip\MetadataParser\Exception\InvalidTypeException;
use Liip\MetadataParser\Metadata\AbstractPropertyType;
use Liip\MetadataParser\Metadata\DateTimeOptions; use Liip\MetadataParser\Metadata\DateTimeOptions;
use Liip\MetadataParser\Metadata\PropertyType; use Liip\MetadataParser\Metadata\PropertyType;
use Liip\MetadataParser\Metadata\PropertyTypeArray;
use Liip\MetadataParser\Metadata\PropertyTypeClass; use Liip\MetadataParser\Metadata\PropertyTypeClass;
use Liip\MetadataParser\Metadata\PropertyTypeDateTime; use Liip\MetadataParser\Metadata\PropertyTypeDateTime;
use Liip\MetadataParser\Metadata\PropertyTypeIterable;
use Liip\MetadataParser\Metadata\PropertyTypeArray;
use Liip\MetadataParser\Metadata\PropertyTypePrimitive; use Liip\MetadataParser\Metadata\PropertyTypePrimitive;
use Liip\MetadataParser\Metadata\PropertyTypeUnknown; use Liip\MetadataParser\Metadata\PropertyTypeUnknown;
use RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Parser;
use RetailCrm\Api\Component\Serializer\Type\PropertyTypeMixed; use RetailCrm\Api\Component\Serializer\Type\PropertyTypeMixed;
final class JMSTypeParser /**
* Class JMSTypeParser
*
* @category JMSTypeParser
* @package RetailCrm\Api\Component\Serializer\Parser
* @license https://github.com/liip/metadata-parser/blob/master/LICENSE MIT License
* @author Liip <https://github.com/liip>
* @author Pavel Kovalenko
* @see https://github.com/liip/metadata-parser
* @internal
*
* @SuppressWarnings(PHPMD)
*/
class JMSTypeParser
{ {
private const TYPE_ARRAY = 'array'; private const TYPE_ARRAY = 'array';
private const TYPE_ARRAY_COLLECTION = 'ArrayCollection';
private const TYPE_DATETIME_INTERFACE = 'DateTimeInterface';
/** @var \RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Parser */ /**
* @var \RetailCrm\Api\Component\Serializer\Parser\BaseJMSParser
*/
private $jmsTypeParser; private $jmsTypeParser;
private $useArrayDateFormat; /**
* JMSTypeParser constructor.
*/
public function __construct() public function __construct()
{ {
$this->jmsTypeParser = new Parser(); $this->jmsTypeParser = new BaseJMSParser();
$this->useArrayDateFormat = null === (new \ReflectionClass(DateTimeOptions::class))
->getConstructor()->getParameters()[2]->getType();
} }
/**
* @param string $rawType
*
* @return \Liip\MetadataParser\Metadata\PropertyType
*/
public function parse(string $rawType): PropertyType public function parse(string $rawType): PropertyType
{ {
if ('' === $rawType) { if ('' === $rawType) {
@ -46,6 +63,12 @@ final class JMSTypeParser
return $this->parseType($this->jmsTypeParser->parse($rawType)); return $this->parseType($this->jmsTypeParser->parse($rawType));
} }
/**
* @param array<mixed> $typeInfo
* @param bool $isSubType
*
* @return \Liip\MetadataParser\Metadata\PropertyType
*/
private function parseType(array $typeInfo, bool $isSubType = false): PropertyType private function parseType(array $typeInfo, bool $isSubType = false): PropertyType
{ {
$typeInfo = array_merge( $typeInfo = array_merge(
@ -61,12 +84,13 @@ final class JMSTypeParser
if (0 === \count($typeInfo['params'])) { if (0 === \count($typeInfo['params'])) {
if (self::TYPE_ARRAY === $typeInfo['name']) { if (self::TYPE_ARRAY === $typeInfo['name']) {
return self::iterableType(new PropertyTypeUnknown(false), false, $nullable); return new PropertyTypeArray(new PropertyTypeUnknown(false), false, $nullable);
} }
if (PropertyTypePrimitive::isTypePrimitive($typeInfo['name'])) { if (PropertyTypePrimitive::isTypePrimitive($typeInfo['name'])) {
return new PropertyTypePrimitive($typeInfo['name'], $nullable); return new PropertyTypePrimitive($typeInfo['name'], $nullable);
} }
if (PropertyTypeDateTime::isTypeDateTime($typeInfo['name'])) { if (PropertyTypeDateTime::isTypeDateTime($typeInfo['name'])) {
return PropertyTypeDateTime::fromDateTimeClass($typeInfo['name'], $nullable); return PropertyTypeDateTime::fromDateTimeClass($typeInfo['name'], $nullable);
} }
@ -78,62 +102,41 @@ final class JMSTypeParser
return new PropertyTypeClass($typeInfo['name'], $nullable); return new PropertyTypeClass($typeInfo['name'], $nullable);
} }
$collectionClass = $this->getCollectionClass($typeInfo['name']); if (self::TYPE_ARRAY === $typeInfo['name']) {
if (self::TYPE_ARRAY === $typeInfo['name'] || $collectionClass) {
if (1 === \count($typeInfo['params'])) { if (1 === \count($typeInfo['params'])) {
return self::iterableType($this->parseType($typeInfo['params'][0], true), false, $nullable, $collectionClass); return new PropertyTypeArray(
$this->parseType($typeInfo['params'][0], true),
false,
$nullable
);
} }
if (2 === \count($typeInfo['params'])) { if (2 === \count($typeInfo['params'])) {
return self::iterableType($this->parseType($typeInfo['params'][1], true), true, $nullable, $collectionClass); return new PropertyTypeArray(
$this->parseType($typeInfo['params'][1], true),
true,
$nullable
);
} }
throw new InvalidTypeException(sprintf('JMS property type array can\'t have more than 2 parameters (%s)', var_export($typeInfo, true))); throw new InvalidTypeException(sprintf(
'JMS property type array can\'t have more than 2 parameters (%s)',
var_export($typeInfo, true)
));
} }
if (PropertyTypeDateTime::isTypeDateTime($typeInfo['name']) || (self::TYPE_DATETIME_INTERFACE === $typeInfo['name'])) { if (PropertyTypeDateTime::isTypeDateTime($typeInfo['name'])) {
// the case of datetime without params is already handled above, we know we have params // the case of datetime without params is already handled above, we know we have params
$serializeFormat = $typeInfo['params'][0] ?: null;
// {@link \JMS\Serializer\Handler\DateHandler} of jms/serializer defaults to using the serialization format as a deserialization format if none was supplied...
$deserializeFormats = ($typeInfo['params'][2] ?? null) ?: $serializeFormat;
// ... and always converts single strings to arrays
$deserializeFormats = \is_string($deserializeFormats) ? [$deserializeFormats] : $deserializeFormats;
// Jms defaults to DateTime when given DateTimeInterface despite the documentation saying DateTimeImmutable, {@see \JMS\Serializer\Handler\DateHandler} in jms/serializer
$className = (self::TYPE_DATETIME_INTERFACE === $typeInfo['name']) ? \DateTime::class : $typeInfo['name'];
return PropertyTypeDateTime::fromDateTimeClass( return PropertyTypeDateTime::fromDateTimeClass(
$className, $typeInfo['name'],
$nullable, $nullable,
new DateTimeOptions( new DateTimeOptions(
$serializeFormat, $typeInfo['params'][0] ?: null,
($typeInfo['params'][1] ?? null) ?: null, ($typeInfo['params'][1] ?? null) ?: null,
$this->useArrayDateFormat ? $deserializeFormats : $deserializeFormats[0], ($typeInfo['params'][2] ?? null) ?: null
) )
); );
} }
throw new InvalidTypeException(sprintf('Unknown JMS property found (%s)', var_export($typeInfo, true))); throw new InvalidTypeException(sprintf('Unknown JMS property found (%s)', var_export($typeInfo, true)));
} }
private function getCollectionClass(string $name): ?string
{
switch ($name) {
case self::TYPE_ARRAY_COLLECTION:
return ArrayCollection::class;
default:
return is_a($name, Collection::class, true) ? $name : null;
}
}
private static function iterableType(
PropertyType $subType,
bool $hashmap,
bool $nullable,
?string $collectionClass = null
): AbstractPropertyType {
if (class_exists('Liip\MetadataParser\Metadata\PropertyTypeIterable')) {
return new PropertyTypeIterable($subType, $hashmap, $nullable, $collectionClass);
}
return new PropertyTypeArray($subType, $hashmap, $nullable, $collectionClass !== null);
}
} }

View file

@ -1,22 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category BySite
* @package RetailCrm\Api\Enum
*/
namespace RetailCrm\Api\Enum;
/**
* Class BySite
*
* @category BySite
* @package RetailCrm\Api\Enum
*/
final class BySite
{
public const ID = 'id';
public const CODE = 'code';
}

View file

@ -1,23 +0,0 @@
<?php
/**
* PHP 7.3
*
* @category PaymentInvoiceType
* @package RetailCrm\Api\Enum\Payments
*/
namespace RetailCrm\Api\Enum\Payments;
/**
* Class PaymentInvoiceType
*
* @category PaymentInvoiceType
* @package RetailCrm\Api\Enum\Payments
*/
final class PaymentObjectType
{
public const COMMODITY = 'commodity';
public const SERVICE = 'service';
public const PAYMENT = 'payment';
}

View file

@ -1,22 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category CallEventType
* @package RetailCrm\Api\Enum\Telephony
*/
namespace RetailCrm\Api\Enum\Product;
/**
* Class CallEventType
*
* @category CallEventType
* @package RetailCrm\Api\Enum\Telephony
*/
final class ProductType
{
public const PRODUCT = 'product';
public const SERVICE = 'service';
}

View file

@ -285,7 +285,6 @@ class ClientFactory implements ClientFactoryInterface, EventDispatcherAwareInter
->setStreamFactory($this->streamFactory) ->setStreamFactory($this->streamFactory)
->setRequestFactory($this->requestFactory) ->setRequestFactory($this->requestFactory)
->setUriFactory($this->uriFactory) ->setUriFactory($this->uriFactory)
->setEventDispatcher($this->eventDispatcher)
->appendRequestHandlers($this->requestHandlers) ->appendRequestHandlers($this->requestHandlers)
->appendResponseHandlers($this->responseHandlers) ->appendResponseHandlers($this->responseHandlers)
->build(); ->build();

View file

@ -15,7 +15,6 @@ use RetailCrm\Api\Enum\RequestMethod;
use RetailCrm\Api\Exception\Client\HandlerException; use RetailCrm\Api\Exception\Client\HandlerException;
use RetailCrm\Api\Handler\AbstractHandler; use RetailCrm\Api\Handler\AbstractHandler;
use RetailCrm\Api\Interfaces\FormEncoderInterface; use RetailCrm\Api\Interfaces\FormEncoderInterface;
use RetailCrm\Api\Model\Request\Files\FilesUploadRequest;
use RetailCrm\Api\Model\RequestData; use RetailCrm\Api\Model\RequestData;
use Throwable; use Throwable;
@ -77,9 +76,7 @@ class RequestDataHandler extends AbstractHandler
} }
if ('' !== $formData) { if ('' !== $formData) {
if ($item->requestModel instanceof FilesUploadRequest) { if (static::queryShouldBeUsed($item->request->getMethod())) {
$item->request = $item->request->withBody($item->requestModel->file);
} elseif (static::queryShouldBeUsed($item->request->getMethod())) {
$item->request = $item->request->withUri( $item->request = $item->request->withUri(
$item->request->getUri()->withQuery($formData) $item->request->getUri()->withQuery($formData)
); );

View file

@ -26,7 +26,7 @@ class FilesDownloadResponseHandler extends AbstractResponseHandler
*/ */
protected function handleResponse(ResponseData $responseData) protected function handleResponse(ResponseData $responseData)
{ {
if (!static::isFileRequest($responseData->request->getUri()->getPath())) { if (!preg_match('/^\/api\/v5\/files\/\d+\/download$/', $responseData->request->getUri()->getPath())) {
$this->next($responseData); $this->next($responseData);
return; return;
@ -44,29 +44,6 @@ class FilesDownloadResponseHandler extends AbstractResponseHandler
)); ));
} }
/**
* Checking the request path to determine a file request
*
* @param string $path
*
* @return bool
*/
private static function isFileRequest(string $path): bool
{
$filePatterns = [
'#^/api/v5/files/\d+/download$#',
'#^/api/v5/orders/\S+/plates/\d+/print$#',
];
foreach ($filePatterns as $pattern) {
if (preg_match($pattern, $path)) {
return true;
}
}
return false;
}
/** /**
* Parses filename from a Content-Disposition header value. * Parses filename from a Content-Disposition header value.
* *

View file

@ -1,38 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category ShipmentPointList
* @package RetailCrm\Api\Model\Callback\Entity\Delivery
*/
namespace RetailCrm\Api\Model\Callback\Entity\Delivery;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
use RetailCrm\Api\Model\Entity\References\GeoHierarchyRow;
/**
* Class ShipmentPointList
*
* @category ShipmentPointList
* @package RetailCrm\Api\Model\Callback\Entity\Delivery
*/
class ShipmentPointList extends GeoHierarchyRow
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("clientId")
*/
public $clientId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("code")
*/
public $code;
}

View file

@ -1,37 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Settings
* @package RetailCrm\Api\Model\Callback\Entity\Integration
*/
namespace RetailCrm\Api\Model\Callback\Entity\Integration;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Settings
*
* @category Settings
* @package RetailCrm\Api\Model\Callback\Entity\Integration
*/
class Settings
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("clientId")
*/
public $clientId;
/**
* @var \RetailCrm\Api\Model\Entity\Settings\Settings
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Settings\Settings")
* @JMS\SerializedName("settings")
*/
public $settings;
}

View file

@ -84,7 +84,6 @@ class CustomField
public $entity; public $entity;
/** /**
* @deprecated Please use $defaultTyped instead.
* @var string * @var string
* *
* @JMS\Type("string") * @JMS\Type("string")
@ -123,12 +122,4 @@ class CustomField
* @JMS\SerializedName("dictionary") * @JMS\SerializedName("dictionary")
*/ */
public $dictionary; public $dictionary;
/**
* @var mixed
*
* @JMS\Type("mixed")
* @JMS\SerializedName("defaultTyped")
*/
public $defaultTyped;
} }

View file

@ -1,86 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Cart
* @package RetailCrm\Api\Model\Entity\CustomerInteraction
*/
namespace RetailCrm\Api\Model\Entity\CustomerInteraction;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Cart
*
* @category Cart
* @package RetailCrm\Api\Model\Entity\CustomerInteraction
*/
class Cart
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("currency")
*/
public $currency;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:sP'>")
* @JMS\SerializedName("droppedAt")
*/
public $droppedAt;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:sP'>")
* @JMS\SerializedName("clearedAt")
*/
public $clearedAt;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("link")
*/
public $link;
/**
* @var \RetailCrm\Api\Model\Entity\CustomerInteraction\CartCustomer
*
* @JMS\Type("RetailCrm\Api\Model\Entity\CustomerInteraction\CartCustomer")
* @JMS\SerializedName("customer")
*/
public $customer;
/**
* @var \RetailCrm\Api\Model\Entity\CustomerInteraction\CartItem[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\CustomerInteraction\CartItem>")
* @JMS\SerializedName("items")
*/
public $items;
/**
* @var \RetailCrm\Api\Model\Entity\CustomerInteraction\CartOrder
*
* @JMS\Type("RetailCrm\Api\Model\Entity\CustomerInteraction\CartOrder")
* @JMS\SerializedName("order")
*/
public $order;
}

View file

@ -1,61 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category CartCustomer
* @package RetailCrm\Api\Model\Entity\CustomerInteraction
*/
namespace RetailCrm\Api\Model\Entity\CustomerInteraction;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class CartCustomer
*
* @category CartCustomer
* @package RetailCrm\Api\Model\Entity\CustomerInteraction
*/
class CartCustomer
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("browserId")
*/
public $browserId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("site")
*/
public $site;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("gaClientId")
*/
public $gaClientId;
}

View file

@ -1,53 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category CartItem
* @package RetailCrm\Api\Model\Entity\CustomerInteraction
*/
namespace RetailCrm\Api\Model\Entity\CustomerInteraction;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class CartItem
*
* @category CartItem
* @package RetailCrm\Api\Model\Entity\CustomerInteraction
*/
class CartItem
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var \RetailCrm\Api\Model\Entity\Orders\Items\Offer
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Orders\Items\Offer")
* @JMS\SerializedName("offer")
*/
public $offer;
/**
* @var float
*
* @JMS\Type("float")
* @JMS\SerializedName("quantity")
*/
public $quantity;
/**
* @var float
*
* @JMS\Type("float")
* @JMS\SerializedName("price")
*/
public $price;
}

View file

@ -1,45 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category CartOrder
* @package RetailCrm\Api\Model\Entity\CustomerInteraction
*/
namespace RetailCrm\Api\Model\Entity\CustomerInteraction;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class CartOrder
*
* @category CartOrder
* @package RetailCrm\Api\Model\Entity\CustomerInteraction
*/
class CartOrder
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("number")
*/
public $number;
}

View file

@ -120,15 +120,6 @@ class Customer implements CustomerInterface
*/ */
public $tags; public $tags;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("attachedTag")
*/
public $attachedTag;
/** /**
* @var string[] * @var string[]
* *
@ -368,20 +359,4 @@ class Customer implements CustomerInterface
* @JMS\SerializedName("subscribed") * @JMS\SerializedName("subscribed")
*/ */
public $subscribed; public $subscribed;
/**
* @var \RetailCrm\Api\Model\Entity\Customers\CustomerSubscription[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Customers\CustomerSubscription>")
* @JMS\SerializedName("customerSubscriptions")
*/
public $customerSubscriptions;
/**
* @var \RetailCrm\Api\Model\Entity\Customers\MGCustomer[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Customers\MGCustomer>")
* @JMS\SerializedName("mgCustomers")
*/
public $mgCustomers;
} }

View file

@ -1,46 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category CustomerSubscription
* @package RetailCrm\Api\Model\Entity\Customers
*/
namespace RetailCrm\Api\Model\Entity\Customers;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class CustomerSubscription
*
* @category CustomerSubscription
* @package RetailCrm\Api\Model\Entity\Customers
*/
class CustomerSubscription
{
/**
* @var \RetailCrm\Api\Model\Entity\Customers\SubscriptionCategory
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Customers\SubscriptionCategory")
* @JMS\SerializedName("subscription")
*/
public $subscription;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("subscribed")
*/
public $subscribed;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("changedAt")
*/
public $changedAt;
}

View file

@ -1,69 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category MGChannel
* @package RetailCrm\Api\Model\Entity\Customers
*/
namespace RetailCrm\Api\Model\Entity\Customers;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class MGChannel
*
* @category MGChannel
* @package RetailCrm\Api\Model\Entity\Customers
*/
class MGChannel
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("type")
*/
public $type;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("active")
*/
public $active;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("name")
*/
public $name;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("allowedSendByPhone")
*/
public $allowedSendByPhone;
}

View file

@ -1,45 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category MGCustomer
* @package RetailCrm\Api\Model\Entity\Customers
*/
namespace RetailCrm\Api\Model\Entity\Customers;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class MGCustomer
*
* @category MGCustomer
* @package RetailCrm\Api\Model\Entity\Customers
*/
class MGCustomer
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var \RetailCrm\Api\Model\Entity\Customers\MGChannel
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Customers\MGChannel")
* @JMS\SerializedName("mgChannel")
*/
public $mgChannel;
}

View file

@ -1,53 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Subscription
* @package RetailCrm\Api\Model\Entity\Customers
*/
namespace RetailCrm\Api\Model\Entity\Customers;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Subscription
*
* @category Subscription
* @package RetailCrm\Api\Model\Entity\Customers
*/
class Subscription
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("channel")
*/
public $channel;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("subscription")
*/
public $subscription;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("active")
*/
public $active;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("messageId")
*/
public $messageId;
}

View file

@ -1,77 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category SubscriptionCategory
* @package RetailCrm\Api\Model\Entity\Customers
*/
namespace RetailCrm\Api\Model\Entity\Customers;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class SubscriptionCategory
*
* @category SubscriptionCategory
* @package RetailCrm\Api\Model\Entity\Customers
*/
class SubscriptionCategory
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("channel")
*/
public $channel;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("name")
*/
public $name;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("code")
*/
public $code;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("active")
*/
public $active;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("autoSubscribe")
*/
public $autoSubscribe;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("ordering")
*/
public $ordering;
}

View file

@ -1,29 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category EntityWithExternalId
* @package RetailCrm\Api\Model\Entity
*/
namespace RetailCrm\Api\Model\Entity;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class EntityWithExternalId
*
* @category EntityWithExternalId
* @package RetailCrm\Api\Model\Entity
*/
class EntityWithExternalId
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
}

View file

@ -1,45 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category EmbedJsConfiguration
* @package RetailCrm\Api\Model\Entity\Integration\EmbedJs
*/
namespace RetailCrm\Api\Model\Entity\Integration\EmbedJs;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class EmbedJsConfiguration
*
* @category EmbedJsConfiguration
* @package RetailCrm\Api\Model\Entity\Integration\EmbedJs
*/
class EmbedJsConfiguration
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("entrypoint")
*/
public $entrypoint;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("stylesheet")
*/
public $stylesheet;
/**
* @var string[]
*
* @JMS\Type("array<string>")
* @JMS\SerializedName("targets")
*/
public $targets;
}

View file

@ -74,12 +74,4 @@ class Integrations
* @JMS\SerializedName("mgBot") * @JMS\SerializedName("mgBot")
*/ */
public $mgBot; public $mgBot;
/**
* @var \RetailCrm\Api\Model\Entity\Integration\EmbedJs\EmbedJsConfiguration
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Integration\EmbedJs\EmbedJsConfiguration")
* @JMS\SerializedName("embedJs")
*/
public $embedJs;
} }

View file

@ -22,7 +22,7 @@ class BonusDetail
/** /**
* @var \DateTime * @var \DateTime
* *
* @JMS\Type("DateTime<'Y-m-d'>") * @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("date") * @JMS\SerializedName("date")
*/ */
public $date; public $date;

View file

@ -46,14 +46,6 @@ class Loyalty
*/ */
public $blocked; public $blocked;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("currency")
*/
public $currency;
/** /**
* @var int * @var int
* *

View file

@ -116,12 +116,4 @@ class SerializedLoyaltyOrder
* @JMS\SerializedName("items") * @JMS\SerializedName("items")
*/ */
public $items; public $items;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("currency")
*/
public $currency;
} }

View file

@ -1,57 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Notification
* @package RetailCrm\Api\Model\Entity\Notifications
*/
namespace RetailCrm\Api\Model\Entity\Notifications;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Notification
*
* @category Notification
* @package RetailCrm\Api\Model\Entity\Notifications
*
* @SuppressWarnings(PHPMD.LongVariables)
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.ExcessivePublicCount)
*/
class Notification
{
/**
* @var string[]
*
* @JMS\Type("array<string>")
* @JMS\SerializedName("userGroups")
*/
public $userGroups;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("type")
*/
public $type;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("message")
*/
public $message;
/**
* @var int[]
*
* @JMS\Type("array<int>")
* @JMS\SerializedName("userIds")
*/
public $userIds;
}

View file

@ -671,12 +671,4 @@ class DeliveryData
* @JMS\SerializedName("service") * @JMS\SerializedName("service")
*/ */
public $service; public $service;
/**
* @var ItemDeclaredValue[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Orders\Delivery\ItemDeclaredValue>")
* @JMS\SerializedName("itemDeclaredValues")
*/
public $itemDeclaredValues;
} }

View file

@ -1,31 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category DeliveryData
* @package RetailCrm\Api\Model\Entity\Orders\Delivery
*/
namespace RetailCrm\Api\Model\Entity\Orders\Delivery;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
class ItemDeclaredValue
{
/**
* @var ItemDeclaredValueProduct
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Orders\Delivery\ItemDeclaredValueProduct")
* @JMS\SerializedName("orderProduct")
*/
public $orderProduct;
/**
* @var float
*
* @JMS\Type("float")
* @JMS\SerializedName("value")
*/
public $value;
}

View file

@ -1,23 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category DeliveryData
* @package RetailCrm\Api\Model\Entity\Orders\Delivery
*/
namespace RetailCrm\Api\Model\Entity\Orders\Delivery;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
class ItemDeclaredValueProduct
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
}

View file

@ -1,45 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category LinkedOrder
* @package RetailCrm\Api\Model\Entity\Orders
*/
namespace RetailCrm\Api\Model\Entity\Orders;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class LinkedOrder
*
* @category LinkedOrder
* @package RetailCrm\Api\Model\Entity\Orders
*/
class LinkedOrder
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("number")
*/
public $number;
}

View file

@ -48,14 +48,6 @@ class Order
*/ */
public $summ; public $summ;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("currency")
*/
public $currency;
/** /**
* @var int * @var int
* *
@ -416,14 +408,6 @@ class Order
*/ */
public $payments; public $payments;
/**
* @var \RetailCrm\Api\Model\Entity\Orders\OrderLink[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Orders\OrderLink>")
* @JMS\SerializedName("links")
*/
public $links;
/** /**
* @var bool * @var bool
* *

View file

@ -1,46 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category OrderLink
* @package RetailCrm\Api\Model\Entity\Orders
*/
namespace RetailCrm\Api\Model\Entity\Orders;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class OrderLink
*
* @category OrderLink
* @package RetailCrm\Api\Model\Entity\Orders
*/
class OrderLink
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("comment")
*/
public $comment;
/**
* @var \RetailCrm\Api\Model\Entity\Orders\LinkedOrder
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Delivery\SerializedEntityOrder>")
* @JMS\SerializedName("order")
*/
public $order;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("createdAt")
*/
public $createdAt;
}

View file

@ -43,14 +43,6 @@ class ApiUpdateInvoiceRequest
*/ */
public $amount; public $amount;
/**
* @var float
*
* @JMS\Type("float")
* @JMS\SerializedName("discountAmount")
*/
public $discountAmount;
/** /**
* @var string * @var string
* *

View file

@ -1,79 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Currency
* @package RetailCrm\Api\Model\Entity\References
*/
namespace RetailCrm\Api\Model\Entity\References;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Currency
*
* @category Currency
* @package RetailCrm\Api\Model\Entity\References
*
* @SuppressWarnings(PHPMD.LongVariable)
*/
class Currency
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("code")
*/
public $code;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("isBase")
*/
public $isBase;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("isAutoConvert")
*/
public $isAutoConvert;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("autoConvertExtraPercent")
*/
public $autoConvertExtraPercent;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("manualConvertNominal")
*/
public $manualConvertNominal;
/**
* @var float
*
* @JMS\Type("float")
* @JMS\SerializedName("manualConvertValue")
*/
public $manualConvertValue;
}

View file

@ -69,14 +69,6 @@ class DeliveryType
*/ */
public $isCostDependsOnDateTime; public $isCostDependsOnDateTime;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("currency")
*/
public $currency;
/** /**
* @var string * @var string
* *
@ -188,20 +180,4 @@ class DeliveryType
* @JMS\SerializedName("defaultTariffName") * @JMS\SerializedName("defaultTariffName")
*/ */
public $defaultTariffName; public $defaultTariffName;
/**
* @var \RetailCrm\Api\Model\Entity\References\DeliveryTypePaymentType[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\References\DeliveryTypePaymentType>")
* @JMS\SerializedName("deliveryPaymentTypes")
*/
public $deliveryPaymentTypes;
/**
* @var string[]
*
* @JMS\Type("array")
* @JMS\SerializedName("sites")
*/
public $sites;
} }

View file

@ -1,37 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category DeliveryTypePaymentType
* @package RetailCrm\Api\Model\Entity\References
*/
namespace RetailCrm\Api\Model\Entity\References;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class DeliveryTypePaymentType
*
* @category DeliveryTypePaymentType
* @package RetailCrm\Api\Model\Entity\References
*/
class DeliveryTypePaymentType
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("code")
*/
public $code;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("cod")
*/
public $cod;
}

View file

@ -97,7 +97,7 @@ class LegalEntity
/** /**
* @var DateTime * @var DateTime
* *
* @JMS\Type("DateTime<'Y-m-d'>") * @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("certificateDate") * @JMS\SerializedName("certificateDate")
*/ */
public $certificateDate; public $certificateDate;

View file

@ -90,12 +90,4 @@ class PaymentType
* @JMS\SerializedName("integrationModule") * @JMS\SerializedName("integrationModule")
*/ */
public $integrationModule; public $integrationModule;
/**
* @var string[]
*
* @JMS\Type("array")
* @JMS\SerializedName("sites")
*/
public $sites;
} }

View file

@ -75,14 +75,6 @@ class PriceType
*/ */
public $filterExpression; public $filterExpression;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("currency")
*/
public $currency;
/** /**
* @var \RetailCrm\Api\Model\Entity\References\GeoHierarchyRow[] * @var \RetailCrm\Api\Model\Entity\References\GeoHierarchyRow[]
* *

View file

@ -20,14 +20,6 @@ use RetailCrm\Api\Component\Serializer\Annotation as JMS;
*/ */
class Site class Site
{ {
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/** /**
* @var string * @var string
* *
@ -156,14 +148,6 @@ class Site
*/ */
public $countryIso; public $countryIso;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("currency")
*/
public $currency;
/** /**
* @var string * @var string
* *

View file

@ -91,14 +91,6 @@ class Store
*/ */
public $phone; public $phone;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("contact")
*/
public $contact;
/** /**
* @var string * @var string
* *

View file

@ -1,45 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Feature
* @package RetailCrm\Api\Model\Entity\Settings
*/
namespace RetailCrm\Api\Model\Entity\Settings;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class ChannelSetting
*
* @category ChannelSetting
* @package RetailCrm\Api\Model\Entity\Settings
*/
class ChannelSetting
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("site")
*/
public $site;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("order_type")
*/
public $orderType;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("order_method")
*/
public $orderMethod;
}

View file

@ -1,37 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Feature
* @package RetailCrm\Api\Model\Entity\Settings
*/
namespace RetailCrm\Api\Model\Entity\Settings;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Feature
*
* @category Feature
* @package RetailCrm\Api\Model\Entity\Settings
*/
class Feature
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("code")
*/
public $code;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("available")
*/
public $available;
}

View file

@ -1,37 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Feature
* @package RetailCrm\Api\Model\Entity\Settings
*/
namespace RetailCrm\Api\Model\Entity\Settings;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class MgOrderCreationSettings
*
* @category MgOrderCreationSettings
* @package RetailCrm\Api\Model\Entity\Settings
*/
class MgOrderCreationSettings
{
/**
* @var \RetailCrm\Api\Model\Entity\Settings\ChannelSetting
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Settings\ChannelSetting")
* @JMS\SerializedName("default")
*/
public $default;
/**
* @var array<int, \RetailCrm\Api\Model\Entity\Settings\ChannelSetting>
*
* @JMS\Type("array<int, RetailCrm\Api\Model\Entity\Settings\ChannelSetting>")
* @JMS\SerializedName("channels")
*/
public $channels;
}

View file

@ -1,29 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Feature
* @package RetailCrm\Api\Model\Entity\Settings
*/
namespace RetailCrm\Api\Model\Entity\Settings;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class MgSettings
*
* @category MgSettings
* @package RetailCrm\Api\Model\Entity\Settings
*/
class MgSettings
{
/**
* @var \RetailCrm\Api\Model\Entity\Settings\MgOrderCreationSettings
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Settings\MgOrderCreationSettings")
* @JMS\SerializedName("order_creation")
*/
public $orderCreation;
}

View file

@ -1,37 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Value
* @package RetailCrm\Api\Model\Entity\Settings
*/
namespace RetailCrm\Api\Model\Entity\Settings;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class NonWorkingDay
*
* @category NonWorkingDay
* @package RetailCrm\Api\Model\Entity\Settings
*/
class NonWorkingDay
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("start_date")
*/
public $startDate;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("end_date")
*/
public $endDate;
}

View file

@ -42,28 +42,4 @@ class Settings
* @JMS\SerializedName("timezone") * @JMS\SerializedName("timezone")
*/ */
public $timezone; public $timezone;
/**
* @var \RetailCrm\Api\Model\Entity\Settings\WorkTime[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Settings\WorkTime>")
* @JMS\SerializedName("work_times")
*/
public $workTimes;
/**
* @var \RetailCrm\Api\Model\Entity\Settings\NonWorkingDay[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Settings\NonWorkingDay>")
* @JMS\SerializedName("non_working_days")
*/
public $nonWorkingDays;
/**
* @var \RetailCrm\Api\Model\Entity\Settings\MgSettings
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Settings\MgSettings")
* @JMS\SerializedName("mg")
*/
public $mgSettings;
} }

View file

@ -1,61 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Value
* @package RetailCrm\Api\Model\Entity\Settings
*/
namespace RetailCrm\Api\Model\Entity\Settings;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class WorkTime
*
* @category WorkTime
* @package RetailCrm\Api\Model\Entity\Settings
*/
class WorkTime
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("day_type")
*/
public $dayType;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("start_time")
*/
public $startTime;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("end_time")
*/
public $endTime;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("lunch_start_time")
*/
public $lunchStartTime;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("lunch_end_time")
*/
public $lunchEndTime;
}

View file

@ -42,12 +42,4 @@ class OfferPrice
* @JMS\SerializedName("ordering") * @JMS\SerializedName("ordering")
*/ */
public $ordering; public $ordering;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("currency")
*/
public $currency;
} }

View file

@ -1,190 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category OfferProduct
* @package RetailCrm\Api\Model\Entity\Store
*/
namespace RetailCrm\Api\Model\Entity\Store;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Product
*
* @category OfferProduct
* @package RetailCrm\Api\Model\Entity\Store
*/
class OfferProduct
{
/**
* @var float
*
* @JMS\Type("float")
* @JMS\SerializedName("minPrice")
*/
public $minPrice;
/**
* @var float
*
* @JMS\Type("float")
* @JMS\SerializedName("maxPrice")
*/
public $maxPrice;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("article")
*/
public $article;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("name")
*/
public $name;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("url")
*/
public $url;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("imageUrl")
*/
public $imageUrl;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("description")
*/
public $description;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("popular")
*/
public $popular;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("stock")
*/
public $stock;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("novelty")
*/
public $novelty;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("recommended")
*/
public $recommended;
/**
* @var array<string, mixed>
*
* @JMS\Type("array")
* @JMS\SerializedName("properties")
*/
public $properties;
/**
* @var \RetailCrm\Api\Model\Entity\FixExternalRow[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\FixExternalRow>")
* @JMS\SerializedName("groups")
*/
public $groups;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("manufacturer")
*/
public $manufacturer;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("active")
*/
public $active;
/**
* @var float
*
* @JMS\Type("float")
* @JMS\SerializedName("quantity")
*/
public $quantity;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("markable")
*/
public $markable;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("updatedAt")
*/
public $updatedAt;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("type")
*/
public $type;
}

View file

@ -36,14 +36,6 @@ class Product
*/ */
public $maxPrice; public $maxPrice;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("catalogId")
*/
public $catalogId;
/** /**
* @var int * @var int
* *
@ -195,12 +187,4 @@ class Product
* @JMS\SerializedName("updatedAt") * @JMS\SerializedName("updatedAt")
*/ */
public $updatedAt; public $updatedAt;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("type")
*/
public $type;
} }

View file

@ -1,141 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category ProductCreateInput
* @package RetailCrm\Api\Model\Request\Store
*/
namespace RetailCrm\Api\Model\Entity\Store;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Abstract class ProductBatchBase
*
* @category ProductBatchBase
* @package RetailCrm\Api\Model\Response\Store
*/
abstract class ProductBatchBase
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("article")
*/
public $article;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("name")
*/
public $name;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("url")
*/
public $url;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("description")
*/
public $description;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("popular")
*/
public $popular;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("stock")
*/
public $stock;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("novelty")
*/
public $novelty;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("recommended")
*/
public $recommended;
/**
* @var \RetailCrm\Api\Model\Entity\Store\ProductEditGroupInput[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Store\ProductEditGroupInput>")
* @JMS\SerializedName("groups")
*/
public $groups;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("manufacturer")
*/
public $manufacturer;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("active")
*/
public $active;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("markable")
*/
public $markable;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("catalogId")
*/
public $catalogId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("type")
*/
public $type;
}

View file

@ -1,20 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category ProductCreateInput
* @package RetailCrm\Api\Model\Request\Store
*/
namespace RetailCrm\Api\Model\Entity\Store;
/**
* Class ProductCreateInput
*
* @category ProductCreateInput
* @package RetailCrm\Api\Model\Response\Store
*/
class ProductCreateInput extends ProductBatchBase
{
}

View file

@ -17,7 +17,7 @@ use RetailCrm\Api\Component\Serializer\Annotation as JMS;
* @category ProductEditInput * @category ProductEditInput
* @package RetailCrm\Api\Model\Response\Store * @package RetailCrm\Api\Model\Response\Store
*/ */
class ProductEditInput extends ProductBatchBase class ProductEditInput
{ {
/** /**
* @var int * @var int
@ -27,6 +27,110 @@ class ProductEditInput extends ProductBatchBase
*/ */
public $id; public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("article")
*/
public $article;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("name")
*/
public $name;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("url")
*/
public $url;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("description")
*/
public $description;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("popular")
*/
public $popular;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("stock")
*/
public $stock;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("novelty")
*/
public $novelty;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("recommended")
*/
public $recommended;
/**
* @var \RetailCrm\Api\Model\Entity\Store\ProductEditGroupInput[]
*
* @JMS\Type("array<RetailCrm\Api\Model\Entity\Store\ProductEditGroupInput>")
* @JMS\SerializedName("groups")
*/
public $groups;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("manufacturer")
*/
public $manufacturer;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("active")
*/
public $active;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("markable")
*/
public $markable;
/** /**
* @var string * @var string
* *
@ -34,4 +138,12 @@ class ProductEditInput extends ProductBatchBase
* @JMS\SerializedName("site") * @JMS\SerializedName("site")
*/ */
public $site; public $site;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("catalogId")
*/
public $catalogId;
} }

View file

@ -66,12 +66,4 @@ class ProductGroup
* @JMS\SerializedName("active") * @JMS\SerializedName("active")
*/ */
public $active; public $active;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("lvl")
*/
public $lvl;
} }

View file

@ -170,12 +170,4 @@ class ProductOffer
* @JMS\SerializedName("barcode") * @JMS\SerializedName("barcode")
*/ */
public $barcode; public $barcode;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("site")
*/
public $site;
} }

View file

@ -1,45 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category ProductPropertyValue
* @package RetailCrm\Api\Model\Entity\Store
*/
namespace RetailCrm\Api\Model\Entity\Store;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class ProductPropertyValue
*
* @category ProductPropertyValue
* @package RetailCrm\Api\Model\Entity\Store
*/
class ProductPropertyValue
{
/**
* @var \RetailCrm\Api\Model\Entity\Store\ProductProperty
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Store\ProductProperty")
* @JMS\SerializedName("property")
*/
public $property;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("value")
*/
public $value;
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("offersCount")
*/
public $offersCount;
}

View file

@ -1,77 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category SerializedProductGroup
* @package RetailCrm\Api\Model\Entity\Store
*/
namespace RetailCrm\Api\Model\Entity\Store;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class SerializedProductGroup
*
* @category SerializedProductGroup
* @package RetailCrm\Api\Model\Entity\Store
*/
class SerializedProductGroup
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("parentId")
*/
public $parentId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("name")
*/
public $name;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("description")
*/
public $description;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("active")
*/
public $active;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("parentExternalId")
*/
public $parentExternalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("site")
*/
public $site;
}

View file

@ -1,34 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category StoreOffer
* @package RetailCrm\Api\Model\Entity\Store
*/
namespace RetailCrm\Api\Model\Entity\Store;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class StoreOffer
*
* @category StoreOffer
* @package RetailCrm\Api\Model\Entity\Store
*/
class StoreOffer extends ProductOffer
{
/**
* @JMS\Exclude()
*/
public $price;
/**
* @var \RetailCrm\Api\Model\Entity\Store\Product
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Store\Product")
* @JMS\SerializedName("product")
*/
public $product;
}

View file

@ -1,37 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category BaseComment
* @package RetailCrm\Api\Model\Entity\Tasks
*/
namespace RetailCrm\Api\Model\Entity\Tasks;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class BaseComment
*
* @category BaseComment
* @package RetailCrm\Api\Model\Entity\Tasks
*/
class BaseComment
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("text")
*/
public $text;
}

View file

@ -1,46 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category TaskComment
* @package RetailCrm\Api\Model\Entity\Tasks
*/
namespace RetailCrm\Api\Model\Entity\Tasks;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class TaskComment
*
* @category TaskComment
* @package RetailCrm\Api\Model\Entity\Tasks
*/
class TaskComment extends BaseComment
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("creator")
*/
public $creator;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("createdAt")
*/
public $createdAt;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("updatedAt")
*/
public $updatedAt;
}

View file

@ -1,110 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category TaskHistory
* @package RetailCrm\Api\Model\Entity\Tasks
*/
namespace RetailCrm\Api\Model\Entity\Tasks;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class TaskHistory
*
* @category TaskHistory
* @package RetailCrm\Api\Model\Entity\Tasks
*/
class TaskHistory
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("createdAt")
*/
public $createdAt;
/**
* @var bool
*
* @JMS\Type("bool")
* @JMS\SerializedName("created")
*/
public $created;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("source")
*/
public $source;
/**
* @var \RetailCrm\Api\Model\Entity\HistoryUser
*
* @JMS\Type("RetailCrm\Api\Model\Entity\HistoryUser")
* @JMS\SerializedName("user")
*/
public $user;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("field")
*/
public $field;
/**
* @var mixed
*
* @JMS\Type("mixed")
* @JMS\SerializedName("oldValue")
*/
public $oldValue;
/**
* @var mixed
*
* @JMS\Type("mixed")
* @JMS\SerializedName("newValue")
*/
public $newValue;
/**
* @var \RetailCrm\Api\Model\Entity\HistoryApiKey
*
* @JMS\Type("RetailCrm\Api\Model\Entity\HistoryApiKey")
* @JMS\SerializedName("apiKey")
*/
public $apiKey;
/**
* @var \RetailCrm\Api\Model\Entity\Tasks\Task
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Tasks\Task")
* @JMS\SerializedName("task")
*/
public $task;
/**
* @var \RetailCrm\Api\Model\Entity\Tasks\BaseComment
*
* @JMS\Type("RetailCrm\Api\Model\Entity\Tasks\BaseComment")
* @JMS\SerializedName("comment")
*/
public $comment;
}

View file

@ -76,14 +76,6 @@ class User
*/ */
public $patronymic; public $patronymic;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("position")
*/
public $position;
/** /**
* @var string * @var string
* *

View file

@ -1,62 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category ClientId
* @package RetailCrm\Api\Model\Entity\WebAnalytics
*/
namespace RetailCrm\Api\Model\Entity\WebAnalytics;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class ClientId
*
* @category ClientId
* @package RetailCrm\Api\Model\Entity\WebAnalytics
*/
class ClientId
{
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("value")
*/
public $value;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("site")
*/
public $site;
/**
* @var Order
*
* @JMS\Type("RetailCrm\Api\Model\Entity\WebAnalytics\Order")
* @JMS\SerializedName("order")
*/
public $order;
/**
* @var Customer
*
* @JMS\Type("RetailCrm\Api\Model\Entity\WebAnalytics\Customer")
* @JMS\SerializedName("customer")
*/
public $customer;
/**
* @var DateTime
*
* @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("createdAt")
*/
public $createdAt;
}

View file

@ -1,46 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Customer
* @package RetailCrm\Api\Model\Entity\WebAnalytics
*/
namespace RetailCrm\Api\Model\Entity\WebAnalytics;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Customer
*
* @category Customer
* @package RetailCrm\Api\Model\Entity\WebAnalytics
*/
class Customer
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("type")
*/
public $type;
}

View file

@ -1,46 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category Order
* @package RetailCrm\Api\Model\Entity\WebAnalytics
*/
namespace RetailCrm\Api\Model\Entity\WebAnalytics;
use DateTime;
use RetailCrm\Api\Component\Serializer\Annotation as JMS;
/**
* Class Order
*
* @category Order
* @package RetailCrm\Api\Model\Entity\WebAnalytics
*/
class Order
{
/**
* @var int
*
* @JMS\Type("int")
* @JMS\SerializedName("id")
*/
public $id;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("externalId")
*/
public $externalId;
/**
* @var string
*
* @JMS\Type("string")
* @JMS\SerializedName("number")
*/
public $number;
}

Some files were not shown because too many files have changed in this diff Show more