<?phpnamespace Payum\Bundle\PayumBundle\DependencyInjection;use Payum\Bundle\PayumBundle\DependencyInjection\Factory\Storage\StorageFactoryInterface;use Payum\Core\Exception\LogicException;use Symfony\Component\Config\Definition\ConfigurationInterface;use Symfony\Component\Config\Definition\Builder\TreeBuilder;use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;use Payum\Core\Model\GatewayConfigInterface;use Payum\Core\Security\TokenInterface;class MainConfiguration implements ConfigurationInterface{    /**     * @var StorageFactoryInterface[]     */    protected array $storageFactories = array();    /**     * @param StorageFactoryInterface[] $storageFactories     */    public function __construct(array $storageFactories)    {        foreach ($storageFactories as $storageFactory) {            $this->storageFactories[$storageFactory->getName()] = $storageFactory;        }    }    /**     * {@inheritDoc}     */    public function getConfigTreeBuilder(): TreeBuilder    {        $tb = new TreeBuilder('payum');        $rootNode = $tb->getRootNode();        $securityNode = $rootNode->children()            ->arrayNode('security')->isRequired()        ;        $this->addSecuritySection($securityNode);        $dynamicGatewaysNode = $rootNode->children()            ->arrayNode('dynamic_gateways')        ;        $this->addDynamicGatewaysSection($dynamicGatewaysNode);        $rootNode            ->children()            ->arrayNode('gateways')                ->useAttributeAsKey('name')                ->prototype('variable')                ->treatNullLike([])        ;                $this->addStoragesSection($rootNode);        return $tb;    }    protected function addStoragesSection(ArrayNodeDefinition $rootPrototypeNode): void    {        $storageNode = $rootPrototypeNode->children()                ->arrayNode('storages')                ->validate()                    ->ifTrue(function($v) {                        $storages = $v;                        unset($storages['extension']);                        foreach($storages as $key => $value) {                            if (false === class_exists($key)) {                                throw new LogicException(sprintf(                                    'The storage entry must be a valid model class. It is set %s',                                    $key                                ));                            }                        }                                            return false;                    })                    ->thenInvalid('A message')                ->end()                ->useAttributeAsKey('key')                ->prototype('array')        ;        $storageNode            ->validate()                ->ifTrue(function($v) {                    $storages = $v;                    unset($storages['extension']);                    if (count($storages) === 0) {                        throw new LogicException('At least one storage must be configured.');                    }                    if (count($storages) > 1) {                        throw new LogicException('Only one storage per entry could be selected');                    }                                        return false;                })                ->thenInvalid('A message')            ->end()        ;        $storageNode->children()            ->arrayNode('extension')                ->addDefaultsIfNotSet()                ->children()                    ->booleanNode('all')->defaultValue(true)->end()                    ->arrayNode('gateways')                        ->useAttributeAsKey('key')                        ->prototype('scalar')                    ->end()->end()                    ->arrayNode('factories')                        ->useAttributeAsKey('key')                        ->prototype('scalar')                    ->end()->end()                ->end()            ->end()        ->end();                foreach ($this->storageFactories as $factory) {            $factory->addConfiguration(                $storageNode->children()->arrayNode($factory->getName())            );        }    }    protected function addSecuritySection(ArrayNodeDefinition $securityNode): void    {        $storageNode = $securityNode->children()            ->arrayNode('token_storage')            ->isRequired()            ->validate()            ->ifTrue(function($v) {                foreach($v as $key => $value) {                    if (false === class_exists($key)) {                        throw new LogicException(sprintf(                            'The storage entry must be a valid model class. It is set %s',                            $key                        ));                    }                    $rc = new \ReflectionClass($key);                    if (false === $rc->implementsInterface(TokenInterface::class)) {                        throw new LogicException('The token class must implement `Payum\Core\Security\TokenInterface` interface');                    }                    if (count($v) > 1) {                        throw new LogicException('Only one token storage could be configured.');                    }                }                return false;            })            ->thenInvalid('A message')            ->end()            ->useAttributeAsKey('key')            ->prototype('array')        ;        $storageNode            ->validate()            ->ifTrue(function($v) {                if (count($v) === 0) {                    throw new LogicException('At least one storage must be configured.');                }                if (count($v) > 1) {                    throw new LogicException('Only one storage per entry could be selected');                }                return false;            })            ->thenInvalid('A message')            ->end()        ;        foreach ($this->storageFactories as $factory) {            $factory->addConfiguration(                $storageNode->children()->arrayNode($factory->getName())            );        }    }    protected function addDynamicGatewaysSection(ArrayNodeDefinition $dynamicGatewaysNode): void    {        $dynamicGatewaysNode->children()            ->booleanNode('sonata_admin')->defaultFalse()        ;        $storageNode = $dynamicGatewaysNode->children()            ->arrayNode('config_storage')            ->isRequired()            ->validate()            ->ifTrue(function($v) {                foreach($v as $key => $value) {                    if (false === class_exists($key)) {                        throw new LogicException(sprintf(                            'The storage entry must be a valid model class. It is set %s',                            $key                        ));                    }                    $rc = new \ReflectionClass($key);                    if (false === $rc->implementsInterface(GatewayConfigInterface::class)) {                        throw new LogicException('The config class must implement `Payum\Core\Model\GatewayConfigInterface` interface');                    }                    if (count($v) > 1) {                        throw new LogicException('Only one config storage could be configured.');                    }                }                return false;            })            ->thenInvalid('A message')            ->end()            ->useAttributeAsKey('key')            ->prototype('array')        ;        $storageNode            ->validate()            ->ifTrue(function($v) {                if (count($v) === 0) {                    throw new LogicException('At least one storage must be configured.');                }                if (count($v) > 1) {                    throw new LogicException('Only one storage per entry could be selected');                }                return false;            })            ->thenInvalid('A message')            ->end()        ;        $dynamicGatewaysNode->children()            ->arrayNode('encryption')                ->children()                    ->scalarNode('defuse_secret_key')->cannotBeEmpty()->end()        ;        foreach ($this->storageFactories as $factory) {            $factory->addConfiguration(                $storageNode->children()->arrayNode($factory->getName())            );        }    }}