├── .github
├── actions
│ └── composer-cache
│ │ └── action.yml
└── workflows
│ └── test_extension.yml
├── .gitignore
├── .php-version
├── LICENSE.txt
├── README.md
├── component
├── README.md
└── app
│ └── etc
│ └── cli_arg_auto_proxy
│ └── di.xml
├── composer.json
├── images
└── logo.png
├── lib
├── Enum
│ └── ProxyClassEntityInterfaceEnum.php
├── Exception
│ └── ClassIsNotEligibleForProxyException.php
├── Mapper
│ └── ProxiedConstructArgsToDiConfigMapper.php
├── Plugin
│ └── Dom
│ │ └── EnrichCliConfigWithProxyPlugin.php
├── Preference
│ └── Framework
│ │ └── ObjectManager
│ │ └── Config
│ │ └── Reader
│ │ └── Dom
│ │ └── Interceptor.php
├── README.md
├── Service
│ ├── EnrichCliConfigWithProxyService.php
│ └── GetProxiedConstructArgsConfigService.php
├── Test
│ └── Unit
│ │ ├── Map
│ │ └── ProxiedConstructArgsToDiConfigMapperTest.php
│ │ ├── Plugin
│ │ └── Dom
│ │ │ └── EnrichCliConfigWithProxyPluginTest.php
│ │ ├── Preference
│ │ └── Framework
│ │ │ └── ObjectManager
│ │ │ └── Config
│ │ │ └── Reader
│ │ │ └── Dom
│ │ │ └── InterceptorTest.php
│ │ └── Service
│ │ ├── EnrichCliConfigWithProxyServiceTest.php
│ │ └── GetProxiedConstructArgsConfigServiceTest.php
└── Validator
│ └── IsClassEligibleForProxyValidator.php
├── phpstan.neon.dist
└── phpunit.xml
/.github/actions/composer-cache/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Cache Composer packages'
2 | runs:
3 | using: 'composite'
4 | steps:
5 | - id: composer-cache
6 | uses: actions/cache@v3
7 | with:
8 | path: vendor
9 | key: ${{ inputs.runner-os }}-php-${{ hashFiles('**/composer.lock') }}
10 | restore-keys: |
11 | ${{ inputs.runner-os }}-php-
--------------------------------------------------------------------------------
/.github/workflows/test_extension.yml:
--------------------------------------------------------------------------------
1 | name: Test Extension
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | permissions:
10 | contents: read
11 |
12 | jobs:
13 | validate-composer:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 |
20 | - name: Validate composer.json and composer.lock
21 | run: composer validate
22 |
23 | build:
24 |
25 | runs-on: ubuntu-latest
26 |
27 | needs: validate-composer
28 |
29 | steps:
30 | - uses: actions/checkout@v3
31 | - uses: ./.github/actions/composer-cache
32 |
33 | - name: Install dependencies
34 | run: composer install --prefer-dist --no-progress
35 |
36 | PHP-Compatibility:
37 | runs-on: ubuntu-latest
38 |
39 | needs: build
40 |
41 | steps:
42 | - uses: actions/checkout@v3
43 | - uses: ./.github/actions/composer-cache
44 |
45 | - name: PHP 7.4 compatibility
46 | run: composer sniffer:php7.4
47 |
48 | - name: PHP 8.0 compatibility
49 | run: composer sniffer:php8.0
50 |
51 | - name: PHP 8.1 compatibility
52 | run: composer sniffer:php8.1
53 |
54 | - name: PHP 8.2 compatibility
55 | run: composer sniffer:php8.2
56 |
57 | - name: PHP 8.3 compatibility
58 | run: composer sniffer:php8.3
59 |
60 | Static-tests:
61 | runs-on: ubuntu-latest
62 |
63 | needs: build
64 |
65 | steps:
66 | - uses: actions/checkout@v3
67 | - uses: ./.github/actions/composer-cache
68 |
69 | - name: phpstan
70 | run: composer phpstan
71 |
72 | PHP-Unit:
73 | runs-on: ubuntu-latest
74 |
75 | needs: build
76 |
77 | steps:
78 | - uses: actions/checkout@v3
79 | - uses: ./.github/actions/composer-cache
80 |
81 | - name: Setup PHP with Xdebug
82 | uses: shivammathur/setup-php@v2
83 | with:
84 | php-version: '8.1'
85 | coverage: xdebug
86 |
87 | - name: PHP Unit
88 | run: composer phpunit
89 |
90 | - name: phpunit-coverage-badge
91 | uses: timkrase/phpunit-coverage-badge@v1.2.0
92 | with:
93 | push_badge: true
94 | commit_message: "Update coverage badge"
95 | repo_token: ${{ secrets.GITHUB_TOKEN }}
96 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .env
3 | .editorconfig
4 | vendor/
5 | composer.lock
6 | .phpunit.result.cache
7 | .phpunit.cache/
8 |
--------------------------------------------------------------------------------
/.php-version:
--------------------------------------------------------------------------------
1 | 8.1
2 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Vladyslav Podorozhnyi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 
4 | 
5 | 
6 | 
7 | 
8 | 
9 | 
10 |
11 |
12 |
13 |

14 |
15 |
Magento 2 - Auto Proxy to CLI class arguments
16 |
17 |
18 | Automatically injects Proxy for any argument defined in CLI command class constructor.
19 |
20 |
21 |
22 |
23 | ## About The Project
24 |
25 | ### Purpose:
26 | * eliminate issues while installation of your project with a fresh database (usually used with integration tests) - caused by not using Proxy in CLI of 3rd parties: `SQLSTATE[42S02]: Base table or view not found: 1146 Table 'magento2.flag' doesn't exist, query was: SELECT flag.* FROM flag WHERE (flag.flag_code='staging')`
27 | * speed up `php bin/magento` command execution;
28 |
29 | ## Getting Started
30 |
31 | ### Prerequisites
32 | * Magento v2.4.* and upper
33 | * composer v2 and upper
34 |
35 | ### Structure
36 | * magento2-component - see [README.md](component/README.md)
37 | * library - see [README.md](lib/README.md)
38 |
39 | ### Installation
40 |
41 | ```bash
42 | composer req run_as_root/magento-cli-auto-proxy:^1
43 | ```
44 |
45 | ## Roadmap
46 |
47 | - [x] MVP release
48 | - [x] Documentation
49 | - [x] PHP 8 support
50 | - [x] Unit tests coverage
51 | - [ ] Static tests coverage
52 | - [ ] php linting
53 | - [ ] phpcs
54 | - [ ] phpmd
55 | - [x] phpstan
56 | - [ ] Integration tests coverage
57 | - [ ] Pipelines tests automation
58 | - [ ] Static tests
59 | - [x] Unit tests
60 | - [ ] Integration tests
61 | - [ ] Magento multiversions tests
62 |
63 | ## License
64 |
65 | Distributed under the MIT License. See `LICENSE.txt` for more information.
66 |
67 | ## Contact
68 |
69 | [_Vlad Podorozhnyi_](https://github.com/vpodorozh)
70 | Twitter: [](https://twitter.com/vpodorozh)
71 | Email: `vpodorozh@gmail.com` | `vlad.podorozhnyi@run-as-root.sh`
72 |
73 | [_run_as_root GmbH_](https://github.com/run-as-root)
74 | Twitter: [](https://twitter.com/run_as_root)
75 | Email: `info@run-as-root.sh`
76 |
--------------------------------------------------------------------------------
/component/README.md:
--------------------------------------------------------------------------------
1 | ## \[magento2-component\] Cli Constructor Arg Auto Proxy Component
2 |
3 |
4 | Magneto 2 component that provides global DI config via `app/etc/di.xml` to create entry point for configuration enrichment.
5 |
--------------------------------------------------------------------------------
/component/app/etc/cli_arg_auto_proxy/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "run_as_root/magento-cli-auto-proxy",
3 | "description": "Makes all Magento CLI commands construct dependencies be injected as Proxy.",
4 | "type": "magento2-component",
5 | "license": "MIT",
6 | "require": {
7 | "php": "^7.4 | ^8",
8 | "magento/framework": ">=102.0.7"
9 | },
10 | "require-dev": {
11 | "roave/security-advisories": "dev-latest",
12 | "squizlabs/php_codesniffer": "^3.7",
13 | "phpcompatibility/php-compatibility": "^9.3",
14 | "phpstan/phpstan": "^1.9",
15 | "phpunit/phpunit": ">9"
16 | },
17 | "repositories": [
18 | {
19 | "type": "composer",
20 | "url": "https://mirror.mage-os.org/",
21 | "only": [
22 | "magento/*"
23 | ]
24 | }
25 | ],
26 | "autoload": {
27 | "psr-4": {
28 | "RunAsRoot\\CliConstructorArgAutoProxy\\": "lib"
29 | }
30 | },
31 | "extra": {
32 | "map": [
33 | [
34 | "component/app/etc/cli_arg_auto_proxy/di.xml",
35 | "app/etc/cli_arg_auto_proxy/di.xml"
36 | ]
37 | ]
38 | },
39 | "config": {
40 | "allow-plugins": {
41 | "magento/composer-dependency-version-audit-plugin": false
42 | }
43 | },
44 | "scripts": {
45 | "sniffer:php7.4": "phpcs -p ./lib --standard=vendor/phpcompatibility/php-compatibility/PHPCompatibility --runtime-set testVersion 7.4",
46 | "sniffer:php8.0": "phpcs -p ./lib --standard=vendor/phpcompatibility/php-compatibility/PHPCompatibility --runtime-set testVersion 8.0",
47 | "sniffer:php8.1": "phpcs -p ./lib --standard=vendor/phpcompatibility/php-compatibility/PHPCompatibility --runtime-set testVersion 8.1",
48 | "sniffer:php8.2": "phpcs -p ./lib --standard=vendor/phpcompatibility/php-compatibility/PHPCompatibility --runtime-set testVersion 8.2",
49 | "sniffer:php8.3": "phpcs -p ./lib --standard=vendor/phpcompatibility/php-compatibility/PHPCompatibility --runtime-set testVersion 8.3",
50 | "phpstan": "phpstan",
51 | "phpunit": "vendor/bin/phpunit -c phpunit.xml"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/run-as-root/magento-cli-auto-proxy/ac6bbb417f8c2876329e64bc4d69e6c6c038fc29/images/logo.png
--------------------------------------------------------------------------------
/lib/Enum/ProxyClassEntityInterfaceEnum.php:
--------------------------------------------------------------------------------
1 | enrichCliConfigWithProxyService = $enrichCliConfigWithProxyService;
21 | $this->logger = $logger;
22 | }
23 |
24 | /**
25 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
26 | */
27 | public function afterRead(Interceptor $subject, array $result, ?string $scope): array
28 | {
29 | if ($scope !== 'global') {
30 | return $result;
31 | }
32 |
33 | try {
34 | return $this->enrichCliConfigWithProxyService->execute($result);
35 | } catch (ReflectionException $exception) {
36 | $this->logger->error((string) $exception);
37 | return $result;
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/lib/Preference/Framework/ObjectManager/Config/Reader/Dom/Interceptor.php:
--------------------------------------------------------------------------------
1 | runPlugin($result, $scope);
17 | }
18 |
19 | /**
20 | * Workaround for plugin execution.
21 | * You can not define plugin over Dom config reader, as it is created before Magento plugin functionality starts.
22 | */
23 | private function runPlugin(array $result, ?string $scope): array
24 | {
25 | /** @var EnrichCliConfigWithProxyPlugin $enrichPlugin */
26 | $enrichPlugin = ObjectManager::getInstance()->get(EnrichCliConfigWithProxyPlugin::class);
27 | return $enrichPlugin->afterRead($this, $result, $scope);
28 | }
29 | }
--------------------------------------------------------------------------------
/lib/README.md:
--------------------------------------------------------------------------------
1 | ## **\[library\] Cli Constructor Arg Auto Proxy**
2 |
3 | ## Features
4 |
5 | ### Auto add proxies as arguments to CLI command classes
6 |
7 | Automatically injects Proxy for any argument defined in CLI command class constructor.
8 | Entry point of functionality is based on DI config reader that is used in both cases - developer and production modes.
9 |
10 | ## Technical Specification
11 |
12 | ### Plugins
13 |
14 | #### `\RunAsRoot\CliConstructorArgAutoProxy\Plugin\Dom\EnrichCliConfigWithProxyPlugin`
15 | * responsible for enriching DI config with Proxies for CLI command constructor arguments;
16 | * executed after DI config reading;
17 | * plugin is executed in not trivial way - via preference on DOM config reader of DI (see section bellow for more details)
18 | * Caller class: `\RunAsRoot\CliConstructorArgAutoProxy\Preference\Framework\ObjectManager\Config\Reader\Dom\Interceptor`
19 |
20 | ### Preferences
21 |
22 | | source-class | custom-class |
23 | |----------------------------------------------------|-----------------------------------------------------------------------|
24 | | Magento\Framework\ObjectManager\Config\Reader\Dom | ...\Preference\Framework\ObjectManager\Config\Reader\Dom\Interceptor |
25 |
26 |
27 | #### `\RunAsRoot\CliConstructorArgAutoProxy\Preference\Framework\ObjectManager\Config\Reader\Dom\Interceptor`
28 | Workaround for plugin execution.
29 | This override has the same purpose as regular Magento 2 Interceptors - hook for calling plugins.
30 | It is not possible to define plugin over DOM config reader, as it is created before Magento plugin functionality starts.
31 | Preference is the only way to hook in.
32 |
33 | ### Services
34 |
35 | #### `\RunAsRoot\CliConstructorArgAutoProxy\Service\EnrichCliConfigWithProxyService`
36 | Enrich provided DI config with proxies for CLI class commands only.
37 |
38 | #### `\RunAsRoot\CliConstructorArgAutoProxy\Service\GetProxiedConstructArgsConfigService`
39 | Receives CLI command constructor arguments types and reformat them to Proxy types.
40 | Using `IsClassEligibleForProxyValidator` to determine is class eligible to be Proxied.
41 |
42 | ### Validator
43 |
44 | #### `\RunAsRoot\CliConstructorArgAutoProxy\Validator\IsClassEligibleForProxyValidator`
45 | Check is Proxy applicable for this specific class.
46 |
47 | ### Mapper
48 |
49 | #### `\RunAsRoot\CliConstructorArgAutoProxy\Mapper\ProxiedConstructArgsToDiConfigMapper`
50 | Adds Proxy DI configs for specific CLI class command to DI configs pool.
51 |
--------------------------------------------------------------------------------
/lib/Service/EnrichCliConfigWithProxyService.php:
--------------------------------------------------------------------------------
1 | classReader = $classReader;
23 | $this->argsConfigService = $argsConfigService;
24 | $this->diConfigMapper = $diConfigMapper;
25 | }
26 |
27 | /**
28 | * @throws ReflectionException
29 | */
30 | public function execute(array $diConfig): array
31 | {
32 | $cliCommandsList = $diConfig[CommandListInterface::class]['arguments']['commands'] ?? null;
33 |
34 | if ($cliCommandsList === null) {
35 | return $diConfig;
36 | }
37 |
38 | foreach ($cliCommandsList as $cliCommandConfig) {
39 |
40 | $cliInstanceClassName = $cliCommandConfig['instance'] ?? null;
41 | if ($cliInstanceClassName === null) {
42 | continue;
43 | }
44 |
45 | $constructConfig = $this->classReader->getConstructor($cliInstanceClassName);
46 | if ($constructConfig === null) {
47 | continue;
48 | }
49 |
50 | $proxiedConstructArgsConfig = $this->argsConfigService->get($constructConfig);
51 | $diConfig = $this->diConfigMapper->map($diConfig, $cliInstanceClassName, $proxiedConstructArgsConfig);
52 | }
53 |
54 | return $diConfig;
55 | }
56 | }
--------------------------------------------------------------------------------
/lib/Service/GetProxiedConstructArgsConfigService.php:
--------------------------------------------------------------------------------
1 | proxyValidator = $proxyValidator;
16 | }
17 |
18 | public function get(array $constructConfig): array
19 | {
20 | $proxiedConstructorParams = [];
21 |
22 | foreach ($constructConfig as $constructorArgument) {
23 | list($paramName, $className) = $constructorArgument;
24 |
25 | try {
26 | $this->proxyValidator->validate($className);
27 | } catch (\Exception $exception) {
28 | continue;
29 | }
30 |
31 | $proxiedConstructorParams[$paramName] = [
32 | 'instance' => $className . ProxyClassEntityInterfaceEnum::PROXY_CLASS_SUFFIX
33 | ];
34 | }
35 |
36 | return $proxiedConstructorParams;
37 | }
38 | }
--------------------------------------------------------------------------------
/lib/Test/Unit/Map/ProxiedConstructArgsToDiConfigMapperTest.php:
--------------------------------------------------------------------------------
1 | sut = new ProxiedConstructArgsToDiConfigMapper();
19 | }
20 |
21 | /**
22 | * @dataProvider mapDataProvider
23 | */
24 | public function test_map(array $diConfig, string $instanceClassName, array $proxiedConstructArgsConfig, array $expected): void
25 | {
26 | $result = $this->sut->map($diConfig, $instanceClassName, $proxiedConstructArgsConfig);
27 | $this->assertEquals($expected, $result);
28 | }
29 |
30 | public static function mapDataProvider(): array
31 | {
32 | return [
33 | 'case1' => [
34 | 'diConfig' => [
35 | 'instance1' => [
36 | 'arguments' => [
37 | 'arg1' => 'value1',
38 | 'arg2' => 'value2',
39 | ],
40 | ],
41 | ],
42 | 'instanceClassName' => 'instance1',
43 | 'proxiedConstructArgsConfig' => [
44 | 'arg3' => 'value3',
45 | 'arg4' => 'value4',
46 | ],
47 | 'expected' => [
48 | 'instance1' => [
49 | 'arguments' => [
50 | 'arg1' => 'value1',
51 | 'arg2' => 'value2',
52 | 'arg3' => 'value3',
53 | 'arg4' => 'value4',
54 | ],
55 | ],
56 | ],
57 | ],
58 | 'case2' => [
59 | 'diConfig' => [
60 | 'instance1' => [],
61 | ],
62 | 'instanceClassName' => 'instance1',
63 | 'proxiedConstructArgsConfig' => [
64 | 'arg3' => 'value3',
65 | 'arg4' => 'value4',
66 | ],
67 | 'expected' => [
68 | 'instance1' => [
69 | 'arguments' => [
70 | 'arg4' => 'value4',
71 | 'arg3' => 'value3',
72 | ],
73 | ],
74 | ]
75 | ],
76 | 'case3' => [
77 | 'diConfig' => [
78 | 'instance1' => [
79 | 'arguments' => [
80 | 'arg1' => 'value1',
81 | 'arg2' => 'value2',
82 | ],
83 | ],
84 | ],
85 | 'instanceClassName' => 'instance1',
86 | 'proxiedConstructArgsConfig' => [],
87 | 'expected' => [
88 | 'instance1' => [
89 | 'arguments' => [
90 | 'arg1' => 'value1',
91 | 'arg2' => 'value2',
92 | ],
93 | ],
94 | ],
95 | ],
96 | 'case4' => [
97 | 'diConfig' => [
98 | 'instance1' => [],
99 | ],
100 | 'instanceClassName' => 'instance1',
101 | 'proxiedConstructArgsConfig' => [],
102 | 'expected' => [
103 | 'instance1' => [
104 | 'arguments' => [],
105 | ],
106 | ],
107 | ],
108 |
109 | ];
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/lib/Test/Unit/Plugin/Dom/EnrichCliConfigWithProxyPluginTest.php:
--------------------------------------------------------------------------------
1 | service = $this->createMock(EnrichCliConfigWithProxyService::class);
21 | $this->logger = $this->createMock(LoggerInterface::class);
22 | $this->sut = new EnrichCliConfigWithProxyPlugin($this->service, $this->logger);
23 | }
24 |
25 | public function test_after_read(): void
26 | {
27 | $subject = $this->createMock(Interceptor::class);
28 | $result = ['foo' => 'bar'];
29 | $scope = 'global';
30 |
31 | $this->service->expects($this->once())->method('execute')->with($result)
32 | ->willReturn(['abc' => 'def']);
33 | $this->logger->expects($this->never())->method('error');
34 |
35 | $this->assertSame(['abc' => 'def'], $this->sut->afterRead($subject, $result, $scope));
36 | }
37 |
38 | public function test_after_read_with_non_global_scope(): void
39 | {
40 | $subject = $this->createMock(Interceptor::class);
41 | $result = ['foo' => 'bar'];
42 | $scope = 'foo';
43 |
44 | $this->service->expects($this->never())->method('execute');
45 | $this->logger->expects($this->never())->method('error');
46 |
47 | $this->assertSame($result, $this->sut->afterRead($subject, $result, $scope));
48 | }
49 |
50 | public function test_after_read_with_exception(): void
51 | {
52 | $subject = $this->createMock(Interceptor::class);
53 | $result = ['foo' => 'bar'];
54 | $scope = 'global';
55 |
56 | $this->service->expects($this->once())->method('execute')
57 | ->with($result)->willThrowException(new \ReflectionException('foo'));
58 | $this->logger->expects($this->once())->method('error');
59 |
60 | $this->assertSame($result, $this->sut->afterRead($subject, $result, $scope));
61 | }
62 | }
--------------------------------------------------------------------------------
/lib/Test/Unit/Preference/Framework/ObjectManager/Config/Reader/Dom/InterceptorTest.php:
--------------------------------------------------------------------------------
1 | fileResolver = $this->createMock(FileResolverInterface::class);
23 | $converter = $this->createMock(Dom::class);
24 | $schemaLocator = $this->createMock(SchemaLocator::class);
25 | $validationState = $this->createMock(ValidationStateInterface::class);
26 |
27 | $this->sut = new Interceptor(
28 | $this->fileResolver,
29 | $converter,
30 | $schemaLocator,
31 | $validationState
32 | );
33 | }
34 |
35 | public function test_read(): void
36 | {
37 | $this->fileResolver->method('get')->willReturn([]);
38 |
39 | $pluginMock = $this->createMock(EnrichCliConfigWithProxyPlugin::class);
40 | $pluginRes = ['some' => 'result'];
41 | $pluginMock->method('afterRead')->willReturn($pluginRes);
42 |
43 | $objMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class);
44 | $objMock->method('get')->with(EnrichCliConfigWithProxyPlugin::class)
45 | ->willReturn($pluginMock);
46 |
47 | ObjectManager::setInstance($objMock);
48 |
49 | $this->assertEquals($pluginRes, $this->sut->read('global'));
50 | }
51 | }
--------------------------------------------------------------------------------
/lib/Test/Unit/Service/EnrichCliConfigWithProxyServiceTest.php:
--------------------------------------------------------------------------------
1 | classReader = $this->createMock(ClassReaderInterface::class);
26 | $this->argsConfigService = $this->createMock(GetProxiedConstructArgsConfigService::class);
27 | $this->diConfigMapper = $this->createMock(ProxiedConstructArgsToDiConfigMapper::class);
28 | $this->sut = new EnrichCliConfigWithProxyService(
29 | $this->argsConfigService,
30 | $this->classReader,
31 | $this->diConfigMapper
32 | );
33 | }
34 |
35 | public function test_execute(): void
36 | {
37 | $diConfig = [
38 | 'some' => 'config',
39 | ];
40 |
41 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
42 | }
43 |
44 | public function test_execute_with_cli_commands(): void
45 | {
46 | $diConfig = [
47 | 'some' => 'config',
48 | 'Magento\Framework\Console\CommandListInterface' => [
49 | 'arguments' => [
50 | 'commands' => [
51 | [
52 | 'instance' => 'Some\CliCommand',
53 | ],
54 | ],
55 | ],
56 | ],
57 | ];
58 |
59 | $this->classReader->expects($this->once())
60 | ->method('getConstructor')
61 | ->with('Some\CliCommand')
62 | ->willReturn([]);
63 |
64 | $this->argsConfigService->expects($this->once())
65 | ->method('get')
66 | ->with([])
67 | ->willReturn([]);
68 |
69 | $this->diConfigMapper->expects($this->once())
70 | ->method('map')
71 | ->with($diConfig, 'Some\CliCommand', [])
72 | ->willReturn($diConfig);
73 |
74 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
75 | }
76 |
77 | public function test_execute_with_cli_commands_and_no_construct(): void
78 | {
79 | $diConfig = [
80 | 'some' => 'config',
81 | 'Magento\Framework\Console\CommandListInterface' => [
82 | 'arguments' => [
83 | 'commands' => [
84 | [
85 | 'instance' => 'Some\CliCommand',
86 | ],
87 | ],
88 | ],
89 | ],
90 | ];
91 |
92 | $this->classReader->expects($this->once())
93 | ->method('getConstructor')
94 | ->with('Some\CliCommand')
95 | ->willReturn(null);
96 |
97 | $this->argsConfigService->expects($this->never())
98 | ->method('get');
99 |
100 | $this->diConfigMapper->expects($this->never())
101 | ->method('map');
102 |
103 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
104 | }
105 |
106 | public function test_execute_with_cli_commands_and_no_instance(): void
107 | {
108 | $diConfig = [
109 | 'some' => 'config',
110 | 'Magento\Framework\Console\CommandListInterface' => [
111 | 'arguments' => [
112 | 'commands' => [
113 | [
114 | 'some' => 'config',
115 | ],
116 | ],
117 | ],
118 | ],
119 | ];
120 |
121 | $this->classReader->expects($this->never())
122 | ->method('getConstructor');
123 |
124 | $this->argsConfigService->expects($this->never())
125 | ->method('get');
126 |
127 | $this->diConfigMapper->expects($this->never())
128 | ->method('map');
129 |
130 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
131 | }
132 |
133 | public function test_execute_with_cli_commands_and_no_commands(): void
134 | {
135 | $diConfig = [
136 | 'some' => 'config',
137 | 'Magento\Framework\Console\CommandListInterface' => [
138 | 'arguments' => [
139 | 'some' => 'config',
140 | ],
141 | ],
142 | ];
143 |
144 | $this->classReader->expects($this->never())
145 | ->method('getConstructor');
146 |
147 | $this->argsConfigService->expects($this->never())
148 | ->method('get');
149 |
150 | $this->diConfigMapper->expects($this->never())
151 | ->method('map');
152 |
153 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
154 | }
155 |
156 | public function test_execute_with_cli_commands_and_no_arguments(): void
157 | {
158 | $diConfig = [
159 | 'some' => 'config',
160 | 'Magento\Framework\Console\CommandListInterface' => [
161 | 'some' => 'config',
162 | ],
163 | ];
164 |
165 | $this->classReader->expects($this->never())
166 | ->method('getConstructor');
167 |
168 | $this->argsConfigService->expects($this->never())
169 | ->method('get');
170 |
171 | $this->diConfigMapper->expects($this->never())
172 | ->method('map');
173 |
174 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
175 | }
176 |
177 | public function test_execute_with_cli_commands_and_no_command_list_interface(): void
178 | {
179 | $diConfig = [
180 | 'some' => 'config',
181 | ];
182 |
183 | $this->classReader->expects($this->never())
184 | ->method('getConstructor');
185 |
186 | $this->argsConfigService->expects($this->never())
187 | ->method('get');
188 |
189 | $this->diConfigMapper->expects($this->never())
190 | ->method('map');
191 |
192 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
193 | }
194 |
195 | public function test_execute_with_cli_commands_and_no_arguments_key(): void
196 | {
197 | $diConfig = [
198 | 'some' => 'config',
199 | 'Magento\Framework\Console\CommandListInterface' => [
200 | 'some' => 'config',
201 | ],
202 | ];
203 |
204 | $this->classReader->expects($this->never())
205 | ->method('getConstructor');
206 |
207 | $this->argsConfigService->expects($this->never())
208 | ->method('get');
209 |
210 | $this->diConfigMapper->expects($this->never())
211 | ->method('map');
212 |
213 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
214 | }
215 |
216 | public function test_execute_with_cli_commands_and_no_commands_key(): void
217 | {
218 | $diConfig = [
219 | 'some' => 'config',
220 | 'Magento\Framework\Console\CommandListInterface' => [
221 | 'arguments' => [
222 | 'some' => 'config',
223 | ],
224 | ],
225 | ];
226 |
227 | $this->classReader->expects($this->never())
228 | ->method('getConstructor');
229 |
230 | $this->argsConfigService->expects($this->never())
231 | ->method('get');
232 |
233 | $this->diConfigMapper->expects($this->never())
234 | ->method('map');
235 |
236 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
237 | }
238 |
239 | public function test_execute_with_cli_commands_and_no_instance_key(): void
240 | {
241 | $diConfig = [
242 | 'some' => 'config',
243 | 'Magento\Framework\Console\CommandListInterface' => [
244 | 'arguments' => [
245 | 'commands' => [
246 | [
247 | 'some' => 'config',
248 | ],
249 | ],
250 | ],
251 | ],
252 | ];
253 |
254 | $this->classReader->expects($this->never())
255 | ->method('getConstructor');
256 |
257 | $this->argsConfigService->expects($this->never())
258 | ->method('get');
259 |
260 | $this->diConfigMapper->expects($this->never())
261 | ->method('map');
262 |
263 | $this->assertEquals($diConfig, $this->sut->execute($diConfig));
264 | }
265 | }
--------------------------------------------------------------------------------
/lib/Test/Unit/Service/GetProxiedConstructArgsConfigServiceTest.php:
--------------------------------------------------------------------------------
1 | proxyValidator = $this->createMock(IsClassEligibleForProxyValidator::class);
22 | $this->sut = new GetProxiedConstructArgsConfigService($this->proxyValidator);
23 | }
24 |
25 | public function test_get(): void
26 | {
27 | $constructConfig = [
28 | ['some', 'Some\Class'],
29 | ['someOther', 'Some\OtherClass'],
30 | ];
31 |
32 | $this->proxyValidator->expects($this->exactly(2))
33 | ->method('validate')
34 | ->willReturnCallback(function () use (&$i) {
35 | switch ($i) {
36 | case 0:
37 | return ['Some\Class'];
38 | case 1:
39 | return ['Some\OtherClass'];
40 | }
41 | $i++;
42 |
43 | return [];
44 | })
45 | ->willReturnOnConsecutiveCalls(true, false);
46 |
47 | $this->assertEquals(
48 | [
49 | 'some' => [
50 | 'instance' => 'Some\Class\Proxy',
51 | ],
52 | 'someOther' => [
53 | 'instance' => 'Some\OtherClass\Proxy',
54 | ],
55 | ],
56 | $this->sut->get($constructConfig)
57 | );
58 | }
59 |
60 | public function test_get_with_no_eligible_classes(): void
61 | {
62 | $constructConfig = [
63 | ['some', 'Some\Class'],
64 | ['someOther', 'Some\OtherClass'],
65 | ];
66 |
67 | $this->proxyValidator->expects($this->exactly(2))
68 | ->method('validate')
69 | ->willReturnCallback(function () use (&$i) {
70 | switch ($i) {
71 | case 0:
72 | return ['Some\Class'];
73 | case 1:
74 | return ['Some\OtherClass'];
75 | }
76 | $i++;
77 |
78 | return [];
79 | })
80 | ->willThrowException(new ClassIsNotEligibleForProxyException());
81 |
82 | $this->assertEquals([], $this->sut->get($constructConfig));
83 | }
84 |
85 | public function testGetWithNoConstructConfig(): void
86 | {
87 | $this->assertEquals([], $this->sut->get([]));
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/Validator/IsClassEligibleForProxyValidator.php:
--------------------------------------------------------------------------------
1 | 0) {
21 | throw new ClassIsNotEligibleForProxyException('Class is already a Proxy');
22 | }
23 |
24 | // skipp - in case Proxy exists and is not a child of original class
25 | if (!is_a(
26 | $className . ProxyClassEntityInterfaceEnum::PROXY_CLASS_SUFFIX,
27 | $className,
28 | true
29 | )) {
30 | throw new ClassIsNotEligibleForProxyException(
31 | 'Proxy already exists and is not a child of original class: ' . $className
32 | );
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/phpstan.neon.dist:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 5
3 | paths:
4 | - lib
5 | excludePaths:
6 | analyseAndScan:
7 | - lib/Test
8 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | lib/Test/Unit
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | lib
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------