Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm-baseline.xml"
findUnusedPsalmSuppress="true"
findUnusedBaselineEntry="true"
findUnusedCode="true"
>
<projectFiles>
Expand Down
4 changes: 3 additions & 1 deletion src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,13 @@
* For classes known from the definitions, a type preference might be the
* better approach
*
* @deprecated Since 3.16.0. This class will be removed in 4.0. Please use the immutable and typesafe
* {@link Config\InjectionConfig} variant.
*
* @see \Laminas\Di\Resolver\ValueInjection A container to force injection of a value
* @see \Laminas\Di\Resolver\TypeInjection A container to force looking up a specific type instance for injection
*
* @final
*
* @psalm-type TypeConfigArray = array{
* typeOf?: class-string|null,
* preferences?: array<string, string>|null,
Expand Down
19 changes: 19 additions & 0 deletions src/Config/AliasConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Laminas\Di\Config;

/**
* Provides type configuration for a type alias (virtual type)
*
* @readonly
*/
final class AliasConfig
{
public function __construct(
public readonly string $name,
public readonly TypeConfig $type,
) {
}
}
11 changes: 11 additions & 0 deletions src/Config/Exception/InvalidConfigException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Laminas\Di\Config\Exception;

use Laminas\Di\Exception\UnexpectedValueException;

class InvalidConfigException extends UnexpectedValueException

Check failure on line 9 in src/Config/Exception/InvalidConfigException.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

InvalidExtendClass

src/Config/Exception/InvalidConfigException.php:9:38: InvalidExtendClass: Class Laminas\Di\Config\Exception\InvalidConfigException may not inherit from final class Laminas\Di\Exception\UnexpectedValueException (see https://psalm.dev/232)
{
}
20 changes: 20 additions & 0 deletions src/Config/Exception/InvalidParametersException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Laminas\Di\Config\Exception;

use function sprintf;

class InvalidParametersException extends InvalidConfigException

Check failure on line 9 in src/Config/Exception/InvalidParametersException.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

ClassMustBeFinal

src/Config/Exception/InvalidParametersException.php:9:7: ClassMustBeFinal: Class Laminas\Di\Config\Exception\InvalidParametersException is never extended and is not part of the public API, and thus must be made final. (see https://psalm.dev/361)
{
public static function numericParamKey(int $key): self
{
return new self(
sprintf(
'Parameter name must be an identifier, got a numeric index %d',
$key,
),
);
}
}
11 changes: 11 additions & 0 deletions src/Config/Exception/InvalidTypePreferenceException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Laminas\Di\Config\Exception;

use Laminas\Di\Config\Exception\InvalidConfigException;

class InvalidTypePreferenceException extends InvalidConfigException

Check failure on line 9 in src/Config/Exception/InvalidTypePreferenceException.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

ClassMustBeFinal

src/Config/Exception/InvalidTypePreferenceException.php:9:7: ClassMustBeFinal: Class Laminas\Di\Config\Exception\InvalidTypePreferenceException is never extended and is not part of the public API, and thus must be made final. (see https://psalm.dev/361)
{
}
24 changes: 24 additions & 0 deletions src/Config/Exception/UndefinedClassException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Laminas\Di\Config\Exception;

use Throwable;

use function sprintf;

/**
* This is thrown when a configured type does not exist
*/
class UndefinedClassException extends InvalidConfigException

Check failure on line 14 in src/Config/Exception/UndefinedClassException.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

ClassMustBeFinal

src/Config/Exception/UndefinedClassException.php:14:7: ClassMustBeFinal: Class Laminas\Di\Config\Exception\UndefinedClassException is never extended and is not part of the public API, and thus must be made final. (see https://psalm.dev/361)
{
public function __construct(
public readonly string $className,

Check failure on line 17 in src/Config/Exception/UndefinedClassException.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

PossiblyUnusedProperty

src/Config/Exception/UndefinedClassException.php:17:32: PossiblyUnusedProperty: Cannot find any references to property Laminas\Di\Config\Exception\UndefinedClassException::$className (see https://psalm.dev/149)
string|null $message = null,
int $code = 0,
Throwable|null $previous = null
) {
parent::__construct($message ?? sprintf('Class "%s" does not exists', $className), $code, $previous);
}
}
166 changes: 166 additions & 0 deletions src/Config/InjectionConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

declare(strict_types=1);

namespace Laminas\Di\Config;

use ArrayAccess;
use Laminas\Di\Config\Exception\InvalidConfigException;
use Laminas\Di\ConfigInterface;
use Laminas\Di\Exception\LogicException;

use function array_keys;
use function get_debug_type;
use function is_array;
use function sprintf;

/**
* Provides a typesafe and immutable DI configuration from a config array.
*
* This configures the instantiation process of the dependency injector.
*
* **Example:**
*
* <code>
* return [
* // This section provides global type preferences.
* // Those are visited if a specific instance has no preference definitions.
* 'preferences' => [
* // The key is the requested class or interface name, the values are
* // the types the dependency injector should prefer.
* Some\Interface::class => Some\Preference::class
* ],
* // This configures the instantiation of specific types.
* // Types may also be purely virtual by defining the aliasOf key.
* 'types' => [
* My\Class::class => [
* 'preferences' => [
* // this supercedes the global type preferences
* // when My\Class is instantiated
* Some\Interface::class => 'My.SpecificAlias'
* ],
*
* // instantiation parameters. These will only be used for
* // the instantiator (i.e. the constructor)
* 'parameters' => [
* 'foo' => My\FooImpl::class, // Use the given type to provide the injection (depends on definition)
* 'bar' => '*' // Use the type preferences
* ],
* ],
*
* 'My.Alias' => [
* // typeOf defines virtual classes which can be used as type
* // preferences or for newInstance calls. They allow providing
* // custom configs for a class
* 'typeOf' => Some\Class::class,
* 'preferences' => [
* Foo::class => Bar::class
* ]
* ]
* ]
* ];
* </code>
*
* ## Notes on Injections
*
* Named arguments and Automatic type lookups will only work for Methods that
* are known to the dependency injector through its definitions. Injections for
* unknown methods do not perform type lookups on its own.
*
* A value injection without any lookups can be forced by providing a
* Resolver\ValueInjection instance.
*
* To force a service/class instance provide a Resolver\TypeInjection instance.
* For classes known from the definitions, a type preference might be the
* better approach
*
* @see \Laminas\Di\Resolver\ValueInjection A container to force injection of a value
* @see \Laminas\Di\Resolver\TypeInjection A container to force looking up a specific type instance for injection
*
* @readonly
Copy link
Member

Choose a reason for hiding this comment

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

FWIW, this can now be marked readonly via the language itself

Copy link
Member Author

Choose a reason for hiding this comment

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

Since support for php 8.1 is now gone, indeed.

*/
final class InjectionConfig implements ConfigInterface
{
/**
* @param array<string, TypeConfig|AliasConfig> $types
*/
public function __construct(
private readonly TypePreferences $preferences = new TypePreferences(),
private readonly array $types = []
) {
}

/**
* Constructs the injection config from a laminas config value
*/
public static function fromConfigValue(mixed $config): self
{
if (! is_array($config) && ! $config instanceof ArrayAccess) {
throw new InvalidConfigException(
sprintf(
'Di configuration must be an array or implement ArrayAccess, got %s',
get_debug_type($config),
),
);
}

return new self(
TypePreferences::fromConfigValue($config['preferences'] ?? []),
TypeConfig::mapFromConfigValue($config['types'] ?? []),
);
}

private function getTypeConfig(string $name): TypeConfig|null
{
$type = $this->types[$name] ?? null;
return $type instanceof AliasConfig ? $type->type : $type;
}

public function isAlias(string $name): bool

Check failure on line 119 in src/Config/InjectionConfig.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

MissingOverrideAttribute

src/Config/InjectionConfig.php:119:5: MissingOverrideAttribute: Method Laminas\Di\Config\InjectionConfig::isalias should have the "Override" attribute (see https://psalm.dev/358)
{
$type = $this->types[$name] ?? null;
return $type instanceof AliasConfig;
}

public function getConfiguredTypeNames(): array

Check failure on line 125 in src/Config/InjectionConfig.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

MissingOverrideAttribute

src/Config/InjectionConfig.php:125:5: MissingOverrideAttribute: Method Laminas\Di\Config\InjectionConfig::getconfiguredtypenames should have the "Override" attribute (see https://psalm.dev/358)
{
return array_keys($this->types);
}

public function getClassForAlias(string $name): string|null

Check failure on line 130 in src/Config/InjectionConfig.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

MissingOverrideAttribute

src/Config/InjectionConfig.php:130:5: MissingOverrideAttribute: Method Laminas\Di\Config\InjectionConfig::getclassforalias should have the "Override" attribute (see https://psalm.dev/358)
{
return $this->getTypeConfig($name)?->getClassName();
}

public function getParameters(string $type): array

Check failure on line 135 in src/Config/InjectionConfig.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

MissingOverrideAttribute

src/Config/InjectionConfig.php:135:5: MissingOverrideAttribute: Method Laminas\Di\Config\InjectionConfig::getparameters should have the "Override" attribute (see https://psalm.dev/358)
{
/**
* Psalm-Bug: https://github.com/vimeo/psalm/issues/7099
*
* @psalm-suppress RedundantCondition
* @psalm-suppress TypeDoesNotContainNull
*/
return $this->getTypeConfig($type)?->parameters->toArray() ?? [];
}

public function setParameters(string $type, array $params)
{
throw new LogicException(
'Injection config is considered immutable. You can set [dependencies][auto][mutableConfig] to '
. 'true to restore the previous, but deprecated, behavior'
);
}

public function getTypePreference(string $type, string|null $contextClass = null): string|null
{
if ($contextClass !== null) {
$preference = $this->getTypeConfig($contextClass)?->preferences->getPreferenceFor($type);

if ($preference !== null) {
return $preference;
}
}

return $this->preferences->getPreferenceFor($type);
}
}
34 changes: 34 additions & 0 deletions src/Config/Parameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Laminas\Di\Config;

use Laminas\Di\Resolver\InjectionInterface;
use Laminas\Di\Resolver\ValueInjection;

use function is_string;

/**
* Parameter injection configuration
*
* @readonly
*/
final class Parameter
{
public function __construct(
public readonly string $name,
public readonly string|InjectionInterface $injection,
) {
}

public static function fromValue(string $name, mixed $value): self
{
return new self(
$name,
is_string($value) || $value instanceof InjectionInterface
? $value
: new ValueInjection($value),
);
}
}
Loading
Loading