Passed
Push — 0.7.0 ( 2c5a8e...dc9ed8 )
by Alexander
10:13 queued 11s
created

ProviderRepository::shouldRecompile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 1
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 3
rs 10
1
<?php 
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Core;
24
25
use Exception;
26
use Syscodes\Filesystem\Filesystem;
27
use Syscodes\Contracts\Core\Application;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Syscodes\Core\Application. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
28
29
/**
30
 * @author Alexander Campo <[email protected]>
31
 */
32
class ProviderRepository
33
{
34
    /**
35
     * The application implementation.
36
     * 
37
     * @var \Syscodes\Contracts\Core\Application $app
38
     */
39
    protected $app;
40
41
    /**
42
     * The filesystem instance.
43
     * 
44
     * @var \Syscodes\Filesystem\Filesystem $files
45
     */
46
    protected $files;
47
48
    /**
49
     * The path to the manifest file.
50
     * 
51
     * @var string $manifestPath
52
     */
53
    protected $manifestPath;
54
    
55
    /**
56
     * Constructor. Create a new ProviderRepository class instance.
57
     * 
58
     * @param  \Syscodes\Contracts\Core\Application  $app
59
     * @param  \Syscodes\Filesystem\Filesystem  $files
60
     * @param  string  $manifestPath
61
     * 
62
     * @return void
63
     */
64
    public function __construct(Application $app, Filesystem $files, $manifestPath)
65
    {
66
        $this->app          = $app;
67
        $this->files        = $files;
68
        $this->manifestPath = $manifestPath;
69
    }
70
    
71
    /**
72
     * Register the application service providers.
73
     * 
74
     * @param  array  $providers
75
     * 
76
     * @return void
77
     */
78
    public function load(array $providers)
79
    {
80
        $manifest = $this->loadManifest();
81
        
82
        if ($this->shouldRecompile($manifest, $providers)) {
83
            $manifest = $this->compileManifest($providers);
84
        }
85
        
86
        foreach ($manifest['when'] as $provider => $events) {
87
            $this->registerLoadEvents($provider, $events);
88
        }
89
        
90
        foreach ($manifest['eager'] as $provider) {
91
            $this->app->register($provider);
92
        }
93
        
94
        $this->app->addDeferredServices($manifest['deferred']);
0 ignored issues
show
Bug introduced by
The method addDeferredServices() does not exist on Syscodes\Contracts\Core\Application. Since it exists in all sub-types, consider adding an abstract or default implementation to Syscodes\Contracts\Core\Application. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

94
        $this->app->/** @scrutinizer ignore-call */ 
95
                    addDeferredServices($manifest['deferred']);
Loading history...
95
    }
96
    
97
    /**
98
     * Load the service provider manifest JSON file.
99
     * 
100
     * @return array|null
101
     */
102
    public function loadManifest()
103
    {
104
        if ($this->files->exists($this->manifestPath)) {
105
            $manifest = $this->files->getRequire($this->manifestPath);
106
            
107
            if ($manifest) {
108
                return array_merge(['when' => []], $manifest);
109
            }
110
        }
111
    }
112
    
113
    /**
114
     * Determine if the manifest should be compiled.
115
     * 
116
     * @param  array  $manifest
117
     * @param  array  $providers
118
     * 
119
     * @return bool
120
     */
121
    public function shouldRecompile($manifest, $providers)
122
    {
123
        return is_null($manifest) || $manifest['providers'] != $providers;
124
    }
125
    
126
    /**
127
     * Register the load events for the given provider.
128
     * 
129
     * @param  string  $provider
130
     * @param  array  $events
131
     * 
132
     * @return void
133
     */
134
    protected function registerLoadEvents($provider, array $events)
135
    {
136
        if (count($events) < 1) {
137
            return;
138
        }
139
        
140
        $this->app->make('events')->listen($events, function () use ($provider) {
141
            $this->app->register($provider);
142
        });
143
    }
144
    
145
    /**
146
     * Compile the application service manifest file.
147
     * 
148
     * @param  array  $providers
149
     * 
150
     * @return array
151
     */
152
    protected function compileManifest($providers)
153
    {
154
        $manifest = $this->freshManifest($providers);
155
        
156
        foreach ($providers as $provider) {
157
            $instance = $this->createProvider($provider);
158
            
159
            if ($instance->isDeferred()) {
160
                foreach ($instance->provides() as $service) {
161
                    $manifest['deferred'][$service] = $provider;
162
                }
163
164
                $manifest['when'][$provider] = $instance->when();
165
            } else {
166
                $manifest['eager'][] = $provider;
167
            }
168
        }
169
        
170
        return $this->writeManifest($manifest);
171
    }
172
    
173
    /**
174
     * Create a fresh service manifest data structure.
175
     * 
176
     * @param  array  $providers
177
     * 
178
     * @return array
179
     */
180
    protected function freshManifest(array $providers)
181
    {
182
        return ['providers' => $providers, 'eager' => [], 'deferred' => []];
183
    }
184
    
185
    /**
186
     * Write the service manifest file to disk.
187
     * 
188
     * @param  array  $manifest
189
     * 
190
     * @return array
191
     * 
192
     * @throws \Exception
193
     */
194
    public function writeManifest($manifest)
195
    {
196
        if (! is_writable($dirname = dirname($this->manifestPath))) {
197
            throw new Exception("The {$dirname} directory must be present and writable.");
198
        }
199
        
200
        $this->files->replace(
201
            $this->manifestPath, "<?php\n\nreturn ".var_export($manifest, true).';'
202
        );
203
        
204
        return array_merge(['when' => []], $manifest);
205
    }
206
    
207
    /**
208
     * Create a new provider instance.
209
     * 
210
     * @param  string  $provider
211
     * 
212
     * @return \Syscodes\Support\ServiceProvider
213
     */
214
    public function createProvider($provider)
215
    {
216
        return new $provider($this->app);
217
    }
218
}