├── .editorconfig
├── .github
├── FUNDING.yaml
└── workflows
│ └── code_analysis.yaml
├── LICENSE
├── composer.json
├── config
└── config.php
├── phpunit.xml
└── src
├── CaseConverter
├── AliasCaseConverter.php
├── ClassServiceCaseConverter.php
├── ConfiguredServiceCaseConverter.php
├── ExtensionConverter.php
├── ImportCaseConverter.php
├── NameOnlyServiceCaseConverter.php
├── NestedCaseConverter
│ └── InstanceOfNestedCaseConverter.php
├── ParameterCaseConverter.php
├── ResourceCaseConverter.php
└── ServicesDefaultsCaseConverter.php
├── Contract
├── CaseConverterInterface.php
├── Converter
│ └── ServiceOptionsKeyYamlToPhpFactoryInterface.php
├── NodeVisitor
│ └── PrePrintNodeVisitorInterface.php
└── RoutingCaseConverterInterface.php
├── Enum
└── RouteOption.php
├── Exception
├── NotImplementedYetException.php
└── ShouldNotHappenException.php
├── ExprResolver
├── ServiceReferenceExprResolver.php
├── StringExprResolver.php
├── TaggedReturnsCloneResolver.php
└── TaggedServiceResolver.php
├── Naming
├── ClassNaming.php
├── ReferenceFunctionNameResolver.php
└── VariableNameResolver.php
├── NodeFactory
├── ArgsNodeFactory.php
├── CommonNodeFactory.php
├── ConstantNodeFactory.php
├── ContainerConfiguratorReturnClosureFactory.php
├── ContainerNestedNodesFactory.php
├── NewValueObjectFactory.php
├── RoutingConfiguratorReturnClosureFactory.php
└── Service
│ ├── AutoBindNodeFactory.php
│ ├── ServiceOptionNodeFactory.php
│ ├── ServicesPhpNodeFactory.php
│ └── SingleServicePhpNodeFactory.php
├── NodeFinder
└── TypeAwareNodeFinder.php
├── NodeModifier
└── SingleFactoryReferenceNodeModifier.php
├── NodeTraverser
└── ImportFullyQualifiedNamesNodeTraverser.php
├── NodeVisitor
└── ImportFullyQualifiedNamesNodeVisitor.php
├── PhpParser
└── NodeFactory
│ └── ConfiguratorClosureNodeFactory.php
├── Printer
├── ArrayDecorator
│ └── ServiceConfigurationDecorator.php
├── NodeDecorator
│ └── EmptyLineNodeDecorator.php
├── PhpParserPhpConfigPrinter.php
└── SmartPhpConfigPrinter.php
├── Provider
└── CurrentFilePathProvider.php
├── Reflection
└── ConstantNameFromValueResolver.php
├── Routing
└── ControllerSplitter.php
├── RoutingCaseConverter
├── ConditionalEnvRoutingCaseConverter.php
├── ImportRoutingCaseConverter.php
└── PathRoutingCaseConverter.php
├── ServiceOptionAnalyzer
└── ServiceOptionAnalyzer.php
├── ServiceOptionConverter
├── AbstractServiceOptionKeyYamlToPhpFactory.php
├── ArgumentsServiceOptionKeyYamlToPhpFactory.php
├── AutowiringTypesOptionKeyYamlToPhpFactory.php
├── BindAutowireAutoconfigureServiceOptionKeyYamlToPhpFactory.php
├── CallsServiceOptionKeyYamlToPhpFactory.php
├── DecoratesServiceOptionKeyYamlToPhpFactory.php
├── DeprecatedServiceOptionKeyYamlToPhpFactory.php
├── FactoryConfiguratorServiceOptionKeyYamlToPhpFactory.php
├── ParentLazyServiceOptionKeyYamlToPhpFactory.php
├── PropertiesServiceOptionKeyYamlToPhpFactory.php
├── SharedPublicServiceOptionKeyYamlToPhpFactory.php
└── TagsServiceOptionKeyYamlToPhpFactory.php
├── Sorter
├── FullyQualifiedImportSorter.php
└── YamlArgumentSorter.php
├── StringFormatConverter.php
├── ValueObject
├── AttributeKey.php
├── FullyQualifiedImport.php
├── FunctionName.php
├── ImportType.php
├── MethodName.php
├── PhpConfigPrinterConfig.php
├── Routing
│ └── RouteDefaults.php
├── VariableMethodName.php
├── VariableName.php
├── YamlKey.php
└── YamlServiceKey.php
└── Yaml
└── CheckerServiceParametersShifter.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.github/FUNDING.yaml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | github: tomasvotruba
3 |
--------------------------------------------------------------------------------
/.github/workflows/code_analysis.yaml:
--------------------------------------------------------------------------------
1 | name: Code Analysis
2 |
3 | on:
4 | pull_request: null
5 | push:
6 | branches:
7 | - main
8 |
9 | env:
10 | # see https://github.com/composer/composer/issues/9368#issuecomment-718112361
11 | COMPOSER_ROOT_VERSION: "dev-main"
12 |
13 | jobs:
14 | code_analysis:
15 | strategy:
16 | fail-fast: false
17 | matrix:
18 | actions:
19 | -
20 | name: 'PHPStan'
21 | run: composer phpstan --ansi
22 |
23 | -
24 | name: 'Composer Validate'
25 | run: composer validate --ansi
26 |
27 | -
28 | name: 'Rector'
29 | run: composer rector --ansi
30 |
31 | -
32 | name: 'Tests'
33 | run: vendor/bin/phpunit
34 |
35 | -
36 | name: 'Check Active Classes'
37 | run: vendor/bin/class-leak check src --ansi --skip-type="\Symplify\PhpConfigPrinter\Contract\Converter\ServiceOptionsKeyYamlToPhpFactoryInterface" --skip-type="\Symplify\PhpConfigPrinter\Contract\RoutingCaseConverterInterface" --skip-type="\Symplify\PhpConfigPrinter\Contract\CaseConverterInterface"
38 |
39 | name: ${{ matrix.actions.name }}
40 | runs-on: ubuntu-latest
41 |
42 | steps:
43 | - uses: actions/checkout@v3
44 | # see https://github.com/shivammathur/setup-php
45 | - uses: shivammathur/setup-php@v2
46 | with:
47 | php-version: 8.2
48 | coverage: none
49 |
50 | # composer install cache - https://github.com/ramsey/composer-install
51 | - uses: "ramsey/composer-install@v2"
52 |
53 | - run: ${{ matrix.actions.run }}
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 | ---------------
3 |
4 | Copyright (c) 2020 Tomas Votruba (https://tomasvotruba.com)
5 |
6 | Permission is hereby granted, free of charge, to any person
7 | obtaining a copy of this software and associated documentation
8 | files (the "Software"), to deal in the Software without
9 | restriction, including without limitation the rights to use,
10 | copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the
12 | Software is furnished to do so, subject to the following
13 | conditions:
14 |
15 | The above copyright notice and this permission notice shall be
16 | included in all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "symplify/php-config-printer",
3 | "description": "Print Symfony services array with configuration to to plain PHP file format thanks to this simple php-parser wrapper",
4 | "license": "MIT",
5 | "require": {
6 | "php": ">=8.2",
7 | "nette/utils": "^3.2",
8 | "nikic/php-parser": "^5.3",
9 | "symfony/yaml": "^6.4"
10 | },
11 | "require-dev": {
12 | "myclabs/php-enum": "^1.8",
13 | "phpstan/extension-installer": "^1.4",
14 | "phpstan/phpstan": "^2.1",
15 | "phpunit/phpunit": "^10.5",
16 | "rector/rector": "^2.0",
17 | "phpecs/phpecs": "^2.0",
18 | "symplify/easy-testing": "^11.1",
19 | "symplify/phpstan-extensions": "^12.0",
20 | "tomasvotruba/class-leak": "^2.0"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "Symplify\\PhpConfigPrinter\\": "src"
25 | }
26 | },
27 | "autoload-dev": {
28 | "psr-4": {
29 | "Symplify\\PhpConfigPrinter\\Tests\\": "tests"
30 | },
31 | "files": [
32 | "tests/Printer/SmartPhpConfigPrinter/Source/custom_inline_objects_function.php",
33 | "tests/Printer/SmartPhpConfigPrinter/Source/custom_inline_object_function.php"
34 | ]
35 | },
36 | "scripts": {
37 | "check-cs": "vendor/bin/ecs check --ansi",
38 | "fix-cs": "vendor/bin/ecs check --fix --ansi",
39 | "phpstan": "vendor/bin/phpstan analyse --ansi",
40 | "rector": "vendor/bin/rector process --dry-run --ansi"
41 | },
42 | "config": {
43 | "sort-packages": true,
44 | "allow-plugins": {
45 | "cweagans/composer-patches": true,
46 | "phpstan/extension-installer": true
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/config/config.php:
--------------------------------------------------------------------------------
1 | services();
20 |
21 | $services->defaults()
22 | ->public()
23 | ->autowire();
24 |
25 | $services->load('Symplify\PhpConfigPrinter\\', __DIR__ . '/../src')
26 | ->exclude([__DIR__ . '/../src/ValueObject']);
27 |
28 | $services->load(
29 | 'Symplify\\PhpConfigPrinter\\CaseConverter\\',
30 | __DIR__ . '/../src/CaseConverter'
31 | )
32 | ->exclude(__DIR__ . '/../src/CaseConverter/NestedCaseConverter/InstanceOfNestedCaseConverter.php')
33 | ->tag(CaseConverterInterface::class);
34 |
35 | $services->load(
36 | 'Symplify\\PhpConfigPrinter\\RoutingCaseConverter\\',
37 | __DIR__ . '/../src/RoutingCaseConverter'
38 | )->tag(RoutingCaseConverterInterface::class);
39 |
40 |
41 | $services->load(
42 | 'Symplify\\PhpConfigPrinter\\ServiceOptionConverter\\',
43 | __DIR__ . '/../src/ServiceOptionConverter'
44 | )->tag(ServiceOptionsKeyYamlToPhpFactoryInterface::class);
45 |
46 | $services->set(ContainerConfiguratorReturnClosureFactory::class)
47 | ->arg('$caseConverters', tagged_iterator(CaseConverterInterface::class));
48 |
49 | $services->set(RoutingConfiguratorReturnClosureFactory::class)
50 | ->arg('$routingCaseConverters', tagged_iterator(RoutingCaseConverterInterface::class));
51 |
52 | $services->set(\Symplify\PhpConfigPrinter\NodeFactory\Service\ServiceOptionNodeFactory::class)
53 | ->arg('$serviceOptionKeyYamlToPhpFactories', tagged_iterator(ServiceOptionsKeyYamlToPhpFactoryInterface::class));
54 |
55 | $services->set(\Symplify\PhpConfigPrinter\Printer\PhpParserPhpConfigPrinter::class)
56 | ->arg('$prePrintNodeVisitors', tagged_iterator(PrePrintNodeVisitorInterface::class));
57 |
58 | $services->set(NodeFinder::class);
59 | $services->set(Parser::class);
60 | $services->set(BuilderFactory::class);
61 | $services->set(ParentConnectingVisitor::class);
62 | };
63 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | tests
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/CaseConverter/AliasCaseConverter.php:
--------------------------------------------------------------------------------
1 | \w+)#';
31 |
32 | /**
33 | * @see https://regex101.com/r/DDuuVM/1
34 | * @var string
35 | */
36 | private const NAMED_ALIAS_REGEX = '#\w+\s+\$\w+#';
37 |
38 | public function __construct(
39 | private CommonNodeFactory $commonNodeFactory,
40 | private ArgsNodeFactory $argsNodeFactory,
41 | private ServiceOptionNodeFactory $serviceOptionNodeFactory,
42 | ) {
43 | }
44 |
45 | public function convertToMethodCallStmt(mixed $key, mixed $values): Stmt
46 | {
47 | if (! is_string($key)) {
48 | throw new ShouldNotHappenException();
49 | }
50 |
51 | $servicesVariable = new Variable(VariableName::SERVICES);
52 | if ($this->doesClassLikeExist($key)) {
53 | return $this->createFromClassLike($key, $values, $servicesVariable);
54 | }
55 |
56 | // handles: "SomeClass $someVariable: ..."
57 | $fullClassName = Strings::before($key, ' $');
58 | if ($fullClassName !== null) {
59 | $methodCall = $this->createAliasNode($key, $fullClassName, $values);
60 | return new Expression($methodCall);
61 | }
62 |
63 | if (is_string($values) && $values[0] === '@') {
64 | $args = $this->argsNodeFactory->createFromValues([$key, $values], true);
65 | $methodCall = new MethodCall($servicesVariable, MethodName::ALIAS, $args);
66 | return new Expression($methodCall);
67 | }
68 |
69 | if (is_array($values)) {
70 | return $this->createFromArrayValues($values, $key, $servicesVariable);
71 | }
72 |
73 | throw new ShouldNotHappenException();
74 | }
75 |
76 | public function match(string $rootKey, mixed $key, mixed $values): bool
77 | {
78 | if ($rootKey !== YamlKey::SERVICES) {
79 | return false;
80 | }
81 |
82 | if (isset($values[YamlKey::ALIAS])) {
83 | return true;
84 | }
85 |
86 | if (Strings::match($key, self::NAMED_ALIAS_REGEX)) {
87 | return true;
88 | }
89 |
90 | if (! is_string($values)) {
91 | return false;
92 | }
93 |
94 | return $values[0] === '@';
95 | }
96 |
97 | private function createAliasNode(string $key, string $fullClassName, mixed $serviceValues): MethodCall
98 | {
99 | $args = [];
100 |
101 | $classConstFetch = $this->commonNodeFactory->createClassReference($fullClassName);
102 |
103 | Strings::match($key, self::ARGUMENT_NAME_REGEX);
104 | $argumentName = '$' . Strings::after($key, '$');
105 |
106 | $concat = new Concat($classConstFetch, new String_(' ' . $argumentName));
107 | $args[] = new Arg($concat);
108 |
109 | $serviceName = ltrim((string) $serviceValues, '@');
110 | $args[] = new Arg(new String_($serviceName));
111 |
112 | return new MethodCall(new Variable(VariableName::SERVICES), MethodName::ALIAS, $args);
113 | }
114 |
115 | private function createFromClassLike(string $key, mixed $values, Variable $servicesVariable): Expression
116 | {
117 | $classConstFetch = $this->commonNodeFactory->createClassReference($key);
118 |
119 | $argValues = [];
120 | $argValues[] = $classConstFetch;
121 | $argValues[] = $values[MethodName::ALIAS] ?? $values;
122 |
123 | $args = $this->argsNodeFactory->createFromValues($argValues, true);
124 | $methodCall = new MethodCall($servicesVariable, MethodName::ALIAS, $args);
125 |
126 | return new Expression($methodCall);
127 | }
128 |
129 | private function createFromAlias(string $serviceName, string $key, Variable $servicesVariable): MethodCall
130 | {
131 | if ($this->doesClassLikeExist($serviceName)) {
132 | $classReference = $this->commonNodeFactory->createClassReference($serviceName);
133 | $args = $this->argsNodeFactory->createFromValues([$key, $classReference]);
134 | } else {
135 | $args = $this->argsNodeFactory->createFromValues([$key, $serviceName]);
136 | }
137 |
138 | return new MethodCall($servicesVariable, MethodName::ALIAS, $args);
139 | }
140 |
141 | /**
142 | * @param mixed[] $values
143 | */
144 | private function createFromArrayValues(array $values, string $key, Variable $servicesVariable): Expression
145 | {
146 | if (isset($values[MethodName::ALIAS])) {
147 | $methodCall = $this->createFromAlias($values[MethodName::ALIAS], $key, $servicesVariable);
148 | unset($values[MethodName::ALIAS]);
149 | } else {
150 | throw new ShouldNotHappenException();
151 | }
152 |
153 | /** @var MethodCall $methodCall */
154 | $methodCall = $this->serviceOptionNodeFactory->convertServiceOptionsToNodes($values, $methodCall);
155 | return new Expression($methodCall);
156 | }
157 |
158 | private function doesClassLikeExist(string $class): bool
159 | {
160 | if (class_exists($class)) {
161 | return true;
162 | }
163 |
164 | if (interface_exists($class)) {
165 | return true;
166 | }
167 |
168 | return trait_exists($class);
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/src/CaseConverter/ClassServiceCaseConverter.php:
--------------------------------------------------------------------------------
1 | argsNodeFactory->createFromValues([$key, $values[YamlKey::CLASS_KEY]]);
29 | $methodCall = new MethodCall(new Variable(VariableName::SERVICES), MethodName::SET, $args);
30 |
31 | unset($values[YamlKey::CLASS_KEY]);
32 |
33 | $decoratedMethodCall = $this->serviceOptionNodeFactory->convertServiceOptionsToNodes($values, $methodCall);
34 | return new Expression($decoratedMethodCall);
35 | }
36 |
37 | public function match(string $rootKey, mixed $key, mixed $values): bool
38 | {
39 | if ($rootKey !== YamlKey::SERVICES) {
40 | return false;
41 | }
42 |
43 | if (is_array($values) && count($values) !== 1) {
44 | return false;
45 | }
46 |
47 | if (! isset($values[YamlKey::CLASS_KEY])) {
48 | return false;
49 | }
50 |
51 | return ! isset($values[YamlKey::ALIAS]);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/CaseConverter/ConfiguredServiceCaseConverter.php:
--------------------------------------------------------------------------------
1 | argsNodeFactory->createFromValues($valuesForArgs);
35 | $methodCall = new MethodCall(new Variable(VariableName::SERVICES), MethodName::SET, $args);
36 |
37 | $decoratedMethodCall = $this->serviceOptionNodeFactory->convertServiceOptionsToNodes($values, $methodCall);
38 | return new Expression($decoratedMethodCall);
39 | }
40 |
41 | public function match(string $rootKey, mixed $key, mixed $values): bool
42 | {
43 | if ($rootKey !== YamlKey::SERVICES) {
44 | return false;
45 | }
46 |
47 | if ($key === YamlKey::_DEFAULTS) {
48 | return false;
49 | }
50 |
51 | if ($key === YamlKey::_INSTANCEOF) {
52 | return false;
53 | }
54 |
55 | if (isset($values[YamlKey::RESOURCE])) {
56 | return false;
57 | }
58 |
59 | // handled by @see \Symplify\PhpConfigPrinter\CaseConverter\CaseConverter\AliasCaseConverter
60 | if ($this->isAlias($values)) {
61 | return false;
62 | }
63 |
64 | if ($values === null) {
65 | return false;
66 | }
67 |
68 | if (array_key_exists('configure', $values)) {
69 | return true;
70 | }
71 |
72 | return $values !== [];
73 | }
74 |
75 | private function isAlias(mixed $values): bool
76 | {
77 | if (isset($values[YamlKey::ALIAS])) {
78 | return true;
79 | }
80 |
81 | if (! is_string($values)) {
82 | return false;
83 | }
84 |
85 | return \str_starts_with($values, '@');
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/CaseConverter/ExtensionConverter.php:
--------------------------------------------------------------------------------
1 | argsNodeFactory->createFromValues([
29 | $this->rootKey,
30 | [
31 | $key => $values,
32 | ],
33 | ]);
34 |
35 | $containerConfiguratorVariable = new Variable(VariableName::CONTAINER_CONFIGURATOR);
36 | $methodCall = new MethodCall($containerConfiguratorVariable, MethodName::EXTENSION, $args);
37 |
38 | return new Expression($methodCall);
39 | }
40 |
41 | public function match(string $rootKey, mixed $key, mixed $values): bool
42 | {
43 | $this->rootKey = $rootKey;
44 | return ! in_array($rootKey, YamlKey::provideRootKeys(), true);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/CaseConverter/ImportCaseConverter.php:
--------------------------------------------------------------------------------
1 | yamlArgumentSorter->sortArgumentsByKeyIfExists($values, [
53 | YamlKey::RESOURCE => '',
54 | 'type' => null,
55 | YamlKey::IGNORE_ERRORS => false,
56 | ]);
57 | return $this->createImportMethodCall($arguments);
58 | }
59 |
60 | if (is_string($values)) {
61 | return $this->createImportMethodCall([$values]);
62 | }
63 |
64 | throw new NotImplementedYetException();
65 | }
66 |
67 | /**
68 | * @param mixed[] $arguments
69 | */
70 | private function createImportMethodCall(array $arguments): Expression
71 | {
72 | $containerConfiguratorVariable = new Variable(VariableName::CONTAINER_CONFIGURATOR);
73 |
74 | $args = $this->createArgs($arguments);
75 | $methodCall = new MethodCall($containerConfiguratorVariable, 'import', $args);
76 |
77 | return new Expression($methodCall);
78 | }
79 |
80 | /**
81 | * @param array $arguments
82 | * @return Arg[]
83 | */
84 | private function createArgs(array $arguments): array
85 | {
86 | $args = [];
87 | foreach ($arguments as $name => $value) {
88 | if (is_string($name) && $this->shouldSkipDefaultValue($name, $value, $arguments)) {
89 | continue;
90 | }
91 |
92 | $expr = $this->resolveExpr($value);
93 | $args[] = new Arg($expr);
94 | }
95 |
96 | return $args;
97 | }
98 |
99 | /**
100 | * @param mixed[] $arguments
101 | */
102 | private function shouldSkipDefaultValue(string $name, mixed $value, array $arguments): bool
103 | {
104 | // skip default value for "ignore_errors"
105 | if ($name === YamlKey::IGNORE_ERRORS && $value === false) {
106 | return true;
107 | }
108 |
109 | // check if default value for "type"
110 | if ($name !== 'type') {
111 | return false;
112 | }
113 |
114 | if ($value !== null) {
115 | return false;
116 | }
117 |
118 | // follow by default value for "ignore_errors"
119 | if (! isset($arguments[YamlKey::IGNORE_ERRORS])) {
120 | return false;
121 | }
122 |
123 | return $arguments[YamlKey::IGNORE_ERRORS] === false;
124 | }
125 |
126 | private function replaceImportedFileSuffix(mixed $value): mixed
127 | {
128 | if (! is_string($value)) {
129 | return $value;
130 | }
131 |
132 | return Strings::replace($value, self::INPUT_SUFFIX_REGEX, '.php');
133 | }
134 |
135 | private function resolveExpr(mixed $value): Expr
136 | {
137 | $expr = $this->processNormalizedValue($value);
138 | if ($expr instanceof Expr) {
139 | return $expr;
140 | }
141 |
142 | if ($value === 'not_found') {
143 | return new String_('not_found');
144 | }
145 |
146 | if (is_string($value) && \str_contains($value, '::')) {
147 | [$className, $constantName] = explode('::', $value);
148 | return new ClassConstFetch(new FullyQualified($className), $constantName);
149 | }
150 |
151 | if (is_string($value) && \str_starts_with($value, '@')) {
152 | return new String_($value);
153 | }
154 |
155 | $value = $this->replaceImportedFileSuffix($value);
156 |
157 | if (is_string($value) && \str_starts_with($value, '%')) {
158 | return new String_($value);
159 | }
160 |
161 | return $this->commonNodeFactory->createAbsoluteDirExpr($value);
162 | }
163 |
164 | private function processNormalizedValue(mixed $value): ?Expr
165 | {
166 | if (is_bool($value)) {
167 | return BuilderHelpers::normalizeValue($value);
168 | }
169 |
170 | if (in_array($value, ['annotations', 'directory', 'glob'], true)) {
171 | return BuilderHelpers::normalizeValue($value);
172 | }
173 |
174 | return null;
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/CaseConverter/NameOnlyServiceCaseConverter.php:
--------------------------------------------------------------------------------
1 | commonNodeFactory->createClassReference($key);
27 | $setMethodCall = new MethodCall(new Variable(VariableName::SERVICES), 'set', [new Arg($classConstFetch)]);
28 |
29 | return new Expression($setMethodCall);
30 | }
31 |
32 | public function match(string $rootKey, mixed $key, mixed $values): bool
33 | {
34 | if ($rootKey !== YamlKey::SERVICES) {
35 | return false;
36 | }
37 |
38 | return $values === null || $values === [];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/CaseConverter/NestedCaseConverter/InstanceOfNestedCaseConverter.php:
--------------------------------------------------------------------------------
1 | commonNodeFactory->createClassReference($key);
32 |
33 | $servicesVariable = new Variable(VariableName::SERVICES);
34 | $args = [new Arg($classConstFetch)];
35 |
36 | $instanceofMethodCall = new MethodCall($servicesVariable, MethodName::INSTANCEOF, $args);
37 |
38 | $decoreatedInstanceofMethodCall = $this->serviceOptionNodeFactory->convertServiceOptionsToNodes(
39 | $values,
40 | $instanceofMethodCall
41 | );
42 |
43 | return new Expression($decoreatedInstanceofMethodCall);
44 | }
45 |
46 | public function isMatch(string $rootKey, int|string $subKey): bool
47 | {
48 | if ($rootKey !== YamlKey::SERVICES) {
49 | return false;
50 | }
51 |
52 | if (! is_string($subKey)) {
53 | return false;
54 | }
55 |
56 | return $subKey === YamlKey::_INSTANCEOF;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/CaseConverter/ParameterCaseConverter.php:
--------------------------------------------------------------------------------
1 | prefixWithDirConstantIfExistingPath($values);
43 | }
44 |
45 | if (is_array($values)) {
46 | foreach ($values as $subKey => $subValue) {
47 | if (! is_string($subValue)) {
48 | continue;
49 | }
50 |
51 | $values[$subKey] = $this->prefixWithDirConstantIfExistingPath($subValue);
52 | }
53 | }
54 |
55 | $args = $this->argsNodeFactory->createFromValues([$key, $values]);
56 |
57 | $parametersVariable = new Variable(VariableName::PARAMETERS);
58 | $methodCall = new MethodCall($parametersVariable, MethodName::SET, $args);
59 |
60 | return new Expression($methodCall);
61 | }
62 |
63 | private function prefixWithDirConstantIfExistingPath(string $value): string | Expr
64 | {
65 | $filePath = $this->currentFilePathProvider->getFilePath();
66 | if ($filePath === null) {
67 | return $value;
68 | }
69 |
70 | $configDirectory = dirname($filePath);
71 |
72 | $possibleConfigPath = $configDirectory . '/' . $value;
73 | if (is_file($possibleConfigPath)) {
74 | return $this->commonNodeFactory->createAbsoluteDirExpr($value);
75 | }
76 |
77 | if (is_dir($possibleConfigPath)) {
78 | return $this->commonNodeFactory->createAbsoluteDirExpr($value);
79 | }
80 |
81 | return $value;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/CaseConverter/ResourceCaseConverter.php:
--------------------------------------------------------------------------------
1 | servicesPhpNodeFactory->createResource($key, $values);
28 | }
29 |
30 | public function match(string $rootKey, mixed $key, mixed $values): bool
31 | {
32 | return isset($values[YamlKey::RESOURCE]);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/CaseConverter/ServicesDefaultsCaseConverter.php:
--------------------------------------------------------------------------------
1 | createServicesVariable(), MethodName::DEFAULTS);
27 |
28 | $decoratedMethodCall = $this->autoBindNodeFactory->createAutoBindCalls($values, $methodCall);
29 |
30 | return new Expression($decoratedMethodCall);
31 | }
32 |
33 | public function match(string $rootKey, mixed $key, mixed $values): bool
34 | {
35 | if ($rootKey !== YamlKey::SERVICES) {
36 | return false;
37 | }
38 |
39 | return $key === YamlKey::_DEFAULTS;
40 | }
41 |
42 | private function createServicesVariable(): Variable
43 | {
44 | return new Variable(VariableName::SERVICES);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Contract/CaseConverterInterface.php:
--------------------------------------------------------------------------------
1 | stringExprResolver->resolve($value, $skipServiceReference, false);
30 |
31 | if ($skipServiceReference) {
32 | return $expr;
33 | }
34 |
35 | $args = [new Arg($expr)];
36 | return new FuncCall(new FullyQualified($functionName), $args);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/ExprResolver/StringExprResolver.php:
--------------------------------------------------------------------------------
1 | constantNodeFactory->createConstant($const);
47 | }
48 |
49 | $classConstFetch = $this->constantNodeFactory->createClassConstantIfValue($value);
50 | if ($classConstFetch instanceof ClassConstFetch) {
51 | return $classConstFetch;
52 | }
53 |
54 | // do not print "\n" as empty space, but use string value instead
55 | if (in_array($value, ["\r", "\n", "\r\n"], true)) {
56 | return $this->keepNewline($value);
57 | }
58 |
59 | $value = ltrim($value, '\\');
60 | if ($this->isClassType($value)) {
61 | return $this->resolveClassType($skipClassesToConstantReference, $value);
62 | }
63 |
64 | if (str_starts_with($value, '@=')) {
65 | return $this->resolveExpr($value, $skipServiceReference, $skipClassesToConstantReference);
66 | }
67 |
68 | // is service reference
69 | if (str_starts_with($value, '@') && ! $this->isFilePath($value)) {
70 | return $this->resolveServiceReferenceExpr(
71 | $value,
72 | $skipServiceReference,
73 | ReferenceFunctionNameResolver::resolve(),
74 | $isRoutingImport
75 | );
76 | }
77 |
78 | return BuilderHelpers::normalizeValue($value);
79 | }
80 |
81 | private function resolveExpr(
82 | string $value,
83 | bool $skipServiceReference,
84 | bool $skipClassesToConstantReference
85 | ): FuncCall {
86 | $value = ltrim($value, '@=');
87 | $expr = $this->resolve($value, $skipServiceReference, $skipClassesToConstantReference);
88 | $args = [new Arg($expr)];
89 |
90 | return new FuncCall(new FullyQualified(FunctionName::EXPR), $args);
91 | }
92 |
93 | private function keepNewline(string $value): String_
94 | {
95 | $string = new String_($value);
96 | $string->setAttribute('kind', String_::KIND_DOUBLE_QUOTED);
97 |
98 | return $string;
99 | }
100 |
101 | private function isFilePath(string $value): bool
102 | {
103 | return (bool) Strings::match($value, self::TWIG_HTML_XML_SUFFIX_REGEX);
104 | }
105 |
106 | private function resolveClassType(bool $skipClassesToConstantReference, string $value): String_ | ClassConstFetch
107 | {
108 | if ($skipClassesToConstantReference) {
109 | return new String_($value);
110 | }
111 |
112 | return $this->commonNodeFactory->createClassReference($value);
113 | }
114 |
115 | private function isClassType(string $value): bool
116 | {
117 | if (! ctype_upper($value[0])) {
118 | return false;
119 | }
120 |
121 | // to avoid autoload in case of missing code sniffer dependency
122 | if (str_ends_with($value, 'Sniff')) {
123 | return true;
124 | }
125 |
126 | if (str_ends_with($value, 'Fixer')) {
127 | return true;
128 | }
129 |
130 | if (class_exists($value)) {
131 | return true;
132 | }
133 |
134 | return interface_exists($value);
135 | }
136 |
137 | /**
138 | * @param FunctionName::* $functionName
139 | */
140 | private function resolveServiceReferenceExpr(
141 | string $value,
142 | bool $skipServiceReference,
143 | string $functionName,
144 | bool $isRoutingImport = false
145 | ): Expr {
146 | $value = ltrim($value, '@');
147 | $expr = $this->resolve($value, $skipServiceReference, false);
148 |
149 | if ($skipServiceReference) {
150 | // return the `@` back
151 | if ($isRoutingImport && $expr instanceof String_) {
152 | $expr->value = '@' . $expr->value;
153 | }
154 |
155 | return $expr;
156 | }
157 |
158 | $args = [new Arg($expr)];
159 | return new FuncCall(new FullyQualified($functionName), $args);
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/ExprResolver/TaggedReturnsCloneResolver.php:
--------------------------------------------------------------------------------
1 | getValue()[0];
22 |
23 | $expr = $this->serviceReferenceExprResolver->resolveServiceReferenceExpr(
24 | $serviceName,
25 | false,
26 | ReferenceFunctionNameResolver::resolve()
27 | );
28 |
29 | return new Array_([new ArrayItem($expr)]);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/ExprResolver/TaggedServiceResolver.php:
--------------------------------------------------------------------------------
1 | getValue()['class'];
21 | return $this->serviceReferenceExprResolver->resolveServiceReferenceExpr(
22 | $serviceName,
23 | false,
24 | FunctionName::INLINE_SERVICE
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Naming/ClassNaming.php:
--------------------------------------------------------------------------------
1 | normalizeUpperCase($shortClassName);
19 | return lcfirst($normalizedShortClassName);
20 | }
21 |
22 | private function normalizeUpperCase(string $shortClassName): string
23 | {
24 | // turns $SOMEUppercase => $someUppercase
25 | for ($i = 0; $i <= strlen($shortClassName); ++$i) {
26 | if (ctype_upper($shortClassName[$i]) && $this->isNumberOrUpper($shortClassName[$i + 1])) {
27 | $shortClassName[$i] = strtolower($shortClassName[$i]);
28 | } else {
29 | break;
30 | }
31 | }
32 |
33 | return $shortClassName;
34 | }
35 |
36 | private function isNumberOrUpper(string $char): bool
37 | {
38 | if (ctype_upper($char)) {
39 | return true;
40 | }
41 |
42 | return ctype_digit($char);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/NodeFactory/ArgsNodeFactory.php:
--------------------------------------------------------------------------------
1 | isPhpNamedArguments = PHP_VERSION_ID >= 80000;
45 | }
46 |
47 | /**
48 | * @return Arg[]
49 | */
50 | public function createFromValuesAndWrapInArray(mixed $values): array
51 | {
52 | if (is_array($values)) {
53 | $array = $this->resolveExprFromArray($values);
54 | } else {
55 | $expr = $this->resolveExpr($values);
56 | $items = [new ArrayItem($expr)];
57 | $array = new Array_($items);
58 | }
59 |
60 | return [new Arg($array)];
61 | }
62 |
63 | /**
64 | * @return mixed[]|Arg[]
65 | */
66 | public function createFromValues(
67 | mixed $values,
68 | bool $skipServiceReference = false,
69 | bool $skipClassesToConstantReference = false,
70 | bool $isForConfig = false,
71 | bool $isRoutingImport = false
72 | ): array {
73 | if (is_array($values)) {
74 | $args = [];
75 | foreach ($values as $key => $value) {
76 | $expr = $this->resolveExpr(
77 | $value,
78 | $skipServiceReference,
79 | $skipClassesToConstantReference,
80 | $isRoutingImport
81 | );
82 | $args = $this->resolveArgs($args, $key, $expr, $isForConfig);
83 | }
84 |
85 | return $args;
86 | }
87 |
88 | if ($values instanceof Node) {
89 | if ($values instanceof Arg) {
90 | return [$values];
91 | }
92 |
93 | if ($values instanceof Expr) {
94 | return [new Arg($values)];
95 | }
96 | }
97 |
98 | if (is_string($values)) {
99 | $expr = $this->resolveExpr($values);
100 | return [new Arg($expr)];
101 | }
102 |
103 | throw new NotImplementedYetException();
104 | }
105 |
106 | public function resolveExpr(
107 | mixed $value,
108 | bool $skipServiceReference = false,
109 | bool $skipClassesToConstantReference = false,
110 | bool $isRoutingImport = false
111 | ): Expr {
112 | if (is_string($value)) {
113 | return $this->stringExprResolver->resolve(
114 | $value,
115 | $skipServiceReference,
116 | $skipClassesToConstantReference,
117 | $isRoutingImport
118 | );
119 | }
120 |
121 | if ($value instanceof Expr) {
122 | return $value;
123 | }
124 |
125 | if ($value instanceof TaggedValue) {
126 | return $this->createServiceReferenceFromTaggedValue($value);
127 | }
128 |
129 | if (is_array($value)) {
130 | $arrayItems = $this->resolveArrayItems($value, $skipClassesToConstantReference);
131 | return new Array_($arrayItems);
132 | }
133 |
134 | if (is_object($value)) {
135 | return $this->newValueObjectFactory->create($value);
136 | }
137 |
138 | return BuilderHelpers::normalizeValue($value);
139 | }
140 |
141 | /**
142 | * @param mixed[] $values
143 | */
144 | public function resolveExprFromArray(array $values): Array_
145 | {
146 | $arrayItems = [];
147 |
148 | $hasNaturalKeysOrder = $this->hasNaturalKeyOrder($values);
149 |
150 | foreach ($values as $key => $value) {
151 | $expr = is_array($value) ? $this->resolveExprFromArray($value) : $this->resolveExpr($value);
152 |
153 | if (! is_int($key) || ! $hasNaturalKeysOrder) {
154 | $keyExpr = $this->resolveExpr($key);
155 | $arrayItem = new ArrayItem($expr, $keyExpr);
156 | } else {
157 | $arrayItem = new ArrayItem($expr);
158 | }
159 |
160 | $arrayItems[] = $arrayItem;
161 | }
162 |
163 | return new Array_($arrayItems);
164 | }
165 |
166 | /**
167 | * @param Arg[] $args
168 | * @return Arg[]
169 | */
170 | private function resolveArgs(array $args, mixed $key, Expr $expr, bool $isForConfig): array
171 | {
172 | if (is_string($key) && $isForConfig) {
173 | $key = $this->resolveExpr($key);
174 | $args[] = new Arg(new Array_([new ArrayItem($expr, $key)]));
175 |
176 | return $args;
177 | }
178 |
179 | if (! is_int($key) && $this->isPhpNamedArguments) {
180 | $args[] = new Arg($expr, false, false, [], new Identifier($key));
181 |
182 | return $args;
183 | }
184 |
185 | $args[] = new Arg($expr);
186 | return $args;
187 | }
188 |
189 | private function createServiceReferenceFromTaggedValue(TaggedValue $taggedValue): Expr
190 | {
191 | // that's the only value
192 | if ($taggedValue->getTag() === self::TAG_RETURNS_CLONE) {
193 | return $this->taggedReturnsCloneResolver->resolve($taggedValue);
194 | }
195 |
196 | if ($taggedValue->getTag() === self::TAG_SERVICE) {
197 | return $this->taggedServiceResolver->resolve($taggedValue);
198 | }
199 |
200 | $name = match ($taggedValue->getTag()) {
201 | 'tagged_iterator' => new FullyQualified(FunctionName::TAGGED_ITERATOR),
202 | 'tagged_locator' => new FullyQualified(FunctionName::TAGGED_LOCATOR),
203 | default => new Name($taggedValue->getTag())
204 | };
205 |
206 | $args = $this->createFromValues($taggedValue->getValue());
207 |
208 | return new FuncCall($name, $args);
209 | }
210 |
211 | /**
212 | * @param mixed[] $value
213 | * @return ArrayItem[]
214 | */
215 | private function resolveArrayItems(array $value, bool $skipClassesToConstantReference): array
216 | {
217 | $arrayItems = [];
218 |
219 | $naturalKey = 0;
220 | foreach ($value as $nestedKey => $nestedValue) {
221 | $valueExpr = $this->resolveExpr($nestedValue, false, $skipClassesToConstantReference);
222 |
223 | if (! is_int($nestedKey) || $nestedKey !== $naturalKey) {
224 | $keyExpr = $this->resolveExpr($nestedKey, false, $skipClassesToConstantReference);
225 | $arrayItem = new ArrayItem($valueExpr, $keyExpr);
226 | } else {
227 | $arrayItem = new ArrayItem($valueExpr);
228 | }
229 |
230 | $arrayItems[] = $arrayItem;
231 |
232 | ++$naturalKey;
233 | }
234 |
235 | return $arrayItems;
236 | }
237 |
238 | /**
239 | * Detects, if the array keys are implicit - natural order integers: 0, 1, 2, 3...
240 | *
241 | * @param array $values
242 | */
243 | private function hasNaturalKeyOrder(array $values): bool
244 | {
245 | $valueCount = count($values);
246 | $naturalOrderKeys = range(0, $valueCount - 1);
247 |
248 | return $naturalOrderKeys === array_keys($values);
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/src/NodeFactory/CommonNodeFactory.php:
--------------------------------------------------------------------------------
1 | createConstFetch($className, 'class');
41 | }
42 |
43 | public function createConstFetch(string $className, string $constantName): ClassConstFetch
44 | {
45 | return new ClassConstFetch(new FullyQualified($className), $constantName);
46 | }
47 |
48 | public function createFalse(): ConstFetch
49 | {
50 | return new ConstFetch(new Name('false'));
51 | }
52 |
53 | public function createTrue(): ConstFetch
54 | {
55 | return new ConstFetch(new Name('true'));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/NodeFactory/ConstantNodeFactory.php:
--------------------------------------------------------------------------------
1 | createClassConstantIfValue($value, false);
52 | if ($classConstFetch instanceof ClassConstFetch) {
53 | return $classConstFetch;
54 | }
55 |
56 | return new ConstFetch(new Name($value));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/NodeFactory/ContainerConfiguratorReturnClosureFactory.php:
--------------------------------------------------------------------------------
1 | $arrayData
42 | * @param string $containerConfiguratorClass Must remain string to avoid prefixing
43 | */
44 | public function createFromYamlArray(
45 | array $arrayData,
46 | string $containerConfiguratorClass = 'Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator'
47 | ): Return_ {
48 | $stmts = $this->createClosureStmts($arrayData);
49 |
50 | $closure = $this->configuratorClosureNodeFactory->createContainerClosureFromStmts(
51 | $stmts,
52 | $containerConfiguratorClass
53 | );
54 |
55 | return new Return_($closure);
56 | }
57 |
58 | /**
59 | * @param mixed[] $yamlData
60 | * @return Stmt[]
61 | */
62 | private function createClosureStmts(array $yamlData): array
63 | {
64 | $yamlData = array_filter($yamlData);
65 | return $this->createStmtsFromCaseConverters($yamlData);
66 | }
67 |
68 | /**
69 | * @param array $yamlData
70 | * @return Stmt[]
71 | */
72 | private function createStmtsFromCaseConverters(array $yamlData): array
73 | {
74 | $stmts = [];
75 |
76 | foreach ($yamlData as $key => $values) {
77 | // keys can be int, but we need string
78 | if (! is_string($key)) {
79 | $key = (string) $key;
80 | }
81 |
82 | $stmts = $this->createInitializeStmt($key, $stmts);
83 |
84 | foreach ($values as $nestedKey => $nestedValues) {
85 | $nestedNodes = $this->processNestedNodes($key, $nestedKey, $nestedValues);
86 |
87 | if ($nestedNodes !== []) {
88 | $stmts = array_merge($stmts, $nestedNodes);
89 | continue;
90 | }
91 |
92 | $expression = $this->resolveStmt($key, $nestedKey, $nestedValues);
93 | if (! $expression instanceof Expression) {
94 | continue;
95 | }
96 |
97 | $lastNode = end($stmts);
98 | $node = $this->resolveExpressionWhenAtEnv($expression, $key, $lastNode);
99 | if ($node !== null) {
100 | $stmts[] = $node;
101 | }
102 | }
103 | }
104 |
105 | return $stmts;
106 | }
107 |
108 | /**
109 | * @return Expression[]|mixed[]
110 | */
111 | private function processNestedNodes(string $key, int|string $nestedKey, mixed $nestedValues): array
112 | {
113 | if (is_array($nestedValues)) {
114 | return $this->containerNestedNodesFactory->createFromValues($nestedValues, $key, $nestedKey);
115 | }
116 |
117 | return [];
118 | }
119 |
120 | private function resolveExpressionWhenAtEnv(
121 | Expression $expression,
122 | string $key,
123 | Expression|If_|bool $lastNode
124 | ): Expression|If_|null {
125 | $explodeAt = explode('@', $key);
126 | if (str_starts_with($key, 'when@') && count($explodeAt) === 2) {
127 | $variable = new Variable(VariableName::CONTAINER_CONFIGURATOR);
128 |
129 | $expr = $expression->expr;
130 | if (! $expr instanceof MethodCall) {
131 | throw new ShouldNotHappenException();
132 | }
133 |
134 | $args = $expr->getArgs();
135 |
136 | if (! isset($args[1]) || ! $args[1]->value instanceof Array_ || ! isset($args[1]->value->items[0])
137 | || ! $args[1]->value->items[0] instanceof ArrayItem || ! $args[1]->value->items[0]->key instanceof Expr) {
138 | throw new ShouldNotHappenException();
139 | }
140 |
141 | $newExpression = new Expression(
142 | new MethodCall(
143 | $variable,
144 | 'extension',
145 | [new Arg($args[1]->value->items[0]->key), new Arg($args[1]->value->items[0]->value)]
146 | )
147 | );
148 |
149 | $environmentString = new String_($explodeAt[1]);
150 | $envMethodCall = new MethodCall($variable, 'env');
151 |
152 | $identical = new Identical($envMethodCall, $environmentString);
153 |
154 | if ($lastNode instanceof If_ && $this->isSameCond($lastNode->cond, $identical)) {
155 | $lastNode->stmts[] = $newExpression;
156 | return null;
157 | }
158 |
159 | $if = new If_($identical);
160 | $if->stmts = [$newExpression];
161 |
162 | return $if;
163 | }
164 |
165 | return $expression;
166 | }
167 |
168 | private function isSameCond(Expr $expr, Identical $identical): bool
169 | {
170 | if ($expr instanceof Identical) {
171 | $val1 = Json::encode($expr);
172 | $val2 = Json::encode($identical);
173 | return $val1 === $val2;
174 | }
175 |
176 | return false;
177 | }
178 |
179 | /**
180 | * @param VariableMethodName::* $variableMethodName
181 | */
182 | private function createInitializeAssign(string $variableMethodName): Expression
183 | {
184 | $servicesVariable = new Variable($variableMethodName);
185 | $containerConfiguratorVariable = new Variable(VariableName::CONTAINER_CONFIGURATOR);
186 |
187 | $assign = new Assign($servicesVariable, new MethodCall($containerConfiguratorVariable, $variableMethodName));
188 |
189 | return new Expression($assign);
190 | }
191 |
192 | /**
193 | * @param Stmt[] $stmts
194 | * @return Stmt[]
195 | */
196 | private function createInitializeStmt(string $key, array $stmts): array
197 | {
198 | if ($key === YamlKey::SERVICES) {
199 | $stmts[] = $this->createInitializeAssign(VariableMethodName::SERVICES);
200 | return $stmts;
201 | }
202 |
203 | if ($key === YamlKey::PARAMETERS) {
204 | $stmts[] = $this->createInitializeAssign(VariableMethodName::PARAMETERS);
205 | return $stmts;
206 | }
207 |
208 | return $stmts;
209 | }
210 |
211 | private function resolveStmt(string $key, int | string $nestedKey, mixed $nestedValues): ?Stmt
212 | {
213 | foreach ($this->caseConverters as $caseConverter) {
214 | if (! $caseConverter->match($key, $nestedKey, $nestedValues)) {
215 | continue;
216 | }
217 |
218 | /** @var string $nestedKey */
219 | return $caseConverter->convertToMethodCallStmt($nestedKey, $nestedValues);
220 | }
221 |
222 | return null;
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/src/NodeFactory/ContainerNestedNodesFactory.php:
--------------------------------------------------------------------------------
1 | $subNestedValue) {
27 | if (! $this->instanceOfNestedCaseConverter->isMatch($key, $nestedKey)) {
28 | continue;
29 | }
30 |
31 | $nestedStmts[] = $this->instanceOfNestedCaseConverter->convertToMethodCall(
32 | $subNestedKey,
33 | $subNestedValue
34 | );
35 | }
36 |
37 | return $nestedStmts;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/NodeFactory/NewValueObjectFactory.php:
--------------------------------------------------------------------------------
1 | getKey());
24 | }
25 |
26 | // assumption that constructor parameters share the same value as property names
27 | $propertyValues = $this->resolvePropertyValuesFromValueObject($valueObjectClass, $valueObject);
28 | $args = $this->createArgs($propertyValues);
29 |
30 | return new New_(new FullyQualified($valueObjectClass), $args);
31 | }
32 |
33 | /**
34 | * @param class-string $valueObjectClass
35 | * @return mixed[]
36 | */
37 | private function resolvePropertyValuesFromValueObject(string $valueObjectClass, object $valueObject): array
38 | {
39 | $reflectionClass = new ReflectionClass($valueObjectClass);
40 | $propertyValues = [];
41 |
42 | foreach ($reflectionClass->getProperties() as $reflectionProperty) {
43 | $reflectionProperty->setAccessible(true);
44 |
45 | $defaultPropertyValue = $reflectionProperty->getDefaultValue();
46 | $currentPropertyValue = $reflectionProperty->getValue($valueObject);
47 |
48 | // do not fill in default values
49 | if ($defaultPropertyValue === $currentPropertyValue) {
50 | continue;
51 | }
52 |
53 | $propertyValues[] = $currentPropertyValue;
54 | }
55 |
56 | return $propertyValues;
57 | }
58 |
59 | /**
60 | * @param mixed[] $propertyValues
61 | * @return Arg[]
62 | */
63 | private function createArgs(array $propertyValues): array
64 | {
65 | $args = [];
66 | foreach ($propertyValues as $propertyValue) {
67 | if (is_object($propertyValue)) {
68 | $nestedValueObject = $this->create($propertyValue);
69 | $args[] = new Arg($nestedValueObject);
70 | } elseif (is_array($propertyValue)) {
71 | $args[] = new Arg(new Array_($this->createArgs($propertyValue)));
72 | } else {
73 | $args[] = new Arg(BuilderHelpers::normalizeValue($propertyValue));
74 | }
75 | }
76 |
77 | return $args;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/NodeFactory/RoutingConfiguratorReturnClosureFactory.php:
--------------------------------------------------------------------------------
1 | createClosureStmts($arrayData);
29 | $closure = $this->containerConfiguratorClosureNodeFactory->createRoutingClosureFromStmts($stmts);
30 |
31 | return new Return_($closure);
32 | }
33 |
34 | /**
35 | * @param mixed[] $arrayData
36 | * @return Stmt[]
37 | */
38 | public function createClosureStmts(array $arrayData): array
39 | {
40 | $arrayData = $this->removeEmptyValues($arrayData);
41 | return $this->createStmtsFromCaseConverters($arrayData);
42 | }
43 |
44 | /**
45 | * @param mixed[] $yamlData
46 | * @return mixed[]
47 | */
48 | private function removeEmptyValues(array $yamlData): array
49 | {
50 | return array_filter($yamlData);
51 | }
52 |
53 | /**
54 | * @param mixed[] $arrayData
55 | * @return Stmt[]
56 | */
57 | private function createStmtsFromCaseConverters(array $arrayData): array
58 | {
59 | $stmts = [];
60 |
61 | foreach ($arrayData as $key => $values) {
62 | $stmt = null;
63 |
64 | foreach ($this->routingCaseConverters as $routingCaseConverter) {
65 | if (! $routingCaseConverter->match($key, $values)) {
66 | continue;
67 | }
68 |
69 | $stmt = $routingCaseConverter->convertToMethodCall($key, $values);
70 | break;
71 | }
72 |
73 | if (! $stmt instanceof Stmt) {
74 | continue;
75 | }
76 |
77 | $stmts[] = $stmt;
78 | }
79 |
80 | return $stmts;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/NodeFactory/Service/AutoBindNodeFactory.php:
--------------------------------------------------------------------------------
1 | autowire()
23 | * ->autoconfigure()
24 | * ->bind()
25 | *
26 | * @param mixed[] $yaml
27 | */
28 | public function createAutoBindCalls(array $yaml, MethodCall $methodCall): MethodCall
29 | {
30 | foreach ($yaml as $key => $value) {
31 | if ($key === YamlKey::AUTOWIRE) {
32 | $methodCall = $this->createAutowire($value, $methodCall);
33 | }
34 |
35 | if ($key === YamlKey::AUTOCONFIGURE) {
36 | $methodCall = $this->createAutoconfigure($value, $methodCall);
37 | }
38 |
39 | if ($key === YamlKey::PUBLIC) {
40 | $methodCall = $this->createPublicPrivate($value, $methodCall);
41 | }
42 |
43 | if ($key === YamlKey::BIND) {
44 | // skip empty definitions
45 | if (empty($yaml[YamlKey::BIND])) {
46 | continue;
47 | }
48 |
49 | $methodCall = $this->createBindMethodCall($methodCall, $yaml[YamlKey::BIND]);
50 | }
51 |
52 | if ($key === YamlKey::TAGS) {
53 | $methodCall = $this->tagsServiceOptionKeyYamlToPhpFactory->decorateServiceMethodCall(
54 | null,
55 | $value,
56 | [],
57 | $methodCall
58 | );
59 | }
60 | }
61 |
62 | return $methodCall;
63 | }
64 |
65 | /**
66 | * @param mixed[] $bindValues
67 | */
68 | private function createBindMethodCall(MethodCall $methodCall, array $bindValues): MethodCall
69 | {
70 | foreach ($bindValues as $key => $value) {
71 | $args = $this->argsNodeFactory->createFromValues([$key, $value]);
72 | $methodCall = new MethodCall($methodCall, YamlKey::BIND, $args);
73 | }
74 |
75 | return $methodCall;
76 | }
77 |
78 | private function createAutowire(mixed $value, MethodCall $methodCall): MethodCall
79 | {
80 | if ($value === true) {
81 | return new MethodCall($methodCall, YamlKey::AUTOWIRE);
82 | }
83 |
84 | return $methodCall;
85 | }
86 |
87 | private function createAutoconfigure(mixed $value, MethodCall $methodCall): MethodCall
88 | {
89 | if ($value === true) {
90 | return new MethodCall($methodCall, YamlKey::AUTOCONFIGURE);
91 | }
92 |
93 | return $methodCall;
94 | }
95 |
96 | private function createPublicPrivate(mixed $value, MethodCall $methodCall): MethodCall
97 | {
98 | if ($value !== false) {
99 | return new MethodCall($methodCall, 'public');
100 | }
101 |
102 | // default value
103 | return $methodCall;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/NodeFactory/Service/ServiceOptionNodeFactory.php:
--------------------------------------------------------------------------------
1 | unNestArguments($servicesValues);
32 |
33 | foreach ($servicesValues as $key => $value) {
34 | if ($this->shouldSkip($key)) {
35 | continue;
36 | }
37 |
38 | foreach ($this->serviceOptionKeyYamlToPhpFactories as $serviceOptionKeyYamlToPhpFactory) {
39 | if (! $serviceOptionKeyYamlToPhpFactory->isMatch($key, $value)) {
40 | continue;
41 | }
42 |
43 | $methodCall = $serviceOptionKeyYamlToPhpFactory->decorateServiceMethodCall(
44 | $key,
45 | $value,
46 | $servicesValues,
47 | $methodCall
48 | );
49 |
50 | continue 2;
51 | }
52 | }
53 |
54 | return $methodCall;
55 | }
56 |
57 | /**
58 | * @param array $servicesValues
59 | * @return array|array>
60 | */
61 | private function unNestArguments(array $servicesValues): array
62 | {
63 | if (! $this->serviceOptionAnalyzer->hasNamedArguments($servicesValues)) {
64 | return $servicesValues;
65 | }
66 |
67 | return [
68 | YamlServiceKey::ARGUMENTS => $servicesValues,
69 | ];
70 | }
71 |
72 | private function shouldSkip(string|int $key): bool
73 | {
74 | if (\is_int($key)) {
75 | return false;
76 | }
77 |
78 | // options started by decoration_