Issues (25)

src/Providers/ApiResourceServiceProvider.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace W2w\Laravel\Apie\Providers;
4
5
use Illuminate\Contracts\Events\Dispatcher;
6
use Illuminate\Support\ServiceProvider;
7
use Madewithlove\IlluminatePsrCacheBridge\Laravel\CacheItemPool;
0 ignored issues
show
The type Madewithlove\IlluminateP...e\Laravel\CacheItemPool was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
9
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
10
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
11
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
12
use Symfony\Component\Serializer\Serializer;
13
use Symfony\Component\Serializer\SerializerInterface;
14
use W2w\Laravel\Apie\Plugins\Illuminate\DataLayers\StatusFromDatabaseRetriever;
15
use W2w\Laravel\Apie\Plugins\Illuminate6Cache\Illuminate6CachePlugin;
16
use W2w\Laravel\Apie\Plugins\Illuminate\IlluminatePlugin;
17
use W2w\Laravel\Apie\Plugins\IlluminateTranslation\DataLayers\TranslationRetriever;
18
use W2w\Laravel\Apie\Plugins\IlluminateTranslation\IlluminateTranslationPlugin;
19
use W2w\Laravel\Apie\Plugins\PsrCacheBridge\PsrCacheBridgePlugin;
20
use W2w\Laravel\Apie\Services\ApieContext;
21
use W2w\Laravel\Apie\Services\ApieExceptionToResponse;
22
use W2w\Laravel\Apie\Services\FileStorageDataLayerContainer;
23
use W2w\Lib\Apie\Apie;
24
use W2w\Lib\Apie\Core\ApiResourceFacade;
25
use W2w\Lib\Apie\Core\ApiResourcePersister;
26
use W2w\Lib\Apie\Core\ApiResourceRetriever;
27
use W2w\Lib\Apie\Core\ClassResourceConverter;
28
use W2w\Lib\Apie\Core\Resources\ApiResources;
29
use W2w\Lib\Apie\Core\Resources\ApiResourcesInterface;
30
use W2w\Lib\Apie\DefaultApie;
31
use W2w\Lib\Apie\Exceptions\InvalidClassTypeException;
32
use W2w\Lib\Apie\Interfaces\ResourceSerializerInterface;
33
use W2w\Lib\Apie\OpenApiSchema\OpenApiSpecGenerator;
34
use W2w\Lib\Apie\Plugins\ApplicationInfo\DataLayers\ApplicationInfoRetriever;
35
use W2w\Lib\Apie\Plugins\Core\Normalizers\ApieObjectNormalizer;
36
use W2w\Lib\Apie\Plugins\Core\Normalizers\ContextualNormalizer;
37
use W2w\Lib\Apie\Plugins\Core\Serializers\SymfonySerializerAdapter;
38
use W2w\Lib\Apie\Plugins\FakeAnnotations\FakeAnnotationsPlugin;
39
use W2w\Lib\Apie\Plugins\FileStorage\DataLayers\FileStorageDataLayer;
40
use W2w\Lib\Apie\Plugins\Mock\MockPlugin;
41
use W2w\Lib\Apie\Plugins\StatusCheck\DataLayers\StatusCheckRetriever;
42
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccessInterface;
43
44
/**
45
 * Install apie classes to Laravel.
46
 */
47
class ApiResourceServiceProvider extends ServiceProvider
48
{
49
    /**
50
     * Perform post-registration booting of services.
51
     *
52
     * @return void
53
     */
54
    public function boot()
55
    {
56
        if (function_exists('config_path')) {
57
            $this->publishes(
58
                [
59
                    __DIR__ . '/../../config/apie.php' => config_path('apie.php'),
60
                ]
61
            );
62
        }
63
        if (function_exists('resource_path')) {
64
            $this->publishes(
65
                [
66
                    __DIR__ . '/../../resources/lang' => resource_path('lang/vendor/apie'),
67
                ]
68
            );
69
        }
70
71
        $this->loadTranslationsFrom(__DIR__ . '/../../resources/lang', 'apie');
72
73
        $this->loadMigrationsFrom(__DIR__ . '/../../migrations');
74
    }
75
76
    /**
77
     * Register any application services.
78
     */
79
    public function register()
80
    {
81
        $this->bindApieContextServices();
82
        $this->bindApieServices();
83
84
        if (strpos($this->app->version(), 'Lumen') === false) {
85
            $this->app->register(ApieLaravelServiceProvider::class);
86
        } else {
87
            $this->app->register(ApieLumenServiceProvider::class);
88
        }
89
90
        $this->addStatusResourceServices();
91
    }
92
93
    /**
94
     * Bind all Apie related services that are on application level. They must be singletons and are not checking
95
     * at the current active Apie instance.
96
     */
97
    private function bindApieContextServices()
98
    {
99
        /**
100
         * apie.config: main config
101
         */
102
        $this->app->singleton('apie.config', function () {
103
            $config = $this->app->get('config');
104
            $res = ApieConfigResolver::resolveConfig($config->get('apie') ?? []);
105
            $config->set('apie', $res);
106
            if (empty($res['use_deprecated_apie_object_normalizer'])) {
107
                ContextualNormalizer::disableNormalizer(ApieObjectNormalizer::class);
108
                ContextualNormalizer::disableDenormalizer(ApieObjectNormalizer::class);
109
            } else {
110
                @trigger_error(
111
                    'You are still using the deprecated apie object normalizer, enable it with use_deprecated_apie_object_normalizer = false in the config to get the 4.0 normalizer active',
112
                    E_USER_DEPRECATED
113
                );
114
            }
115
            return $res;
116
        });
117
118
        /**
119
         * apie.plugins: array of Apie plugins of the main apie instance.
120
         */
121
        $this->app->singleton('apie.plugins', function () {
122
            return $this->getPlugins();
123
        });
124
125
        /**
126
         * ApieContext::class: get all Apie instances and see which is the current Apie instance.
127
         */
128
        $this->app->singleton(ApieContext::class, function () {
129
            $plugins = $this->app->get('apie.plugins');
130
            $debug = (bool) config('app.debug');
131
            $config = $this->app->get('apie.config');
132
            $cacheFolder = storage_path('app/apie-cache');
133
            $apie = DefaultApie::createDefaultApie($debug, $plugins, $cacheFolder, false);
134
            return new ApieContext($this->app, $apie, $config, $config['contexts']);
135
        });
136
137
        /**
138
         * FileStorageDataLayerContainer::class: creates FileStorageDataLayer for the right Apie instance.
139
         */
140
        $this->app->singleton(FileStorageDataLayerContainer::class, function () {
141
            return new FileStorageDataLayerContainer(
142
                storage_path('app/api-file-storage'),
143
                $this->app->get(ApieContext::class)
144
            );
145
        });
146
    }
147
148
    private function getPlugins(): array
149
    {
150
        $plugins = [];
151
        $config = $this->app->get('apie.config');
152
        if (!empty($config['mock'])) {
153
            $plugins[] = new MockPlugin($config['mock-skipped-resources']);
154
        }
155
        foreach ($this->app->tagged(Apie::class) as $plugin) {
156
            $plugins[] = $plugin;
157
        }
158
        foreach ($config['plugins'] as $plugin) {
159
            $plugins[] = $this->app->make($plugin);
160
        }
161
        if (!empty($config['resource-config'])) {
162
            $plugins[] = new FakeAnnotationsPlugin($config['resource-config']);
163
        }
164
        if (!empty($config['translations'])) {
165
            $plugins[] = new IlluminateTranslationPlugin(
166
                $config['translations'],
167
                $this->app->make('translator'),
168
                $this->app
169
            );
170
        }
171
        if (!empty($config['caching'])) {
172
            if ($this->app->bound('cache.psr6')) {
173
                $plugins[] = new Illuminate6CachePlugin($this->app);
174
            } elseif (class_exists(CacheItemPool::class)) {
175
                $plugins[] = new PsrCacheBridgePlugin($this->app);
176
            }
177
        }
178
        $plugins[] = new IlluminatePlugin($this->app, $config);
179
        return $plugins;
180
    }
181
182
    private function bindApieServices()
183
    {
184
        /**
185
         * ApiResourcesInterface::class: get all resources of the current Apie instance.
186
         */
187
        $this->app->bind(ApiResourcesInterface::class, function () {
188
            return new ApiResources($this->app->get(Apie::class)->getResources());
189
        });
190
191
        /**
192
         * ApiResourcePersister: call the correct data layer persist functionality.
193
         */
194
        $this->app->bind(ApiResourcePersister::class, function () {
195
            return new ApiResourcePersister($this->app->get(Apie::class)->getApiResourceMetadataFactory());
196
        });
197
198
        /**
199
         * ApiResourcePersister: call the correct data layer retrieve functionality.
200
         */
201
        $this->app->bind(ApiResourceRetriever::class, function () {
202
            return new ApiResourceRetriever($this->app->get(Apie::class)->getApiResourceMetadataFactory());
203
        });
204
205
        /**
206
         * Apie::class: current Apie instance.
207
         */
208
        $this->app->bind(Apie::class, function () {
209
            /** @var ApieContext $context */
210
            $context = $this->app->get(ApieContext::class)->getActiveContext();
211
            return $context->getApie();
212
        });
213
214
        /**
215
         * IlluminatePlugin::class: current IlluminatePlugin instance
216
         */
217
        $this->app->bind(IlluminatePlugin::class, function () {
218
            /** @var Apie $apie */
219
            $apie = $this->app->get(Apie::class);
220
            return $apie->getPlugin(IlluminatePlugin::class);
221
        });
222
223
        /**
224
         * ApplicationInfoRetriever::class: get retriever for Application Info of current apie instance.
225
         */
226
        $this->app->bind(ApplicationInfoRetriever::class, function () {
227
            /** @var IlluminatePlugin $laravelPlugin */
228
            $laravelPlugin = $this->app->get(IlluminatePlugin::class);
229
            $config = $laravelPlugin->getLaravelConfig();
230
            return new ApplicationInfoRetriever(
231
                config('app.name'),
232
                config('app.env'),
233
                $config['metadata']['hash'],
234
                config('app.debug')
235
            );
236
        });
237
238
        /**
239
         * FileStorageDataLayer::class: get file storage data layer for current apie instance.
240
         */
241
        $this->app->bind(FileStorageDataLayer::class, function () {
242
            return $this->app->get(FileStorageDataLayerContainer::class)->getCurrentFileStorageDataLayer();
243
        });
244
245
        $this->app->singleton(TranslationRetriever::class);
246
247
        /**
248
         * ApieExceptionToResponse::class: converts exception to an Apie response.
249
         */
250
        $this->app->bind(ApieExceptionToResponse::class, function () {
251
            /** @var IlluminatePlugin $laravelPlugin */
252
            $laravelPlugin = $this->app->get(IlluminatePlugin::class);
253
            $config = $laravelPlugin->getLaravelConfig();
254
            $mapping = $config['exception-mapping'];
255
            return new ApieExceptionToResponse($this->app->make(HttpFoundationFactory::class), $mapping);
256
        });
257
258
        /**
259
         * Serializer::class: gets the Symfony serializer.
260
         */
261
        $this->app->bind(Serializer::class, function () {
262
            $serializer = $this->app->get(ResourceSerializerInterface::class);
263
            if (! ($serializer instanceof SymfonySerializerAdapter)) {
264
                throw new InvalidClassTypeException('resource serializer', SymfonySerializerAdapter::class);
265
            }
266
            return $serializer->getSerializer();
267
        });
268
        $this->app->bind(SerializerInterface::class, Serializer::class);
269
        $this->app->bind(NormalizerInterface::class, Serializer::class);
270
        $this->app->bind(DenormalizerInterface::class, Serializer::class);
271
272
        $todo = [
273
            [ApiResourceFacade::class, 'getApiResourceFacade'],
274
            [ClassResourceConverter::class, 'getClassResourceConverter'],
275
            [OpenApiSpecGenerator::class, 'getOpenApiSpecGenerator'],
276
            [ResourceSerializerInterface::class, 'getResourceSerializer'],
277
            [NameConverterInterface::class, 'getPropertyConverter'],
278
            [ObjectAccessInterface::class, 'getObjectAccess']
279
        ];
280
        while ($item = array_pop($todo)) {
281
            $this->registerApieService($item[0], $item[1]);
282
        }
283
    }
284
285
    private function registerApieService(string $serviceName, string $methodName)
286
    {
287
        $this->app->bind($serviceName, function () use ($methodName) {
288
            return $this->app->get(Apie::class)->$methodName();
289
        });
290
    }
291
292
    private function addStatusResourceServices()
293
    {
294
        $this->app->singleton(StatusFromDatabaseRetriever::class, function () {
295
            return new StatusFromDatabaseRetriever(config('app.debug'));
296
        });
297
        $this->app->tag([StatusFromDatabaseRetriever::class], ['status-check']);
298
        $this->app->singleton(StatusCheckRetriever::class, function () {
299
            return new StatusCheckRetriever($this->app->tagged('status-check'));
300
        });
301
    }
302
}
303