FixtureTrait::initFixtures()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace CorpSoft\Fixture\Traits;
4
5
use CorpSoft\Fixture\Exceptions\InvalidConfigException;
6
use CorpSoft\Fixture\Fixture;
7
8
/**
9
 * FixtureTrait provides functionality for loading, unloading and accessing fixtures for a test case.
10
 *
11
 * By using FixtureTrait, a test class will be able to specify which fixtures to load by overriding
12
 * the [[fixtures()]] method. It can then load and unload the fixtures using [[loadFixtures()]] and [[unloadFixtures()]].
13
 */
14
trait FixtureTrait
15
{
16
    /**
17
     * @var array the list of fixture objects available for the current test.
18
     * The array keys are the corresponding fixture class names.
19
     * The fixtures are listed in their dependency order. That is, fixture A is listed before B
20
     * if B depends on A.
21
     */
22
    private $_fixtures;
23
24
    /**
25
     * Declares the fixtures that are needed by the current test case.
26
     *
27
     * The return value of this method must be an array of fixture configurations. For example,
28
     *
29
     * ```php
30
     * [
31
     *     // anonymous fixture
32
     *     ArticleFixture::class,
33
     *     // "articles" fixture
34
     *     'articles' => ArticleFixture::class,
35
     * ]
36
     * ```
37
     *
38
     * @return array the fixtures needed by the current test case
39
     */
40
    public function fixtures(): array
41
    {
42
        return [];
43
    }
44
45
    /**
46
     * Loads the specified fixtures.
47
     * This method will call [[Fixture::load()]] for every fixture object.
48
     *
49
     * @param Fixture[] $fixtures the fixtures to be loaded. If this parameter is not specified,
50
     * the return value of [[getFixtures()]] will be used.
51
     *
52
     * @throws InvalidConfigException
53
     */
54
    public function loadFixtures($fixtures = null): void
55
    {
56
        if ($fixtures === null) {
57
            $fixtures = $this->getFixtures();
58
        }
59
60
        /* @var $fixture Fixture */
61
        foreach ($fixtures as $fixture) {
62
            $fixture->beforeLoad();
63
        }
64
65
        foreach ($fixtures as $fixture) {
66
            $fixture->load();
67
        }
68
69
        foreach (array_reverse($fixtures) as $fixture) {
70
            $fixture->afterLoad();
71
        }
72
    }
73
74
    /**
75
     * Unloads the specified fixtures.
76
     * This method will call [[Fixture::unload()]] for every fixture object.
77
     *
78
     * @param Fixture[] $fixtures the fixtures to be loaded. If this parameter is not specified,
79
     * the return value of [[getFixtures()]] will be used.
80
     *
81
     * @throws InvalidConfigException
82
     */
83
    public function unloadFixtures($fixtures = null): void
84
    {
85
        if ($fixtures === null) {
86
            $fixtures = $this->getFixtures();
87
        }
88
89
        /* @var $fixture Fixture */
90
        foreach ($fixtures as $fixture) {
91
            $fixture->beforeUnload();
92
        }
93
94
        $fixtures = array_reverse($fixtures);
95
96
        foreach ($fixtures as $fixture) {
97
            $fixture->unload();
98
        }
99
100
        foreach ($fixtures as $fixture) {
101
            $fixture->afterUnload();
102
        }
103
    }
104
105
    /**
106
     * Initialize the fixtures
107
     *
108
     * @throws InvalidConfigException
109
     */
110
    public function initFixtures(): void
111
    {
112
        $this->unloadFixtures();
113
        $this->loadFixtures();
114
    }
115
116
    /**
117
     * Returns the fixture objects as specified in [[globalFixtures()]] and [[fixtures()]].
118
     *
119
     * @throws InvalidConfigException
120
     *
121
     * @return Fixture[] the loaded fixtures for the current test case
122
     */
123
    public function getFixtures()
124
    {
125
        if ($this->_fixtures === null) {
126
            $this->_fixtures = $this->createFixtures($this->fixtures());
127
        }
128
129
        return $this->_fixtures;
130
    }
131
132
    /**
133
     * Returns the named fixture.
134
     *
135
     * @param string $name the fixture name
136
     *
137
     * @throws InvalidConfigException
138
     *
139
     * @return Fixture the fixture object, or null if the named fixture does not exist
140
     */
141
    public function getFixture($name): Fixture
142
    {
143
        if ($this->_fixtures === null) {
144
            $this->_fixtures = $this->createFixtures($this->fixtures());
145
        }
146
147
        $name = ltrim($name, '\\');
148
149
        return isset($this->_fixtures[$name]) ? $this->_fixtures[$name] : null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IssetNode ? $this->_fixtures[$name] : null could return the type null which is incompatible with the type-hinted return CorpSoft\Fixture\Fixture. Consider adding an additional type-check to rule them out.
Loading history...
150
    }
151
152
    /**
153
     * Creates the specified fixture instances.
154
     * All dependent fixtures will also be created.
155
     *
156
     * @param array $fixtures the fixtures to be created. You may provide fixture names or fixture configurations.
157
     * If this parameter is not provided, the fixtures specified in [[globalFixtures()]] and [[fixtures()]] will be created.
158
     *
159
     * @throws InvalidConfigException if fixtures are not properly configured or if a circular dependency among
160
     * the fixtures is detected
161
     *
162
     * @return Fixture[] the created fixture instances
163
     */
164
    protected function createFixtures(array $fixtures)
165
    {
166
        // normalize fixture configurations
167
        $config = [];  // configuration provided in test case
168
        $aliases = [];  // class name => alias or class name
169
170
        foreach ($fixtures as $name => $fixture) {
171
            if (!is_array($fixture)) {
172
                $class = ltrim($fixture, '\\');
173
                $fixtures[$name] = ['class' => $class];
174
                $aliases[$class] = is_int($name) ? $class : $name;
175
            } elseif (isset($fixture['class'])) {
176
                $class = ltrim($fixture['class'], '\\');
177
                $config[$class] = $fixture;
178
                $aliases[$class] = $name;
179
            } else {
180
                throw new InvalidConfigException("You must specify 'class' for the fixture '$name'.");
181
            }
182
        }
183
184
        // create fixture instances
185
        $instances = [];
186
        $stack = array_reverse($fixtures);
187
188
        while (($fixture = array_pop($stack)) !== null) {
189
            if ($fixture instanceof Fixture) {
190
                $class = get_class($fixture);
191
                $name = isset($aliases[$class]) ? $aliases[$class] : $class;
192
                unset($instances[$name]);  // unset so that the fixture is added to the last in the next line
193
                $instances[$name] = $fixture;
194
            } else {
195
                $class = ltrim($fixture['class'], '\\');
196
                $name = isset($aliases[$class]) ? $aliases[$class] : $class;
197
                if (!isset($instances[$name])) {
198
                    $instances[$name] = false;
199
200
                    $stack[] = $fixture = app()->make($fixture['class']);
201
                    foreach ($fixture->depends as $dep) {
202
                        // need to use the configuration provided in test case
203
                        $stack[] = isset($config[$dep]) ? $config[$dep] : ['class' => $dep];
204
                    }
205
                } elseif ($instances[$name] === false) {
206
                    throw new InvalidConfigException("A circular dependency is detected for fixture '$class'.");
207
                }
208
            }
209
        }
210
211
        return $instances;
212
    }
213
}
214