• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

68publishers / smart-nette-component / 3671547795

pending completion
3671547795

Pull #3

github

GitHub
Merge f7302d948 into d93ee51a5
Pull Request #3: WIP: v1

389 of 389 new or added lines in 24 files covered. (100.0%)

408 of 419 relevant lines covered (97.37%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.03
/src/Bridge/Nette/DI/ComponentAuthorizationExtension.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace SixtyEightPublishers\SmartNetteComponent\Bridge\Nette\DI;
6

7
use ReflectionClass;
8
use RuntimeException;
9
use Nette\Schema\Expect;
10
use Nette\Schema\Schema;
11
use ReflectionException;
12
use Nette\Loaders\RobotLoader;
13
use Nette\DI\CompilerExtension;
14
use Nette\Application\IPresenter;
15
use Nette\Application\UI\Control;
16
use Composer\Autoload\ClassLoader;
17
use Nette\Application\UI\Presenter;
18
use Nette\DI\Definitions\Reference;
19
use Nette\DI\Definitions\Statement;
20
use Nette\DI\Definitions\Definition;
21
use Nette\DI\Definitions\FactoryDefinition;
22
use Nette\DI\Definitions\ServiceDefinition;
23
use SixtyEightPublishers\SmartNetteComponent\Reader\AttributesMap;
24
use SixtyEightPublishers\SmartNetteComponent\Attribute\AttributeInterface;
25
use SixtyEightPublishers\SmartNetteComponent\Attribute\AttributePrototype;
26
use SixtyEightPublishers\SmartNetteComponent\Reader\StaticAttributeReader;
27
use SixtyEightPublishers\SmartNetteComponent\Authorization\RuleHandlerInterface;
28
use SixtyEightPublishers\SmartNetteComponent\Authorization\ComponentAuthorizatorAwareInterface;
29
use function assert;
30
use function dirname;
31
use function fnmatch;
32
use function is_file;
33
use function sprintf;
34
use function array_map;
35
use function array_keys;
36
use function array_merge;
37
use function array_filter;
38
use function array_unique;
39
use function array_values;
40
use function class_exists;
41
use function sys_get_temp_dir;
42

43
final class ComponentAuthorizationExtension extends CompilerExtension
44
{
45
        public function getConfigSchema(): Schema
46
        {
47
                $parameters = $this->getContainerBuilder()->parameters;
1✔
48
                $debugMode = (bool) ($parameters['debugMode'] ?? false);
1✔
49
                $scanDirs = array_filter([$parameters['appDir'] ?? null]);
1✔
50

51
                return Expect::structure([
1✔
52
                        'cache' => Expect::bool($debugMode),
1✔
53
                        'scanDirs' => Expect::anyOf(
1✔
54
                                Expect::arrayOf('string')->default($scanDirs)->mergeDefaults(),
1✔
55
                                false
1✔
56
                        )->firstIsDefault(),
1✔
57
                        'scanComposer' => Expect::bool(class_exists(ClassLoader::class)),
1✔
58
                        'scanFilters' => Expect::arrayOf('string')
1✔
59
                                ->default([
1✔
60
                                        '*Presenter',
1✔
61
                                        '*Control',
62
                                        '*Component',
63
                                ])
64
                                ->mergeDefaults(),
1✔
65
                ])->castTo(ComponentAuthorizationConfig::class);
1✔
66
        }
67

68
        public function loadConfiguration(): void
69
        {
70
                $this->loadDefinitionsFromConfig($this->loadFromFile(__DIR__ . '/services.neon')['services']);
1✔
71
        }
1✔
72

73
        /**
74
         * @throws ReflectionException
75
         */
76
        public function beforeCompile(): void
77
        {
78
                $config = $this->getConfig();
1✔
79
                assert($config instanceof ComponentAuthorizationConfig);
80

81
                if ($config->cache) {
1✔
82
                        $this->registerStaticAttributeReader($config);
1✔
83
                }
84

85
                $this->registerRuleHandlers();
1✔
86
                $this->awareComponentAuthorizatorService();
1✔
87
        }
1✔
88

89
        /**
90
         * @throws ReflectionException
91
         */
92
        private function registerStaticAttributeReader(ComponentAuthorizationConfig $config): void
1✔
93
        {
94
                $builder = $this->getContainerBuilder();
1✔
95
                $classes = [];
1✔
96

97
                if (!$config->scanDirs && !$config->scanComposer) {
1✔
98
                        throw new RuntimeException(sprintf(
1✔
99
                                'Can\'t create a cached attribute reader because both options %s and %s are disabled.',
1✔
100
                                $this->prefix('scanDirs'),
1✔
101
                                $this->prefix('scanComposer')
1✔
102
                        ));
103
                }
104

105
                if ($config->scanDirs) {
1✔
106
                        if (!class_exists(RobotLoader::class)) {
1✔
107
                                throw new RuntimeException(sprintf(
×
108
                                        'RobotLoader is required to create cached attribute reader, install package "nette/robot-loader" or disable option %s: false.',
×
109
                                        $this->prefix('scanDirs')
×
110
                                ));
111
                        }
112

113
                        $tempDir = ($builder->parameters['tempDir'] ?? sys_get_temp_dir()) . '/cache/68publishers.smart-nette-components';
1✔
114
                        $loader = new RobotLoader();
1✔
115
                        $loader->acceptFiles = array_map(static fn (string $filter): string => $filter . '.php', $config->scanFilters);
1✔
116

117
                        $loader->addDirectory(...$config->scanDirs);
1✔
118
                        $loader->setTempDirectory($tempDir);
1✔
119
                        $loader->refresh();
1✔
120

121
                        $classes = array_keys($loader->getIndexedClasses());
1✔
122
                }
123

124
                if ($config->scanComposer) {
1✔
125
                        $classLoaderReflection = new ReflectionClass(ClassLoader::class);
1✔
126
                        $classFile = dirname((string) $classLoaderReflection->getFileName()) . '/autoload_classmap.php';
1✔
127

128
                        if (is_file($classFile)) {
1✔
129
                                $builder->addDependency($classFile);
1✔
130
                                $classes = array_merge($classes, array_keys((static fn (string $path) => require $path)($classFile)));
1✔
131
                        }
132
                }
133

134
                $classList = [];
1✔
135

136
                foreach (array_unique($classes) as $class) {
1✔
137
                        $class = (string) $class;
1✔
138
                        $matched = false;
1✔
139

140
                        foreach ($config->scanFilters as $scanFilter) {
1✔
141
                                if (fnmatch($scanFilter, $class)) {
1✔
142
                                        $matched = true;
1✔
143

144
                                        break;
1✔
145
                                }
146
                        }
147

148
                        if (!$matched || !class_exists($class)) {
1✔
149
                                continue;
1✔
150
                        }
151

152
                        $classReflection = new ReflectionClass($class);
1✔
153

154
                        if (Control::class === $classReflection->getName()
1✔
155
                                || Presenter::class === $classReflection->getName()
1✔
156
                                || !($classReflection->isSubclassOf(Control::class) || $classReflection->implementsInterface(IPresenter::class))
1✔
157
                        ) {
158
                                continue;
1✔
159
                        }
160

161
                        $classList[] = $classReflection->getName();
1✔
162
                }
163

164
                $map = AttributesMap::createFromClassList($classList, true);
1✔
165
                $reader = $builder->getDefinition($this->prefix('reader.inner'));
1✔
166
                assert($reader instanceof ServiceDefinition);
167

168
                $reader->setType(StaticAttributeReader::class);
1✔
169
                $reader->setFactory(StaticAttributeReader::class, [
1✔
170
                        'map' => new Statement(AttributesMap::class, [
1✔
171
                                'classHierarchy' => $map->classHierarchy,
1✔
172
                                'attributes' => array_map(
1✔
173
                                        static fn (array $attrs): array => array_map(
1✔
174
                                                static function (AttributeInterface $prototype): Statement {
1✔
175
                                                        assert($prototype instanceof AttributePrototype);
176

177
                                                        return new Statement($prototype->classname, $prototype->arguments);
1✔
178
                                                },
1✔
179
                                                $attrs
1✔
180
                                        ),
1✔
181
                                        $map->attributes
1✔
182
                                ),
183
                        ]),
184
                ]);
185

186
                /*
187
                 * array_map(
188
                                                static fn (AttributePrototype $prototype): Statement => new Statement($prototype->classname, $prototype->arguments),
189
                                                $attrs
190
                                        )
191
                 */
192
        }
1✔
193

194
        private function registerRuleHandlers(): void
195
        {
196
                $builder = $this->getContainerBuilder();
1✔
197
                $handlerRegistry = $builder->getDefinition($this->prefix('authorization.handler'));
1✔
198
                assert($handlerRegistry instanceof ServiceDefinition);
199

200
                $handlers = array_filter(
1✔
201
                        $builder->findByType(RuleHandlerInterface::class),
1✔
202
                        static fn (Definition $definition): bool => $definition !== $handlerRegistry
1✔
203
                );
1✔
204

205
                $handlerRegistry->setArgument('handlers', array_values($handlers));
1✔
206
        }
1✔
207

208
        private function awareComponentAuthorizatorService(): void
209
        {
210
                $definitions = array_filter(
1✔
211
                        $this->getContainerBuilder()->getDefinitions(),
1✔
212
                        static fn (Definition $def): bool =>
1✔
213
                                null !== $def->getType()
1✔
214
                                 && (
215
                                         is_a($def->getType(), ComponentAuthorizatorAwareInterface::class, true)
1✔
216
                                        || ($def instanceof FactoryDefinition && null !== $def->getResultType() && is_a($def->getResultType(), ComponentAuthorizatorAwareInterface::class, true))
1✔
217
                                 )
218
                );
1✔
219

220
                foreach ($definitions as $definition) {
1✔
221
                        if ($definition instanceof FactoryDefinition) {
1✔
222
                                $definition = $definition->getResultDefinition();
1✔
223
                        }
224

225
                        assert($definition instanceof ServiceDefinition);
226

227
                        $definition->addSetup('setComponentAuthorizator', [
1✔
228
                                new Reference($this->prefix('authorization.component_authorizator')),
1✔
229
                        ]);
230
                }
231
        }
1✔
232
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc