PHPackages                             kostislav/php-class-symfony-service-config - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. kostislav/php-class-symfony-service-config

ActiveLibrary[Framework](/categories/framework)

kostislav/php-class-symfony-service-config
==========================================

Class-based service configuration for Symfony

1.0.0(5y ago)14MITPHPPHP &gt;=8.0

Since Apr 18Pushed 5y ago1 watchersCompare

[ Source](https://github.com/kostislav/php-class-symfony-service-config)[ Packagist](https://packagist.org/packages/kostislav/php-class-symfony-service-config)[ RSS](/packages/kostislav-php-class-symfony-service-config/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (3)Versions (2)Used By (0)

PHP-based Symfony service configuration
=======================================

[](#php-based-symfony-service-configuration)

[![Version](https://camo.githubusercontent.com/b38728e7443c35a7db04ca7592645f5cad19d5f892d211fc77f0ee9d521ac788/68747470733a2f2f706f7365722e707567782e6f72672f6b6f737469736c61762f7068702d636c6173732d73796d666f6e792d736572766963652d636f6e6669672f76657273696f6e)](//packagist.org/packages/kostislav/php-class-symfony-service-config)[![Build status](https://github.com/kostislav/php-class-symfony-service-config/actions/workflows/php.yml/badge.svg)](https://github.com/kostislav/php-class-symfony-service-config/actions/workflows/php.yml/badge.svg)

The Symfony framework offers three methods of configuring its [Dependency Injection Container](https://symfony.com/doc/current/service_container.html#explicitly-configuring-services-and-arguments): XML configuration, YAML configuration and PHP configuration. The XML and YAML approaches are a little developer-unfriendly because of things like missing IDE completion. The PHP approach tries to solve these problems, but instead of using PHP directly, it invents a DSL on top of PHP and in turn suffers from the same problems, albeit to a lesser degree.

This library offers a different method of configuring services using plain PHP, heavily inspired by [Java's Spring framework Java-based configuration](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-java).

**NOTE:** The library makes extensive use of [PHP attributes](https://www.php.net/manual/en/language.attributes.overview.php), which means it requires at least PHP 8.0.

Example
-------

[](#example)

Consider the following simple setup: One public service depending on two other services. In Symfony PHP config, it might look like this:

```
return function(ContainerConfigurator $configurator) {
    $services = $configurator->services();

    $services->set('innerService1', SimpleService::class)
        ->arg(['Hello']);

    $services->set('innerService2', SimpleService::class)
        ->args(['world!']);

    $services->set('combinedService', CombinedService::class)
        ->args(service('innerService1'), service('innerService2'))
        ->public();
}
```

Or, if you prefer YAML:

```
service1:
  class: SimpleService
  arguments: [ 'Hello' ]

service2:
  class: SimpleService
  arguments: [ 'world!' ]

combinedService:
  class: CombinedService
  public: true
  arguments: [ '@service1', '@service2' ]
```

Using this library, you can write the same definition like this:

```
class ExampleConfig {
    #[ServiceDefinition]
    public function innerService1(): SimpleService {
        return new SimpleService('Hello');
    }

    #[ServiceDefinition]
    public function innerService2(): SimpleService {
        return new SimpleService('world!');
    }

    #[ServiceDefinition(isPublic: true)]
    public function combinedService(SimpleService $innerService1, SimpleService $innerService2): CombinedService {
        return new CombinedService($innerService1, $innerService2);
    }
}
```

For this simple example, the code might not be shorter, but there is an obvious advantage: the creation of your services is written in plain PHP. IDE features like code completion, type hinting and usage analysis work out of the box. And if you need to do something more complicated, like calling a setter or using a static factory method, you can do it like in any other place in your codebase - no need to learn yet another [expression language](https://symfony.com/doc/current/service_container/expression_language.html) to call a method on another service.

Setting it up
-------------

[](#setting-it-up)

1. Add a composer dependency on `kostislav/php-class-symfony-service-config`
2. Unfortunately, Symfony does not offer any way for the library to hook into the right internals, so a manual change of your `Kernel` class is necessary. Add the following line to the `configureContainer` method:

```
protected function configureContainer(ContainerBuilder $container, Loader $loader): void {
    $loader->getResolver()->addLoader(new Kostislav\ClassConfig\ConfigClassServiceConfigLoader($container));
    // other stuff
}
```

3. Register your configuration classes with the `$loader`. A convenient place to do it is in the `Kernel` class, just below the line added in step 2.

```
protected function configureContainer(ContainerBuilder $container, Loader $loader): void {
    $loader->getResolver()->addLoader(new Kostislav\ClassConfig\ConfigClassServiceConfigLoader($container));
    // now you can load all your config classes with $loader
    $loader->load(MyConfig::class);
}
```

What it does
------------

[](#what-it-does)

By default, each public non-static method in the config class that is annotated with the `ServiceDefinition` attribute will be used to create a private service with the same name as that method. This method **must** have a return type hint. Any parameters of this method will be added as dependencies of this service and will be resolved by name.

The config class itself must have a no-argument constructor.

### Explicitly naming a service

[](#explicitly-naming-a-service)

If you need a service to have a name other than the defining method, pass it to the `ServiceDefinition` attribute.

```
#[ServiceDefinition('alternative.name')]
public function whatever(): SimpleService {
    return new SimpleService('serv1');
}
```

### Public services

[](#public-services)

In Symfony, services are by default private and cannot be requested from the container. Things like controllers need to be marked as public. This is controlled by the `isPublic` parameter of the `ServiceDefinition` attribute.

```
#[ServiceDefinition(isPublic: true)]
public function publicService(): SimpleService {
    return new SimpleService('serv1');
}
```

### Using a service with a name that cannot be used as a PHP identifier

[](#using-a-service-with-a-name-that-cannot-be-used-as-a-php-identifier)

Symfony is full of services with names like `annotations.cached_reader`. As PHP won't allow us to use parameter names like that, you can specify the name of the injected service explicitly using the `Service` parameter attribute.

```
#[ServiceDefinition]
public function myService(#[Service('annotations.cached_reader')] Reader annotationReader): SimpleService {
    // do whatever here
}
```

### Using container parameters

[](#using-container-parameters)

Method parameters annotated with the `Parameter` attribute will be populated with the corresponding container parameter rather than a service. Again, by default, the method parameter name is used as the name of the parameter to look up, but another name can be specified on the attribute.

```
#[ServiceDefinition]
public function myService(#[Parameter('kernel.debug')] string $debug): SimpleService {
    // do whatever here
}
```

### Splitting the configuration class

[](#splitting-the-configuration-class)

When you have a lot of services, the configuration class can get long. You can split it into multiple classes and either load each of them in your `Kernel`, or just load one of them and use the `Import` attribute to include the other ones. Services from the imported classes will be available in the importing config class.

Imports work transitively - if the imported class itself has another `Import` attribute with another class, that class will be included as well.

```
#[Import(AnotherConfig::class)]
class OneConfig {
    // some service definitions here
}
```

### Service tags

[](#service-tags)

If you need to tag your service, just add a `Tag` attribute. There can be multiple `Tag` attributes on the same method.

```
#[Tag('kernel.event_listener')]
#[ServiceDefinition]
public function myService(): SimpleService {
    // do whatever here
}
```

Performance
-----------

[](#performance)

What is the performance impact of all this reflection and attribute reading? Don't worry, the configuration classes are only analyzed once when the container is built, at the same time the `services.yaml` file would get parsed. After that, the performance difference compared to the other approaches is one additional method call (of the service definition method), which isn't noticeable at all.

###  Health Score

25

—

LowBetter than 37% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity5

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1850d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/062c6b066bcb0b700107a6f825c4854c7bbeebfba3e574dbe0e27ba4ecb0ba04?d=identicon)[snugar\_i](/maintainers/snugar_i)

---

Top Contributors

[![kostislav](https://avatars.githubusercontent.com/u/5141029?v=4)](https://github.com/kostislav "kostislav (17 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/kostislav-php-class-symfony-service-config/health.svg)

```
[![Health](https://phpackages.com/badges/kostislav-php-class-symfony-service-config/health.svg)](https://phpackages.com/packages/kostislav-php-class-symfony-service-config)
```

###  Alternatives

[symfony/framework-bundle

Provides a tight integration between Symfony components and the Symfony full-stack framework

3.6k235.4M9.7k](/packages/symfony-framework-bundle)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[contao/core-bundle

Contao Open Source CMS

1231.6M2.4k](/packages/contao-core-bundle)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

81733.7k](/packages/flow-php-flow)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
