Completed
Push — wip/steps ( f18ed0...d4cd55 )
by Romain
02:48
created

FormObjectFactory::all()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * 2017 Romain CANON <[email protected]>
4
 *
5
 * This file is part of the TYPO3 FormZ project.
6
 * It is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License, either
8
 * version 3 of the License, or any later version.
9
 *
10
 * For the full copyright and license information, see:
11
 * http://www.gnu.org/licenses/gpl-3.0.html
12
 */
13
14
namespace Romm\Formz\Form\FormObject;
15
16
use Romm\ConfigurationObject\ConfigurationObjectInterface;
17
use Romm\Formz\Configuration\Configuration;
18
use Romm\Formz\Configuration\ConfigurationFactory;
19
use Romm\Formz\Core\Core;
20
use Romm\Formz\Exceptions\ClassNotFoundException;
21
use Romm\Formz\Exceptions\DuplicateEntryException;
22
use Romm\Formz\Exceptions\EntryNotFoundException;
23
use Romm\Formz\Exceptions\InvalidArgumentTypeException;
24
use Romm\Formz\Exceptions\InvalidArgumentValueException;
25
use Romm\Formz\Form\FormInterface;
26
use Romm\Formz\Form\FormObject\Builder\DefaultFormObjectBuilder;
27
use Romm\Formz\Form\FormObject\Builder\FormObjectBuilderInterface;
28
use Romm\Formz\Form\FormObject\Service\FormObjectSteps;
29
use Romm\Formz\Service\CacheService;
30
use Romm\Formz\Service\ContextService;
31
use Romm\Formz\Service\StringService;
32
use Romm\Formz\Service\Traits\ExtendedSelfInstantiateTrait;
33
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
34
use TYPO3\CMS\Core\SingletonInterface;
35
36
/**
37
 * Factory class that will manage form object instances.
38
 *
39
 * You can fetch a new instance by calling one of the following methods:
40
 * `getInstanceFromFormInstance()` or `getInstanceFromClassName()`
41
 *
42
 * @see \Romm\Formz\Form\FormObject\FormObject
43
 */
44
class FormObjectFactory implements SingletonInterface
45
{
46
    use ExtendedSelfInstantiateTrait;
47
48
    /**
49
     * @var FormObject[]
50
     */
51
    protected $instances = [];
52
53
    /**
54
     * @var FormObjectStatic[]
55
     */
56
    protected $static = [];
57
58
    /**
59
     * @var FormObjectProxy[]
60
     */
61
    protected $proxy = [];
62
63
    /**
64
     * @var FormObjectSteps[]
65
     */
66
    protected $stepService = [];
67
68
    /**
69
     * Returns the form object for the given form instance. The form instance
70
     * must have been defined first in this factory, or an exception is thrown.
71
     *
72
     * @param FormInterface $form
73
     * @return FormObject
74
     * @throws EntryNotFoundException
75
     */
76
    public function getInstanceWithFormInstance(FormInterface $form)
77
    {
78
        if (false === $this->formInstanceWasRegistered($form)) {
79
            throw EntryNotFoundException::formObjectInstanceNotFound($form);
80
        }
81
82
        return $this->instances[spl_object_hash($form)];
83
    }
84
85
    /**
86
     * Checks that the given form instance was registered in this factory.
87
     *
88
     * @param FormInterface $form
89
     * @return bool
90
     */
91
    public function formInstanceWasRegistered(FormInterface $form)
92
    {
93
        return isset($this->instances[spl_object_hash($form)]);
94
    }
95
96
    /**
97
     * Registers a new form instance.
98
     *
99
     * @param FormInterface $form
100
     * @param string        $name
101
     * @throws DuplicateEntryException
102
     * @throws InvalidArgumentValueException
103
     */
104
    public function registerFormInstance(FormInterface $form, $name)
105
    {
106
        if (empty($name)) {
107
            throw InvalidArgumentValueException::formNameEmpty($form);
108
        }
109
110
        if ($this->formInstanceWasRegistered($form)) {
111
            throw DuplicateEntryException::formObjectInstanceAlreadyRegistered($form, $name);
112
        }
113
114
        $hash = spl_object_hash($form);
115
        $this->instances[$hash] = $this->getInstanceWithClassName(get_class($form), $name);
116
        $this->instances[$hash]->setForm($form);
117
    }
118
119
    /**
120
     * A shortcut function to register the given form instance (if it was not
121
     * already registered) and return the form object.
122
     *
123
     * @param FormInterface $form
124
     * @param string        $name
125
     * @return FormObject
126
     */
127
    public function registerAndGetFormInstance(FormInterface $form, $name)
128
    {
129
        if (false === $this->formInstanceWasRegistered($form)) {
130
            $this->registerFormInstance($form, $name);
131
        }
132
133
        return $this->getInstanceWithFormInstance($form);
134
    }
135
136
    /**
137
     * Will create an instance of `FormObject` based on a class that implements
138
     * the interface `FormInterface`.
139
     *
140
     * @param string $className
141
     * @param string $name
142
     * @return FormObject
143
     */
144
    public function getInstanceWithClassName($className, $name)
145
    {
146
        /** @var FormObject $formObject */
147
        $formObject = Core::instantiate(FormObject::class, $name, $this->getStaticInstance($className));
148
149
        return $formObject;
150
    }
151
152
    /**
153
     * Returns the proxy object for the given form object and form instance.
154
     *
155
     * Please use with caution, as this is a very low level function!
156
     *
157
     * @param FormInterface $form
158
     * @return FormObjectProxy
159
     */
160
    public function getProxy(FormInterface $form)
161
    {
162
        $hash = spl_object_hash($form);
163
164
        if (false === isset($this->proxy[$hash])) {
165
            $this->proxy[$hash] = $this->getNewProxyInstance($form);
166
        }
167
168
        return $this->proxy[$hash];
169
    }
170
171
    /**
172
     * @todo
173
     *
174
     * @param FormObject $formObject
175
     * @return FormObjectSteps
176
     */
177
    public function getStepService(FormObject $formObject)
178
    {
179
        $hash = $formObject->getObjectHash();
180
181
        if (false === isset($this->stepService[$hash])) {
182
            $this->stepService[$hash] = Core::instantiate(FormObjectSteps::class, $formObject);
183
        }
184
185
        return $this->stepService[$hash];
186
    }
187
188
    /**
189
     * @return FormObject[]
190
     */
191
    public function all()
192
    {
193
        return $this->instances;
194
    }
195
196
    /**
197
     * @param string $className
198
     * @return FormObjectStatic
199
     * @throws ClassNotFoundException
200
     * @throws InvalidArgumentTypeException
201
     */
202
    protected function getStaticInstance($className)
203
    {
204
        if (false === class_exists($className)) {
205
            throw ClassNotFoundException::wrongFormClassName($className);
206
        }
207
208
        if (false === in_array(FormInterface::class, class_implements($className))) {
209
            throw InvalidArgumentTypeException::wrongFormType($className);
210
        }
211
212
        $cacheIdentifier = $this->getCacheIdentifier($className);
213
214
        if (false === isset($this->static[$cacheIdentifier])) {
215
            $cacheInstance = $this->getCacheInstance();
216
217
            if ($cacheInstance->has($cacheIdentifier)) {
218
                $static = $cacheInstance->get($cacheIdentifier);
219
            } else {
220
                $static = $this->buildStaticInstance($className);
221
                $static->getObjectHash();
222
223
                if (false === $static->getDefinitionValidationResult()->hasErrors()) {
224
                    $cacheInstance->set($cacheIdentifier, $static);
225
                }
226
            }
227
228
            $static->getDefinition()->attachParent($this->getRootConfiguration());
229
230
            // The definition is frozen: no modification will be allowed after that.
231
            $static->getDefinition()->getState()->markAsFrozen();
232
233
            $this->static[$cacheIdentifier] = $static;
234
        }
235
236
        return $this->static[$cacheIdentifier];
237
    }
238
239
    /**
240
     * @param string $className
241
     * @return string
242
     */
243
    protected function getCacheIdentifier($className)
244
    {
245
        $sanitizedClassName = StringService::get()->sanitizeString(str_replace('\\', '-', $className));
246
        $sanitizedClassName .= '-' . ContextService::get()->getContextHash();
247
248
        return 'form-object-' . $sanitizedClassName;
249
    }
250
251
    /**
252
     * Wrapper for unit tests.
253
     *
254
     * @param string $className
255
     * @return FormObjectStatic
256
     */
257
    protected function buildStaticInstance($className)
258
    {
259
        /** @var FormObjectBuilderInterface $builder */
260
        $builder = Core::instantiate(DefaultFormObjectBuilder::class);
261
262
        return $builder->getStaticInstance($className);
263
    }
264
265
    /**
266
     * Wrapper for unit tests.
267
     *
268
     * @param FormInterface $form
269
     * @return FormObjectProxy
270
     */
271
    protected function getNewProxyInstance(FormInterface $form)
272
    {
273
        $formObject = $this->getInstanceWithFormInstance($form);
274
275
        /** @var FormObjectProxy $formObjectProxy */
276
        $formObjectProxy = Core::instantiate(FormObjectProxy::class, $formObject, $form);
277
278
        return $formObjectProxy;
279
    }
280
281
    /**
282
     * @return Configuration|ConfigurationObjectInterface
283
     */
284
    protected function getRootConfiguration()
285
    {
286
        return ConfigurationFactory::get()->getRootConfiguration()->getObject(true);
287
    }
288
289
    /**
290
     * @return FrontendInterface
291
     */
292
    protected function getCacheInstance()
293
    {
294
        return CacheService::get()->getCacheInstance();
295
    }
296
}
297