Skip to content

[DI] Add "PHP fluent format" for configuring the container #23834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\ExpressionLanguage\Expression;

abstract class AbstractConfigurator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing @author tag

{
const FACTORY = 'unknown';

public function __call($method, $args)
{
if (method_exists($this, 'set'.$method)) {
return call_user_func_array(array($this, 'set'.$method), $args);
}

throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_class($this), $method));
}

/**
* Checks that a value is valid, optionally replacing Definition and Reference configurators by their configure value.
*
* @param mixed $value
* @param bool $allowServices whether Definition and Reference are allowed; by default, only scalars and arrays are
*
* @return mixed the value, optionaly cast to a Definition/Reference
*/
public static function processValue($value, $allowServices = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any usage where $allowServices is false. Why do we need that then?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in ContainerConfigurator::extension()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it be protected?

{
if (is_array($value)) {
foreach ($value as $k => $v) {
$value[$k] = static::processValue($v, $allowServices);
}

return $value;
}

if ($value instanceof ReferenceConfigurator) {
static $refCast;

if (!$refCast) {
$refCast = \Closure::bind(function ($value) {
return new Reference($value->id, $value->invalidBehavior);
}, null, $value);
}

// cast ReferenceConfigurator to Reference
return $refCast($value);
}

if ($value instanceof InlineServiceConfigurator) {
static $defCast;

if (!$defCast) {
$defCast = \Closure::bind(function ($value) {
$def = $value->definition;
$value->definition = null;

return $def;
}, null, $value);
}

// cast InlineServiceConfigurator to Definition
return $defCast($value);
}

if ($value instanceof self) {
throw new InvalidArgumentException(sprintf('"%s()" can be used only at the root of service configuration files.', $value::FACTORY));
}

switch (true) {
case null === $value:
case is_scalar($value):
return $value;

case $value instanceof ArgumentInterface:
case $value instanceof Definition:
case $value instanceof Expression:
case $value instanceof Parameter:
case $value instanceof Reference:
if ($allowServices) {
return $value;
}
}

throw new InvalidArgumentException(sprintf('Cannot use values of type "%s" in service configuration files.', is_object($value) ? get_class($value) : gettype($value)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;

abstract class AbstractServiceConfigurator extends AbstractConfigurator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing @author tag

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@author tags are not required.

{
protected $parent;
protected $definition;
protected $id;
protected $defaultTags = array();

public function __construct(ServicesConfigurator $parent, Definition $definition, $id = null, array $defaultTags = array())
{
$this->parent = $parent;
$this->definition = $definition;
$this->id = $id;
$this->defaultTags = $defaultTags;
}

public function __destruct()
{
// default tags should be added last
foreach ($this->defaultTags as $name => $attributes) {
foreach ($attributes as $attributes) {
$this->definition->addTag($name, $attributes);
}
}
$this->defaultTags = array();
}

/**
* Registers a service.
*
* @param string $id
* @param string|null $class
*
* @return ServiceConfigurator
*/
final public function set($id, $class = null)
{
$this->__destruct();

return $this->parent->set($id, $class);
}

/**
* Creates an alias.
*
* @param string $id
* @param string $ref
*
* @return AliasConfigurator
*/
final public function alias($id, $referencedId)
{
$this->__destruct();

return $this->parent->alias($id, $referencedId);
}

/**
* Registers a PSR-4 namespace using a glob pattern.
*
* @param string $namespace
* @param string $resource
*
* @return PrototypeConfigurator
*/
final public function load($namespace, $resource)
{
$this->__destruct();

return $this->parent->load($namespace, $resource);
}

/**
* Gets an already defined service definition.
*
* @param string $id
*
* @return ServiceConfigurator
*
* @throws ServiceNotFoundException if the service definition does not exist
*/
final public function get($id)
{
$this->__destruct();

return $this->parent->get($id);
}

/**
* Registers a service.
*
* @param string $id
* @param string|null $class
*
* @return ServiceConfigurator
*/
final public function __invoke($id, $class = null)
{
$this->__destruct();

return $this->parent->set($id, $class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$this-parent is defined as AbstractConfigurator but this class does not have a set method. So it does not seem safe to call this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, parent is now a ServicesConfigurator so the issue is gone

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\DependencyInjection\Alias;

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class AliasConfigurator extends AbstractServiceConfigurator
{
const FACTORY = 'alias';

use Traits\PublicTrait;

public function __construct(ServicesConfigurator $parent, Alias $alias)
{
$this->parent = $parent;
$this->definition = $alias;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\ExpressionLanguage\Expression;

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class ContainerConfigurator extends AbstractConfigurator
{
const FACTORY = 'container';

private $container;
private $loader;
private $instanceof;
private $path;
private $file;

public function __construct(ContainerBuilder $container, PhpFileLoader $loader, &$instanceof, $path, $file)
{
$this->container = $container;
$this->loader = $loader;
$this->instanceof = &$instanceof;
$this->path = $path;
$this->file = $file;
}

final public function extension($namespace, array $config)
{
if (!$this->container->hasExtension($namespace)) {
$extensions = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions()));
throw new InvalidArgumentException(sprintf(
'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s',
$namespace,
$this->file,
$namespace,
$extensions ? sprintf('"%s"', implode('", "', $extensions)) : 'none'
));
}

$this->container->loadFromExtension($namespace, static::processValue($config));
}

final public function import($resource, $type = null, $ignoreErrors = false)
{
$this->loader->setCurrentDir(dirname($this->path));
$this->loader->import($resource, $type, $ignoreErrors, $this->file);
}

/**
* @return ParametersConfigurator
*/
public function parameters()
{
return new ParametersConfigurator($this->container);
}

/**
* @return ServicesConfigurator
*/
public function services()
{
return new ServicesConfigurator($this->container, $this->loader, $this->instanceof);
}
}

/**
* Creates a service reference.
*
* @param string $id
*
* @return ReferenceConfigurator
*/
function ref($id)
{
return new ReferenceConfigurator($id);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to keep the functions? If functions aren't the official way of doing things, nobody will use them. So I'd prefer less options things to maintain, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I disagree with keeping them, but not very heavily. So it's ok for me :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the functions as well :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Ryan and Fabien. It's better to provide just one solution.


/**
* Creates an inline service.
*
* @param string|null $class
*
* @return InlineServiceConfigurator
*/
function inline($class = null)
{
return new InlineServiceConfigurator(new Definition($class));
}

/**
* Creates a lazy iterator.
*
* @param ReferenceConfigurator[] $values
*
* @return IteratorArgument
*/
function iterator(array $values)
{
return new IteratorArgument(AbstractConfigurator::processValue($values, true));
}

/**
* Creates an expression.
*
* @param string $expression an expression
*
* @return Expression
*/
function expr($expression)
{
return new Expression($expression);
}
Loading