Completed
Push — master ( 314343...05f115 )
by Bart
9s
created

App::_setSchematicComponents()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 47
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 8
Bugs 1 Features 2
Metric Value
c 8
b 1
f 2
dl 0
loc 47
rs 9.0303
cc 2
eloc 28
nc 2
nop 0
1
<?php
2
3
namespace NerdsAndCompany\Schematic\Console;
4
5
use Craft\Craft;
6
use Craft\Logger;
7
use CConsoleApplication as Base;
8
use NerdsAndCompany\Schematic\Behaviors\Schematic;
9
use NerdsAndCompany\Schematic\Services as Service;
10
11
/**
12
 * Schematic Console App.
13
 *
14
 * Sync Craft Setups.
15
 *
16
 * @author    Nerds & Company
17
 * @copyright Copyright (c) 2015-2016, Nerds & Company
18
 * @license   MIT
19
 *
20
 * @link      http://www.nerds.company
21
 */
22
class App extends Base
23
{
24
    // Properties
25
    // =========================================================================
26
27
    /**
28
     * @var
29
     */
30
    public $componentAliases;
31
32
    /**
33
     * @var
34
     */
35
    private $_pendingEvents;
36
37
    /**
38
     * @var
39
     */
40
    private $_editionComponents;
41
42
    // Public Methods
43
    // =========================================================================
44
45
    /**
46
     * Initializes the console app by creating the command runner.
47
     */
48
    public function init()
49
    {
50
        // Set default timezone to UTC
51
        date_default_timezone_set('UTC');
52
53
        // Import all the built-in components
54
        foreach ($this->componentAliases as $alias) {
55
            Craft::import($alias);
56
        }
57
58
        // Attach our Craft app behavior.
59
        $this->attachBehavior('SchematicBehavior', new Schematic());
60
61
        // Attach our own custom Logger
62
        Craft::setLogger(new Logger());
63
64
        // Initialize Cache and LogRouter right away (order is important)
65
        $this->getComponent('cache');
66
        $this->getComponent('log');
67
68
        // So we can try to translate Yii framework strings
69
        $this->coreMessages->attachEventHandler('onMissingTranslation', ['Craft\LocalizationHelper', 'findMissingTranslation']);
70
71
        // Set our own custom runtime path.
72
        $this->setRuntimePath(Craft::app()->path->getRuntimePath());
73
74
        // No need for these.
75
        Craft::app()->log->removeRoute('WebLogRoute');
76
        Craft::app()->log->removeRoute('ProfileLogRoute');
77
78
        // Set the edition components
79
        $this->_setEditionComponents();
80
81
        // Install Craft if needed
82
        if (!$this->isInstalled()) {
83
            $this->_installCraft();
84
        }
85
86
        // Set the schematic components
87
        $this->_setSchematicComponents();
88
89
        // Call parent::init() before the plugin console command logic so the command runner gets initialized
90
        parent::init();
91
92
        // Load the plugins
93
        Craft::app()->plugins->loadPlugins();
94
95
        // Validate some basics on the database configuration file.
96
        Craft::app()->validateDbConfigFile();
97
98
        // Add commands
99
        Craft::app()->commandRunner->commands = [];
100
        Craft::app()->commandRunner->addCommands(__DIR__.'/../ConsoleCommands/');
101
    }
102
103
    /**
104
     * Returns the target application language.
105
     *
106
     * @return string
107
     */
108
    public function getLanguage()
109
    {
110
        return $this->asa('SchematicBehavior')->getLanguage();
111
    }
112
113
    /**
114
     * Sets the target application language.
115
     *
116
     * @param string $language
117
     */
118
    public function setLanguage($language)
119
    {
120
        $this->asa('SchematicBehavior')->setLanguage($language);
121
    }
122
123
    /**
124
     * Returns the system time zone.  Note that this method cannot be in {@link AppBehavior}, because Yii will check
125
     * {@link \CApplication::getTimeZone()} instead.
126
     *
127
     * @return string
128
     */
129
    public function getTimeZone()
130
    {
131
        return $this->asa('SchematicBehavior')->getTimezone();
132
    }
133
134
    /**
135
     * Attaches an event handler, or remembers it for later if the component has not been initialized yet.
136
     *
137
     * The event should be identified in a `serviceHandle.eventName` format. For example, if you want to add an event
138
     * handler for {@link EntriesService::onSaveEntry()}, you would do this:
139
     *
140
     * ```php
141
     * Craft::app()->on('entries.saveEntry', function(Event $event) {
142
     *     // ...
143
     * });
144
     * ```
145
     *
146
     * Note that the actual event name (`saveEntry`) does not need to include the “`on`”.
147
     *
148
     * By default, event handlers will not get attached if Craft is current in the middle of updating itself or a
149
     * plugin. If you want the event to fire even in that condition, pass `true` to the $evenDuringUpdates argument.
150
     *
151
     * @param string $event   The event to listen for.
152
     * @param mixed  $handler The event handler.
153
     */
154
    public function on($event, $handler)
155
    {
156
        list($componentId, $eventName) = explode('.', $event, 2);
157
158
        $component = $this->getComponent($componentId);
159
160
        // Normalize the event name
161
        if (strncmp($eventName, 'on', 2) !== 0) {
162
            $eventName = 'on'.ucfirst($eventName);
163
        }
164
165
        $component->$eventName = $handler;
166
    }
167
168
    /**
169
     * Returns whether we are executing in the context on a console app.
170
     *
171
     * @return bool
172
     */
173
    public function isConsole()
174
    {
175
        return true;
176
    }
177
178
    /**
179
     * Override getComponent() so we can attach any pending events if the component is getting initialized as well as
180
     * do some special logic around creating the `Craft::app()->db` application component.
181
     *
182
     * @param string $id
183
     * @param bool   $createIfNull
184
     *
185
     * @return mixed
186
     */
187
    public function getComponent($id, $createIfNull = true)
188
    {
189
        $component = parent::getComponent($id, false);
190
191
        if (!$component && $createIfNull) {
192
            if ($id === 'db') {
193
                $dbConnection = $this->asa('SchematicBehavior')->createDbConnection();
194
                $this->setComponent('db', $dbConnection);
195
            }
196
197
            $component = parent::getComponent($id, true);
198
            $this->_attachEventListeners($id);
199
        }
200
201
        return $component;
202
    }
203
204
    /**
205
     * Sets the application components.
206
     *
207
     * @param      $components
208
     * @param bool $merge
209
     */
210
    public function setComponents($components, $merge = true)
211
    {
212
        if (isset($components['editionComponents'])) {
213
            $this->_editionComponents = $components['editionComponents'];
214
            unset($components['editionComponents']);
215
        }
216
217
        parent::setComponents($components, $merge);
218
    }
219
220
    /**
221
     * @todo Remove for Craft 3.
222
     *
223
     * @param int    $code    The level of the error raised.
224
     * @param string $message The error message.
225
     * @param string $file    The filename that the error was raised in.
226
     * @param int    $line    The line number the error was raised at.
227
     */
228
    public function handleError($code, $message, $file, $line)
229
    {
230
        // PHP 7 turned some E_STRICT messages to E_WARNINGs. Code 2 is for all warnings
231
        // and since there are no messages specific codes we have to parse the string for what
232
        // we're looking for. Lame, but it works since all PHP error messages are always in English.
233
        // https://stackoverflow.com/questions/11556375/is-there-a-way-to-localize-phps-error-output
234
        if (version_compare(PHP_VERSION, '7', '>=') && $code === 2 && strpos($message, 'should be compatible with') !== false) {
235
            return;
236
        }
237
238
        parent::handleError($code, $message, $file, $line);
239
    }
240
241
    // Protected Methods
242
    // =========================================================================
243
244
    /**
245
     * @return ConsoleCommandRunner
246
     */
247
    protected function createCommandRunner()
248
    {
249
        return new CommandRunner();
250
    }
251
252
    // Private Methods
253
    // =========================================================================
254
255
    /**
256
     * Attaches any pending event listeners to the newly-initialized component.
257
     *
258
     * @param string $componentId
259
     */
260
    private function _attachEventListeners($componentId)
261
    {
262
        if (isset($this->_pendingEvents[$componentId])) {
263
            $component = $this->getComponent($componentId, false);
264
265
            if ($component) {
266
                foreach ($this->_pendingEvents[$componentId] as $eventName => $handlers) {
267
                    foreach ($handlers as $handler) {
268
                        $component->$eventName = $handler;
269
                    }
270
                }
271
            }
272
        }
273
    }
274
275
    /**
276
     * Sets the edition components.
277
     */
278
    private function _setEditionComponents()
279
    {
280
        // Set the appropriate edition components
281
        if (isset($this->_editionComponents)) {
282
            foreach ($this->_editionComponents as $edition => $editionComponents) {
283
                if ($this->getEdition() >= $edition) {
284
                    $this->setComponents($editionComponents);
285
                }
286
            }
287
288
            unset($this->_editionComponents);
289
        }
290
    }
291
292
    /**
293
     * Sets the schematic components.
294
     */
295
    private function _setSchematicComponents()
296
    {
297
        $components = [
298
            'schematic' => [
299
                'class' => Service\Schematic::class,
300
            ],
301
            'schematic_locales' => [
302
                'class' => Service\Locales::class,
303
            ],
304
            'schematic_assetSources' => [
305
                'class' => Service\AssetSources::class,
306
            ],
307
            'schematic_fields' => [
308
                'class' => Service\Fields::class,
309
            ],
310
            'schematic_globalSets' => [
311
                'class' => Service\GlobalSets::class,
312
            ],
313
            'schematic_plugins' => [
314
                'class' => Service\Plugins::class,
315
            ],
316
            'schematic_sections' => [
317
                'class' => Service\Sections::class,
318
            ],
319
            'schematic_userGroups' => [
320
                'class' => Service\UserGroups::class,
321
            ],
322
            'schematic_users' => [
323
                'class' => Service\Users::class,
324
            ],
325
            'schematic_categoryGroups' => [
326
                'class' => Service\CategoryGroups::class,
327
            ],
328
            'schematic_tagGroups' => [
329
                'class' => Service\TagGroups::class,
330
            ],
331
        ];
332
333
        // Element index settings are supported from Craft 2.5
334
        if (version_compare(CRAFT_VERSION, '2.5', '>=')) {
335
            $components['schematic_elementIndexSettings'] = [
336
                'class' => Service\ElementIndexSettings::class,
337
            ];
338
        }
339
340
        $this->setComponents($components);
341
    }
342
343
    /**
344
     * Install Craft.
345
     */
346
    private function _installCraft()
347
    {
348
        $options = [
349
            'username'  => getenv('CRAFT_USERNAME'),
350
            'email'     => getenv('CRAFT_EMAIL'),
351
            'password'  => getenv('CRAFT_PASSWORD'),
352
            'siteName'  => getenv('CRAFT_SITENAME'),
353
            'siteUrl'   => getenv('CRAFT_SITEURL'),
354
            'locale'    => getenv('CRAFT_LOCALE'),
355
        ];
356
357
        Craft::app()->install->run($options);
358
    }
359
}
360