Test Failed
Push — master ( 51e108...b1718b )
by Alxarafe
04:22
created

Models::addClassFirst()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.2559

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
ccs 3
cts 5
cp 0.6
crap 2.2559
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->addClassDependencies($class);
68
            }
69
        }
70
71 1
        foreach ($list as $class) {
72 1
            $this->addClass($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 $className
116
     */
117 1
    private function addClassDependencies(string $className): void
118
    {
119 1
        if (method_exists($className, 'getDependencies')) {
120 1
            $deps = (new $className())->getDependencies();
121 1
            foreach ($deps as $dep) {
122 1
                $this->addClassFirst((string) $dep);
123
            }
124 1
            $this->addClass((string) $className);
125
        }
126 1
    }
127
128
    /**
129
     * Load class only if not yet loaded.
130
     *
131
     * @param string $className
132
     */
133 1
    private function addClassFirst(string $className): void
134
    {
135 1
        if (in_array($className, $this->classDependencies, true)) {
136
            $whereIs = array_search($className, $this->classDependencies, true);
137
            unset($this->classDependencies[$whereIs]);
138
        }
139 1
        array_unshift($this->classDependencies, $className);
140 1
    }
141
142
    /**
143
     * Load class only if not yet loaded.
144
     *
145
     * @param string $className
146
     */
147 1
    private function addClass(string $className): void
148
    {
149 1
        if (!in_array($className, $this->classDependencies, true)) {
150 1
            $this->classDependencies[] = $className;
151
        }
152 1
    }
153
154
    /**
155
     * Adds Model needed data to TableModel.
156
     *
157
     * @param $className
158
     */
159 1
    private function addTableData($className)
160
    {
161 1
        $class = new $className();
162 1
        if ($class->modelName !== 'TableModel') {
163 1
            $tableModel = new TableModel();
164 1
            if (!$tableModel->load($class->tableName)) {
165 1
                $tableModel->tablename = $class->tableName;
166 1
                $tableModel->model = $class->modelName;
167
                try {
168 1
                    $tableModel->namespace = (new ReflectionClass($class))->getName();
169
                } catch (\ReflectionException $e) {
170
                    $tableModel->namespace = static::class;
171
                }
172 1
                $tableModel->save();
173
            }
174
        }
175 1
    }
176
}
177