Completed
Push — middleware-wip-tmp ( d8f2e1 )
by Romain
02:50
created

FormObjectFactory::insertObjectProperties()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
cc 6
eloc 13
nc 3
nop 1
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\Formz\Configuration\Configuration;
17
use Romm\Formz\Configuration\ConfigurationFactory;
18
use Romm\Formz\Core\Core;
19
use Romm\Formz\Exceptions\ClassNotFoundException;
20
use Romm\Formz\Exceptions\InvalidArgumentTypeException;
21
use Romm\Formz\Form\FormInterface;
22
use Romm\Formz\Service\CacheService;
23
use Romm\Formz\Service\StringService;
24
use Romm\Formz\Service\Traits\ExtendedSelfInstantiateTrait;
25
use Romm\Formz\Service\TypoScriptService;
26
use TYPO3\CMS\Core\SingletonInterface;
27
use TYPO3\CMS\Core\Utility\GeneralUtility;
28
use TYPO3\CMS\Extbase\Reflection\ReflectionService;
29
30
/**
31
 * Factory class which will manage instances of `FormObject`.
32
 */
33
class FormObjectFactory implements SingletonInterface
34
{
35
    use ExtendedSelfInstantiateTrait;
36
37
    const IGNORE_PROPERTY = 'formz-ignore';
38
39
    /**
40
     * @var ConfigurationFactory
41
     */
42
    protected $configurationFactory;
43
44
    /**
45
     * @var TypoScriptService
46
     */
47
    protected $typoScriptService;
48
49
    /**
50
     * @var FormObject[]
51
     */
52
    protected $instances = [];
53
54
    /**
55
     * @var FormObjectStatic[]
56
     */
57
    protected $static = [];
58
59
    /**
60
     * @var FormObjectProxy[]
61
     */
62
    protected $proxy = [];
63
64
    /**
65
     * @var array
66
     */
67
    private static $ignoredProperties = ['validationData', 'uid', 'pid', '_localizedUid', '_languageUid', '_versionedUid'];
68
69
    /**
70
     * Will create an instance of `FormObject` based on a class which implements
71
     * the interface `FormInterface`.
72
     *
73
     * @param string $className
74
     * @param string $name
75
     * @return FormObject
76
     */
77
    public function getInstanceFromClassName($className, $name)
78
    {
79
        /** @var FormObject $formObject */
80
        $formObject = Core::instantiate(FormObject::class, $name, $this->getStaticInstance($className));
81
82
        return $formObject;
83
    }
84
85
    /**
86
     * @param FormInterface $form
87
     * @param string        $name
88
     * @return FormObject
89
     */
90
    public function getInstanceFromFormInstance(FormInterface $form, $name = 'defaultName')
91
    {
92
        $hash = spl_object_hash($form);
93
94
        if (false === isset($this->instances[$hash])) {
95
            $formObject = $this->getInstanceFromClassName(get_class($form), $name);
96
            $formObject->setForm($form);
97
98
            $this->instances[$hash] = $formObject;
99
        }
100
101
        return $this->instances[$hash];
102
    }
103
104
    /**
105
     * @param FormObject    $formObject
106
     * @param FormInterface $form
107
     * @return FormObjectProxy
108
     */
109
    public function getProxy(FormObject $formObject, FormInterface $form)
110
    {
111
        $hash = spl_object_hash($form);
112
113
        if (false === isset($this->proxy[$hash])) {
114
            $proxy = Core::instantiate(FormObjectProxy::class, $formObject, $form);
115
116
            $this->proxy[$hash] = $proxy;
117
        }
118
119
        return $this->proxy[$hash];
120
    }
121
122
    /**
123
     * @param string $className
124
     * @return FormObjectStatic
125
     * @throws ClassNotFoundException
126
     * @throws InvalidArgumentTypeException
127
     */
128
    protected function getStaticInstance($className)
129
    {
130
        if (false === class_exists($className)) {
131
            throw ClassNotFoundException::wrongFormClassName($className);
132
        }
133
134
        if (false === in_array(FormInterface::class, class_implements($className))) {
135
            throw InvalidArgumentTypeException::wrongFormType($className);
136
        }
137
138
        $cacheIdentifier = $this->getCacheIdentifier($className);
139
140
        if (false === isset($this->static[$cacheIdentifier])) {
141
            $cacheInstance = CacheService::get()->getCacheInstance();
142
143
            if ($cacheInstance->has($cacheIdentifier)) {
144
                $instance = $cacheInstance->get($cacheIdentifier);
145
            } else {
146
                $instance = $this->createInstance($className);
147
                $cacheInstance->set($cacheIdentifier, $instance);
148
            }
149
150
            /** @var Configuration $formzConfigurationObject */
151
            $formzConfigurationObject = $this->configurationFactory
152
                ->getFormzConfiguration()
153
                ->getObject(true);
154
155
            if (false === $formzConfigurationObject->hasForm($className)) {
156
                $formzConfigurationObject->addForm($instance);
157
            }
158
159
            $this->static[$cacheIdentifier] = $instance;
160
        }
161
162
        return $this->static[$cacheIdentifier];
163
    }
164
165
    /**
166
     * Creates and initializes a new `StaticFormObject` instance.
167
     *
168
     * @param string $className
169
     * @return FormObjectStatic
170
     */
171
    protected function createInstance($className)
172
    {
173
        $formConfiguration = $this->typoScriptService->getFormConfiguration($className);
174
175
        /** @var FormObjectStatic $instance */
176
        $instance = Core::instantiate(FormObjectStatic::class, $className, $formConfiguration);
177
178
        $this->insertObjectProperties($instance);
179
180
        $instance->getObjectHash();
181
182
        return $instance;
183
    }
184
185
    /**
186
     * Will insert all the accessible properties of the given instance.
187
     *
188
     * @param FormObjectStatic $instance
189
     */
190
    protected function insertObjectProperties(FormObjectStatic $instance)
191
    {
192
        $className = $instance->getClassName();
193
194
        /** @var ReflectionService $reflectionService */
195
        $reflectionService = GeneralUtility::makeInstance(ReflectionService::class);
196
        $reflectionProperties = $reflectionService->getClassPropertyNames($className);
197
198
        $classReflection = new \ReflectionClass($className);
199
        $publicProperties = $classReflection->getProperties(\ReflectionProperty::IS_PUBLIC);
200
201
        foreach ($reflectionProperties as $property) {
202
            if (false === in_array($property, self::$ignoredProperties)
203
                && false === $reflectionService->isPropertyTaggedWith($className, $property, self::IGNORE_PROPERTY)
204
                && ((true === in_array($property, $publicProperties))
205
                    || $reflectionService->hasMethod($className, 'get' . ucfirst($property))
206
                )
207
            ) {
208
                $instance->addProperty($property);
209
            }
210
        }
211
212
        unset($publicProperties);
213
    }
214
215
    /**
216
     * @param string $className
217
     * @return string
218
     */
219
    protected function getCacheIdentifier($className)
220
    {
221
        $sanitizedClassName = StringService::get()->sanitizeString(str_replace('\\', '-', $className));
222
223
        return 'form-object-' . $sanitizedClassName;
224
    }
225
226
    /**
227
     * @param ConfigurationFactory $configurationFactory
228
     */
229
    public function injectConfigurationFactory(ConfigurationFactory $configurationFactory)
230
    {
231
        $this->configurationFactory = $configurationFactory;
232
    }
233
234
    /**
235
     * @param TypoScriptService $typoScriptService
236
     */
237
    public function injectTypoScriptService(TypoScriptService $typoScriptService)
238
    {
239
        $this->typoScriptService = $typoScriptService;
240
    }
241
}
242