Test Failed
Push — master ( 35f8f8...e41b5a )
by Alxarafe
05:14
created

Models::loadFirstClassIfNeeded()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Alxarafe. Development of PHP applications in a flash!
4
 * Copyright (C) 2018-2019 Alxarafe <[email protected]>
5
 */
6
7
namespace Alxarafe\Core\PreProcessors;
8
9
use Alxarafe\Core\Helpers\Utils\FileSystemUtils;
10
use Alxarafe\Core\Models\TableModel;
11
use Alxarafe\Core\Providers\Database;
12
use Alxarafe\Core\Providers\FlashMessages;
13
use Alxarafe\Core\Providers\Translator;
14
use ReflectionClass;
15
use Symfony\Component\Finder\Finder;
16
17
/**
18
 * This class pre-process Models to generate some needed information.
19
 *
20
 * @package Alxarafe\Core\PreProcessors
21
 */
22
class Models
23
{
24
    /**
25
     * Array that contains the paths to search.
26
     *
27
     * @var array
28
     */
29
    protected $searchDir;
30
31
    /**
32
     * A list of class dependencies
33
     *
34
     * @var array
35
     */
36
    protected $classDependencies;
37
38
    /**
39
     * Models constructor.
40
     *
41
     * @param array $dirs
42
     */
43 1
    public function __construct(array $dirs)
44
    {
45 1
        $this->searchDir = $dirs;
46 1
        $this->instantiateModels();
47 1
    }
48
49
    /**
50
     * Instantiate all available models
51
     *
52
     * TODO: This must be executed only when update/upgrade the core. At this moment is forced if DEBUG is enabled.
53
     *
54
     * @return void
55
     */
56 1
    private function instantiateModels(): void
57
    {
58
        // Start DB transaction
59 1
        Database::getInstance()->getDbEngine()->beginTransaction();
60
61 1
        $this->classDependencies = [];
62 1
        $list = [];
63 1
        foreach ($this->searchDir as $namespace => $baseDir) {
64 1
            $this->fillList($namespace, $baseDir, $list);
65
            // Instanciate dependencies and after the main class
66 1
            foreach ($list as $class) {
67 1
                $this->loadClassDependencies($class);
68
            }
69
        }
70
71 1
        foreach ($list as $class) {
72 1
            $this->loadClassIfNeeded($class);
73
        }
74
75 1
        foreach ($this->classDependencies as $className) {
76 1
            $this->addTableData($className);
77
        }
78 1
        foreach ($this->classDependencies as $className) {
79 1
            new $className(true);
80
        }
81
82
        // End DB transaction
83 1
        if (!Database::getInstance()->getDbEngine()->commit()) {
84
            FlashMessages::getInstance()::setError(Translator::getInstance()->trans('reinstanciated-model-class-error'));
85
        }
86 1
    }
87
88
    /**
89
     * Fill list of classes.
90
     *
91
     * @param string   $namespace
92
     * @param string   $baseDir
93
     * @param string[] $list
94
     */
95 1
    private function fillList(string $namespace, string $baseDir, array &$list): void
96
    {
97 1
        $dir = $baseDir . DIRECTORY_SEPARATOR . 'Models';
98 1
        FileSystemUtils::mkdir($dir, 0777, true);
99 1
        $models = Finder::create()
100 1
            ->files()
101 1
            ->name('*.php')
102 1
            ->in($dir);
103 1
        foreach ($models->getIterator() as $modelFile) {
104 1
            $class = str_replace([$dir, '/', '\\', '.php'], '', $modelFile);
105 1
            if ($namespace === 'Alxarafe') {
106
                $namespace .= '\\Core';
107
            }
108 1
            $list[] = $namespace . '\\Models\\' . $class;
109
        }
110 1
    }
111
112
    /**
113
     * Load class dependencies before load direct class.
114
     *
115
     * @param string $class
116
     */
117 1
    private function loadClassDependencies(string $class): void
118
    {
119 1
        if (method_exists($class, 'getDependencies')) {
120 1
            $deps = (new $class())->getDependencies();
121 1
            foreach ($deps as $dep) {
122 1
                $this->loadFirstClassIfNeeded($dep);
123
            }
124 1
            $this->loadClassIfNeeded((string) $class);
125
        }
126 1
    }
127
128
    /**
129
     * Load class only if not yet loaded.
130
     *
131
     * @param string $class
132
     */
133 1
    private function loadFirstClassIfNeeded(string $className): void
134
    {
135 1
        if (!in_array($className, $this->classDependencies, true)) {
136 1
            array_unshift($this->classDependencies, $className);
137
        }
138 1
    }
139
140
    /**
141
     * Load class only if not yet loaded.
142
     *
143
     * @param string $class
144
     */
145 1
    private function loadClassIfNeeded(string $className): void
146
    {
147 1
        if (!in_array($className, $this->classDependencies, true)) {
148 1
            $this->classDependencies[] = $className;
149
        }
150 1
    }
151
152
    /**
153
     * Adds Model needed data to TableModel.
154
     *
155
     * @param $className
156
     */
157 1
    private function addTableData($className)
158
    {
159 1
        $class = new $className();
160 1
        if ($class->modelName !== 'TableModel') {
161 1
            $tableModel = new TableModel();
162 1
            if (!$tableModel->load($class->tableName)) {
163 1
                $tableModel->tablename = $class->tableName;
164 1
                $tableModel->model = $class->modelName;
165
                try {
166 1
                    $tableModel->namespace = (new ReflectionClass($class))->getName();
167
                } catch (\ReflectionException $e) {
168
                    $tableModel->namespace = static::class;
169
                }
170 1
                $tableModel->save();
171
            }
172
        }
173 1
    }
174
}
175