├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist └── src ├── DefinitionHelper ├── AliasDefinitionHelper.php ├── CreateDefinitionHelper.php ├── DefinitionHelper.php ├── ExtensionConfiguration.php ├── FactoryDefinitionHelper.php └── ParameterDefinitionHelper.php ├── EnableFluentConfig.php ├── Import.php ├── PhpConfigFileLoader.php ├── PhpConfigLoader.php ├── Reference.php └── functions.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # .gitattributes 2 | tests/ export-ignore 3 | 4 | # Auto detect text files and perform LF normalization 5 | * text=auto 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /composer.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | 7 | matrix: 8 | include: 9 | - php: 7.0 10 | env: dependencies=lowest 11 | 12 | cache: 13 | directories: 14 | - $HOME/.composer/cache 15 | 16 | before_script: 17 | - if [[ "$dependencies" != "lowest" ]]; then composer install -n ; fi 18 | - if [ "$dependencies" = "lowest" ]; then composer update --prefer-lowest --prefer-stable -n; fi; 19 | 20 | script: 21 | - vendor/bin/phpunit 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Matthieu Napoli 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fluent configuration for Symfony 2 | 3 | [![Build Status](https://img.shields.io/travis/mnapoli/fluent-symfony/master.svg?style=flat-square)](https://travis-ci.org/mnapoli/fluent-symfony) 4 | 5 | This package offers an alternative configuration syntax for Symfony's container, inspired by [PHP-DI's configuration](http://php-di.org/doc/php-definitions.html). 6 | 7 | - [Why?](#why) 8 | - [Comparison with existing formats](#comparison-with-existing-formats) 9 | - [Installation](#installation) 10 | - [Syntax](#syntax) 11 | - [Parameters](#parameters) 12 | - [Services](#services) 13 | - [Using the class name as the entry ID](#using-the-class-name-as-the-entry-id) 14 | - [Autowiring](#autowiring) 15 | - [Constructor arguments](#constructor-arguments) 16 | - [Dependencies](#dependencies) 17 | - [Setter injection](#setter-injection) 18 | - [Property injection](#property-injection) 19 | - [Optional service references](#optional-service-references) 20 | - [Decorated services](#decorated-services) 21 | - [Non shared services](#non-shared-services) 22 | - [Factories](#factories) 23 | - [Aliases](#aliases) 24 | - [Tags](#tags) 25 | - [Imports](#imports) 26 | - [Extensions](#extensions) 27 | 28 | ## Why? 29 | 30 | The main goal is to benefit from stricter analysis from the PHP engine and IDEs. If you are interested you can also read [why YAML was replaced by a similar syntax in PHP-DI 5](http://php-di.org/news/06-php-di-4-0-new-definitions.html). 31 | 32 | - auto-completion on classes or constants: 33 | 34 | ![](https://i.imgur.com/t65dZ9l.png) 35 | 36 | - auto-completion when writing configuration: 37 | 38 | ![](http://i.imgur.com/0w0or7S.gif) 39 | 40 | - real time validation in IDEs: 41 | 42 | ![](http://i.imgur.com/28wO3Oa.png) 43 | 44 | - constant support: 45 | 46 | ![](https://i.imgur.com/LsRXbJx.png) 47 | 48 | - better refactoring support 49 | 50 | ## Comparison with existing formats 51 | 52 | Currently, in Symfony, you can configure the container using: 53 | 54 | - YAML 55 | 56 | ```yaml 57 | parameters: 58 | mailer.transport: sendmail 59 | 60 | services: 61 | mailer: 62 | class: Mailer 63 | arguments: ['%mailer.transport%'] 64 | ``` 65 | 66 | - XML 67 | 68 | ```xml 69 | 70 | 73 | 74 | 75 | sendmail 76 | 77 | 78 | 79 | 80 | %mailer.transport% 81 | 82 | 83 | 84 | ``` 85 | 86 | - PHP code 87 | 88 | ```php 89 | $container->setParameter('mailer.transport', 'sendmail'); 90 | $container 91 | ->register('mailer', 'Mailer') 92 | ->addArgument('%mailer.transport%'); 93 | ``` 94 | 95 | With this package, you can now use a 4th alternative: 96 | 97 | ```php 98 | return [ 99 | 'mailer.transport' => 'sendmail', 100 | 101 | 'mailer' => create(Mailer::class) 102 | ->arguments('%mailer.transport%'), 103 | ]; 104 | ``` 105 | 106 | ## Installation 107 | 108 | ``` 109 | composer require mnapoli/fluent-symfony 110 | ``` 111 | 112 | To enable the new format in a Symfony fullstack application, simply import the `EnableFluentConfig` trait in `app/AppKernel.php`, for example: 113 | 114 | ```php 115 | load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.php'); 143 | } 144 | } 145 | ``` 146 | 147 | - or import PHP config files from YAML config files: 148 | 149 | ```yaml 150 | imports: 151 | - services.php 152 | 153 | # ... 154 | ``` 155 | 156 | Be advised that PHP config files in the "traditional" form ([see the documentation](http://symfony.com/doc/current/components/dependency_injection.html#setting-up-the-container-with-configuration-files)) *are still supported* and will continue to work. 157 | 158 | ## Syntax 159 | 160 | A configuration file must `return` a PHP array. In that array, parameters, services and imports are defined altogether: 161 | 162 | ```php 163 | 'bar', 178 | ]; 179 | ``` 180 | 181 | This is the same as: 182 | 183 | ```yaml 184 | parameters: 185 | foo: 'bar' 186 | ``` 187 | 188 | Parameters and services can be mixed in the same array. 189 | 190 | ## Services 191 | 192 | Services can be declared simply using the `create()` function helper: 193 | 194 | ```php 195 | use function Fluent\create; 196 | 197 | return [ 198 | 'mailer' => create(Mailer::class), 199 | ]; 200 | ``` 201 | 202 | When calling `$container->get('mailer')` an instance of the `Mailer` class will be created and returned. 203 | 204 | This is the same as: 205 | 206 | ```yaml 207 | services: 208 | mailer: 209 | class: Mailer 210 | ``` 211 | 212 | #### Using the class name as the entry ID 213 | 214 | If the container entry ID is a class name, you can skip it when calling `create()`. 215 | 216 | ```php 217 | return [ 218 | Mailer::class => create(), 219 | ]; 220 | ``` 221 | 222 | #### Autowiring 223 | 224 | Services can also be [automatically wired](http://symfony.com/doc/current/components/dependency_injection/autowiring.html) using the `autowire()` function helper in place of `create()`: 225 | 226 | ```php 227 | use function Fluent\autowire; 228 | 229 | return [ 230 | Mailer::class => autowire(), 231 | ]; 232 | ``` 233 | 234 | This is the same as: 235 | 236 | ```yaml 237 | services: 238 | Mailer: 239 | class: Mailer 240 | autowire: true 241 | ``` 242 | 243 | #### Constructor arguments 244 | 245 | ```php 246 | return [ 247 | 'mailer' => create(Mailer::class) 248 | ->arguments('smtp.google.com', 2525), 249 | ]; 250 | ``` 251 | 252 | This is the same as: 253 | 254 | ```yaml 255 | services: 256 | mailer: 257 | class: Mailer 258 | arguments: ['smtp.google.com', 2525] 259 | ``` 260 | 261 | #### Dependencies 262 | 263 | Parameters can be injected using the `'%foo%'` syntax: 264 | 265 | ```php 266 | return [ 267 | 'mailer' => create(Mailer::class) 268 | ->arguments('%mailer.transport%'), 269 | ]; 270 | ``` 271 | 272 | This is the same as: 273 | 274 | ```yaml 275 | services: 276 | mailer: 277 | class: Mailer 278 | arguments: ['%mailer.transport%'] 279 | ``` 280 | 281 | Services can be injected using the `get()` function helper: 282 | 283 | ```php 284 | use function Fluent\get; 285 | 286 | return [ 287 | 'newsletter_manager' => create(NewsletterManager::class) 288 | ->arguments(get('mailer')), 289 | ]; 290 | ``` 291 | 292 | This is the same as: 293 | 294 | ```yaml 295 | services: 296 | newsletter_manager: 297 | class: NewsletterManager 298 | arguments: ['@mailer'] 299 | ``` 300 | 301 | #### Setter injection 302 | 303 | ```php 304 | return [ 305 | 'mailer' => create(Mailer::class) 306 | ->method('setHostAndPort', 'smtp.google.com', 2525), 307 | ]; 308 | ``` 309 | 310 | This is the same as: 311 | 312 | ```yaml 313 | services: 314 | mailer: 315 | class: Mailer 316 | calls: 317 | - [setHostAndPort, ['smtp.google.com', 2525]] 318 | ``` 319 | 320 | #### Property injection 321 | 322 | ```php 323 | return [ 324 | 'mailer' => create(Mailer::class) 325 | ->property('host', 'smtp.google.com'), 326 | ]; 327 | ``` 328 | 329 | This is the same as: 330 | 331 | ```yaml 332 | services: 333 | mailer: 334 | class: Mailer 335 | properties: 336 | host: smtp.google.com 337 | ``` 338 | 339 | #### Optional service references 340 | 341 | Services can have [optional dependencies](https://symfony.com/doc/current/service_container/optional_dependencies.html), so that the dependency is not required for it to work. 342 | 343 | ##### Setting missing dependencies to null 344 | 345 | ```php 346 | use function Fluent\create; 347 | use function Fluent\get; 348 | 349 | return [ 350 | 'newsletter_manager' => create(NewsletterManager::class) 351 | ->arguments(get('mailer')->nullIfMissing()), 352 | ]; 353 | ``` 354 | 355 | This is the same as : 356 | 357 | ```xml 358 | 359 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | ``` 371 | 372 | ##### Ignore missing dependencies 373 | 374 | When used with setter injection, it's possible to remove the method call using `ignoreIfMissing()` : 375 | 376 | ```php 377 | use function Fluent\create; 378 | use function Fluent\get; 379 | 380 | return [ 381 | 'newsletter_manager' => create(NewsletterManager::class) 382 | ->method('setMailer', get('mailer')->ignoreIfMissing()), 383 | ]; 384 | ``` 385 | 386 | This is the same as : 387 | 388 | ```yaml 389 | services: 390 | app.newsletter_manager: 391 | class: AppBundle\Newsletter\NewsletterManager 392 | calls: 393 | - [setMailer, ['@?app.mailer']] 394 | ``` 395 | 396 | #### Private Services 397 | 398 | ```php 399 | return [ 400 | Mailer::class => create() 401 | ->private(), 402 | ]; 403 | ``` 404 | 405 | This is the same as: 406 | 407 | ```yaml 408 | services: 409 | mailer: 410 | class: Mailer 411 | public: false 412 | ``` 413 | 414 | #### Decorated services 415 | 416 | Services can be [decorated](https://symfony.com/doc/current/service_container/service_decoration.html) with the `decorate()` method 417 | 418 | ```php 419 | return [ 420 | 'decorating_mailer' => create(MailerDecorator::class) 421 | ->decorate('mailer') 422 | ->argument(get('decorating_mailer.inner')), 423 | ]; 424 | ``` 425 | 426 | This is the same as: 427 | 428 | ```yaml 429 | services: 430 | decorating_mailer: 431 | class: 'MailerDecorator' 432 | decorates: 'mailer' 433 | arguments: ['@decorating_mailer.inner'] 434 | ``` 435 | 436 | If you want to apply more than one decorator to a service, you can change the inner service name (IE the decorated service) and configure the priority of decoration : 437 | 438 | ```php 439 | return [ 440 | 'foo' => create(Foo::class), 441 | 'bar' => create(Bar::class) 442 | ->decorate('foo', 'bar.foo', 5) 443 | ->arguments(get('bar.foo')) 444 | , 445 | 'baz': create(Baz::class) 446 | ->decorate('foo', 'baz.foo', 1), 447 | ->arguments(get('baz.foo')) 448 | ]; 449 | ``` 450 | 451 | This is the same as: 452 | 453 | ```yaml 454 | foo: 455 | class: Foo 456 | 457 | bar: 458 | class: Bar 459 | decorates: foo 460 | decoration_inner_name: 'bar.foo' 461 | decoration_priority: 5 462 | arguments: ['@bar.foo'] 463 | 464 | baz: 465 | class: Baz 466 | decorates: foo 467 | decoration_inner_name: 'baz.foo' 468 | decoration_priority: 1 469 | arguments: ['@baz.inner'] 470 | ``` 471 | 472 | #### Non shared services 473 | 474 | All services [are shared by default](http://symfony.com/doc/current/service_container/shared.html). You can force the container to always create a new instance using the `unshared()` function helper: 475 | 476 | ```php 477 | return [ 478 | 'app.phpmailer' => create(PhpMailer::class) 479 | ->unshared(), 480 | ]; 481 | ``` 482 | 483 | This is the same as: 484 | 485 | ```yaml 486 | 487 | services: 488 | app.phpmailer: 489 | class: AppBundle\Mail\PhpMailer 490 | shared: false 491 | `````` 492 | 493 | #### Synthetic services 494 | 495 | Services can be injected [at runtime](http://symfony.com/doc/current/service_container/synthetic_services.html). You can inject a class instance as service, instead of configuring the container to create a new instance using the `synthetic()` function helper: 496 | 497 | ```php 498 | return [ 499 | 'app.phpmailer' => create(PhpMailer::class) 500 | ->synthetic(), 501 | ]; 502 | ``` 503 | 504 | This is the same as: 505 | 506 | ```yaml 507 | 508 | services: 509 | app.phpmailer: 510 | class: AppBundle\Mail\PhpMailer 511 | synthetic: true 512 | ``` 513 | 514 | ## Factories 515 | 516 | Services can be created by [factories](https://symfony.com/doc/current/service_container/factories.html) using the `factory()` function helper: 517 | 518 | ```php 519 | use function Fluent\factory; 520 | 521 | return [ 522 | 'newsletter_manager' => factory([NewsletterManager::class, 'create'], NewsletterManager::class) 523 | ->arguments('foo', 'bar'), 524 | ]; 525 | ``` 526 | 527 | When calling `$container->get('newsletter_manager')` the result of `NewsletterManager::create('foo', 'bar')` will be returned. 528 | 529 | This is the same as: 530 | 531 | ```yaml 532 | services: 533 | newsletter_manager: 534 | factory: ['AppBundle\Email\NewsletterManager', 'create'] 535 | class: 'AppBundle\Email\NewsletterManager' 536 | arguments: ['foo', 'bar'] 537 | ``` 538 | 539 | When using the class name as service ID, you don't have to explicitly state the class name of the service: 540 | 541 | ```php 542 | return [ 543 | // you can write: 544 | NewsletterManager::class => factory([NewsletterManager::class, 'create']), 545 | // instead of: 546 | NewsletterManager::class => factory([NewsletterManager::class, 'create'], NewsletterManager::class), 547 | ]; 548 | ``` 549 | 550 | ## Aliases 551 | 552 | Services can be aliased using the `alias()` function helper: 553 | 554 | ```php 555 | use function Fluent\create; 556 | use function Fluent\alias; 557 | 558 | return [ 559 | 'app.phpmailer' => create(PhpMailer::class), 560 | 'app.mailer' => alias('app.phpmailer'), 561 | ]; 562 | ``` 563 | 564 | When calling `$container->get('app.mailer')` the `app.phpmailer` entry will be returned. 565 | 566 | This is the same as: 567 | 568 | ```yaml 569 | services: 570 | app.phpmailer: 571 | class: AppBundle\Mail\PhpMailer 572 | app.mailer: 573 | alias: app.phpmailer 574 | ``` 575 | 576 | #### Private Aliases 577 | 578 | ```php 579 | return [ 580 | 'app.phpmailer' => create(PhpMailer::class), 581 | 'app.mailer' => alias('app.phpmailer') 582 | ->private(), 583 | ]; 584 | ``` 585 | 586 | This is the same as: 587 | 588 | ```yaml 589 | 590 | services: 591 | app.phpmailer: 592 | class: AppBundle\Mail\PhpMailer 593 | app.mailer: 594 | alias: app.phpmailer 595 | public: false 596 | ``` 597 | 598 | ## Tags 599 | 600 | Services can be tagged : 601 | 602 | ```php 603 | return [ 604 | 'mailer' => create(Mailer::class) 605 | ->tag('foo', ['fizz' => 'buzz', 'bar' => 'baz']) 606 | ->tag('bar'), 607 | ]; 608 | ``` 609 | 610 | This is the same as: 611 | 612 | ```yaml 613 | services: 614 | mailer: 615 | class: Mailer 616 | tags: 617 | - {name: foo, fizz: buzz, bar: baz} 618 | - {name: bar} 619 | ``` 620 | 621 | ## Imports 622 | 623 | Other configuration files can be imported using the `import()` function helper: 624 | 625 | ```php 626 | use function Fluent\import; 627 | 628 | return [ 629 | import('services/mailer.php'), 630 | ]; 631 | ``` 632 | 633 | You will notice that the array item is not indexed by an entry ID. 634 | 635 | This is the same as: 636 | 637 | ```yaml 638 | imports: 639 | - { resource: services/mailer.yml } 640 | ``` 641 | 642 | ## Extensions 643 | 644 | Extensions (like the framework configuration for example) can be configured using the `extension()` function helper: 645 | 646 | ```php 647 | use function Fluent\extension; 648 | 649 | return [ 650 | extension('framework', [ 651 | 'http_method_override' => true, 652 | 'trusted_proxies' => ['192.0.0.1', '10.0.0.0/8'], 653 | ]), 654 | ]; 655 | ``` 656 | 657 | This is the same as: 658 | 659 | ```yaml 660 | framework: 661 | http_method_override: true 662 | trusted_proxies: [192.0.0.1, 10.0.0.0/8] 663 | ``` 664 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mnapoli/fluent-symfony", 3 | "license": "MIT", 4 | "autoload": { 5 | "psr-4": { 6 | "Fluent\\": "src/" 7 | }, 8 | "files": [ 9 | "src/functions.php" 10 | ] 11 | }, 12 | "autoload-dev": { 13 | "psr-4": { 14 | "Fluent\\Test\\": "tests" 15 | } 16 | }, 17 | "require": { 18 | "php": ">=7.0", 19 | "symfony/dependency-injection": "^3.2", 20 | "symfony/config": "^3.2" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "^6.0", 24 | "symfony/symfony": "^3.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 13 | 14 | 15 | 16 | ./tests/ 17 | 18 | 19 | 20 | 21 | 22 | src 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/DefinitionHelper/AliasDefinitionHelper.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class AliasDefinitionHelper implements DefinitionHelper 15 | { 16 | /** 17 | * @var Alias 18 | */ 19 | private $alias; 20 | 21 | public function __construct(string $targetEntry) 22 | { 23 | $this->alias = new Alias($targetEntry); 24 | } 25 | 26 | /** 27 | * Marks the alias as private 28 | */ 29 | public function private() : self 30 | { 31 | $this->alias->setPublic(false); 32 | 33 | return $this; 34 | } 35 | 36 | public function register(string $entryId, ContainerBuilder $container) 37 | { 38 | $container->setAlias($entryId, $this->alias); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DefinitionHelper/CreateDefinitionHelper.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class CreateDefinitionHelper implements DefinitionHelper 16 | { 17 | /** 18 | * @var Definition 19 | */ 20 | private $definition; 21 | 22 | /** 23 | * Helper for defining an object. 24 | * 25 | * @param string|null $className Class name of the object. 26 | * If null, the name of the entry (in the container) will be used as class name. 27 | * @param bool $autowire Whether the class should be autowired. 28 | */ 29 | public function __construct(string $className = null, $autowire = false) 30 | { 31 | $this->definition = new Definition($className); 32 | if ($autowire) { 33 | $this->definition->setAutowired(true); 34 | } 35 | } 36 | 37 | public function register(string $entryId, ContainerBuilder $container) 38 | { 39 | if ($this->definition->getClass() === null) { 40 | $this->definition->setClass($entryId); 41 | } 42 | 43 | $container->setDefinition($entryId, $this->definition); 44 | } 45 | 46 | /** 47 | * Define the entry as lazy. 48 | * 49 | * A lazy entry is created only when it is used, a proxy is injected instead. 50 | */ 51 | public function lazy() : self 52 | { 53 | $this->definition->setLazy(true); 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * Defines the arguments to use to call the constructor. 60 | * 61 | * This method takes a variable number of arguments, example: 62 | * ->arguments($param1, $param2, $param3) 63 | * 64 | * @param mixed ... Arguments to use for calling the constructor of the class. 65 | */ 66 | public function arguments(...$arguments) : self 67 | { 68 | $this->definition->setArguments($arguments); 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * Defines a value to inject in a property of the object. 75 | * 76 | * @param string $property Entry in which to inject the value. 77 | * @param mixed $value Value to inject in the property. 78 | */ 79 | public function property(string $property, $value) : self 80 | { 81 | $this->definition->setProperty($property, $value); 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Defines a method to call and the arguments to use. 88 | * 89 | * This method takes a variable number of arguments after the method name, example: 90 | * 91 | * ->method('myMethod', $param1, $param2) 92 | * 93 | * Can be used multiple times to declare multiple calls. 94 | * 95 | * @param string $method Name of the method to call. 96 | * @param mixed ... Arguments to use for calling the method. 97 | */ 98 | public function method(string $method, ...$arguments) : self 99 | { 100 | $this->definition->addMethodCall($method, $arguments); 101 | 102 | return $this; 103 | } 104 | 105 | /** 106 | * Adds a tag to the current definition 107 | * 108 | * Can be used multiple times to declare multiple calls. 109 | * 110 | * @param string $name The tag name 111 | * @param array $attributes An array of attributes 112 | */ 113 | public function tag(string $name, array $attributes = []) : self 114 | { 115 | $this->definition->addTag($name, $attributes); 116 | 117 | return $this; 118 | } 119 | 120 | /** 121 | * Marks the definition as unshared 122 | */ 123 | public function unshared() : self 124 | { 125 | $this->definition->setShared(false); 126 | 127 | return $this; 128 | } 129 | 130 | /** 131 | * Marks the definition as synthetic 132 | */ 133 | public function synthetic() : self 134 | { 135 | $this->definition->setSynthetic(true); 136 | 137 | return $this; 138 | } 139 | 140 | /** 141 | * Marks the definition as private 142 | */ 143 | public function private() : self 144 | { 145 | $this->definition->setPublic(false); 146 | 147 | return $this; 148 | } 149 | 150 | /** 151 | 152 | * Mark the service as deprecated 153 | * 154 | * @param string|null $template Template message to use if the definition is deprecated 155 | * 156 | * @throws InvalidArgumentException 157 | */ 158 | public function deprecate(string $template = null) : self 159 | { 160 | $this->definition->setDeprecated(true, $template); 161 | 162 | return $this; 163 | } 164 | 165 | /** 166 | * Override an existing definition, keeping acces to the old one 167 | * 168 | * @param string $entryId The decorated service id 169 | * @param null|string $renamedId The new decorated service id 170 | * @param int $priority The priority of decoration 171 | */ 172 | public function decorate(string $entryId, string $renamedId = null, int $priority = 0) : self 173 | { 174 | $this->definition->setDecoratedService($entryId, $renamedId, $priority); 175 | 176 | return $this; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/DefinitionHelper/DefinitionHelper.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | interface DefinitionHelper 12 | { 13 | public function register(string $entryId, ContainerBuilder $container); 14 | } 15 | -------------------------------------------------------------------------------- /src/DefinitionHelper/ExtensionConfiguration.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class ExtensionConfiguration implements DefinitionHelper 12 | { 13 | /** 14 | * @var string 15 | */ 16 | private $extensionName; 17 | 18 | /** 19 | * @var array 20 | */ 21 | private $configuration; 22 | 23 | public function __construct(string $extensionName, array $configuration) 24 | { 25 | $this->extensionName = $extensionName; 26 | $this->configuration = $configuration; 27 | } 28 | 29 | public function register(string $entryId, ContainerBuilder $container) 30 | { 31 | $container->loadFromExtension($this->extensionName, $this->configuration); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/DefinitionHelper/FactoryDefinitionHelper.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class FactoryDefinitionHelper implements DefinitionHelper 15 | { 16 | /** 17 | * @var Definition 18 | */ 19 | private $definition; 20 | 21 | /** 22 | * @param string|array $factory A PHP function or an array containing a class/Reference and a method to call 23 | * @param string|null $className Class name of the object. 24 | * If null, the name of the entry (in the container) will be used as class name. 25 | */ 26 | public function __construct($factory, string $className = null) 27 | { 28 | $this->definition = new Definition($className); 29 | $this->definition->setFactory($factory); 30 | } 31 | 32 | public function register(string $entryId, ContainerBuilder $container) 33 | { 34 | if ($this->definition->getClass() === null) { 35 | $this->definition->setClass($entryId); 36 | } 37 | 38 | $container->setDefinition($entryId, $this->definition); 39 | } 40 | 41 | /** 42 | * Defines the arguments to use when calling the factory. 43 | * 44 | * This method takes a variable number of arguments, example: 45 | * ->arguments($param1, $param2, $param3) 46 | */ 47 | public function arguments(...$arguments) : self 48 | { 49 | $this->definition->setArguments($arguments); 50 | 51 | return $this; 52 | } 53 | 54 | /** 55 | * Marks the definition as synthetic 56 | */ 57 | public function synthetic() : self 58 | { 59 | $this->definition->setSynthetic(true); 60 | 61 | return $this; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/DefinitionHelper/ParameterDefinitionHelper.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ParameterDefinitionHelper implements DefinitionHelper 14 | { 15 | /** 16 | * Parameter value. 17 | * 18 | * @var mixed 19 | */ 20 | private $value; 21 | 22 | public function __construct($value) 23 | { 24 | $this->value = $value; 25 | } 26 | 27 | public function register(string $entryId, ContainerBuilder $container) 28 | { 29 | $container->setParameter($entryId, $this->value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/EnableFluentConfig.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | trait EnableFluentConfig 31 | { 32 | protected function getContainerLoader(ContainerInterface $container) 33 | { 34 | $locator = new FileLocator($this); 35 | $resolver = new LoaderResolver(array( 36 | new XmlFileLoader($container, $locator), 37 | new YamlFileLoader($container, $locator), 38 | new IniFileLoader($container, $locator), 39 | new PhpConfigFileLoader($container, $locator), 40 | new DirectoryLoader($container, $locator), 41 | new ClosureLoader($container), 42 | )); 43 | 44 | return new DelegatingLoader($resolver); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Import.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Import 12 | { 13 | /** 14 | * @var string 15 | */ 16 | private $resource; 17 | 18 | public function __construct(string $resource) 19 | { 20 | $this->resource = $resource; 21 | } 22 | 23 | public function getResource() : string 24 | { 25 | return $this->resource; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/PhpConfigFileLoader.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class PhpConfigFileLoader extends FileLoader 18 | { 19 | /** 20 | * @var PhpConfigLoader 21 | */ 22 | private $loader; 23 | 24 | public function __construct(ContainerBuilder $container, FileLocatorInterface $locator) 25 | { 26 | parent::__construct($container, $locator); 27 | 28 | $this->loader = new PhpConfigLoader($container); 29 | } 30 | 31 | public function load($resource, $type = null) 32 | { 33 | // The container and loader variables are exposed to the included file below 34 | // This is done to support "traditional" PHP config files 35 | // @see \Symfony\Component\DependencyInjection\Loader\PhpFileLoader 36 | $container = $this->container; 37 | $loader = $this; 38 | 39 | $path = $this->locator->locate($resource); 40 | $this->setCurrentDir(dirname($path)); 41 | $this->container->addResource(new FileResource($path)); 42 | 43 | $definitions = require $path; 44 | 45 | if (!is_array($definitions)) { 46 | // Support for traditional PHP config files 47 | return; 48 | } 49 | 50 | // Process imports 51 | foreach ($definitions as $entryId => $definition) { 52 | if ($definition instanceof Import) { 53 | // Import the resource 54 | $this->import($definition->getResource()); 55 | // Remove it from the array 56 | unset($definitions[$entryId]); 57 | } 58 | } 59 | 60 | $this->loader->load($definitions); 61 | } 62 | 63 | public function supports($resource, $type = null) 64 | { 65 | if (!is_string($resource)) { 66 | return false; 67 | } 68 | 69 | if (null === $type && 'php' === pathinfo($resource, PATHINFO_EXTENSION)) { 70 | return true; 71 | } 72 | 73 | return 'php' === $type; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/PhpConfigLoader.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class PhpConfigLoader extends Loader 17 | { 18 | /** 19 | * @var ContainerBuilder 20 | */ 21 | private $container; 22 | 23 | public function __construct(ContainerBuilder $container) 24 | { 25 | $this->container = $container; 26 | } 27 | 28 | public function load($resource, $type = null) 29 | { 30 | if (!is_array($resource)) { 31 | throw new \Exception('Invalid resource'); 32 | } 33 | 34 | $definitions = $resource; 35 | 36 | foreach ($definitions as $entryId => $definitionHelper) { 37 | // Raw values are automatically turned into parameters 38 | if (!$definitionHelper instanceof DefinitionHelper) { 39 | $definitionHelper = new ParameterDefinitionHelper($definitionHelper); 40 | } 41 | 42 | // Register the definition in the container 43 | $definitionHelper->register((string) $entryId, $this->container); 44 | } 45 | } 46 | 47 | public function supports($resource, $type = null) 48 | { 49 | return is_array($resource); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Reference.php: -------------------------------------------------------------------------------- 1 | ['192.0.0.1', '10.0.0.0/8'], 81 | * ]), 82 | * ]; 83 | */ 84 | function extension(string $extensionName, array $config) : ExtensionConfiguration 85 | { 86 | return new ExtensionConfiguration($extensionName, $config); 87 | } 88 | 89 | } 90 | --------------------------------------------------------------------------------