Skip to content

Commit bf1cbc2

Browse files
Merge pull request #28 from netlogix/bugfix/use-compilestatic-to-register-known-types
BUGFIX: Use "@flow\CompileStatic" to collect known types
2 parents 02c835c + 490ca45 commit bf1cbc2

1 file changed

Lines changed: 114 additions & 58 deletions

File tree

Classes/Resource/Information/ExposableTypeMap.php

Lines changed: 114 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
*/
1313

1414
use Neos\Flow\Annotations as Flow;
15-
use Neos\Flow\Package\PackageManagerInterface;
15+
use Neos\Flow\Log\PsrSystemLoggerInterface;
16+
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
17+
use Neos\Flow\Package\PackageManager;
1618
use Neos\Flow\Reflection\ReflectionService;
1719
use Neos\Utility\TypeHandling;
1820
use Netlogix\JsonApiOrg\AnnotationGenerics\Annotations as JsonApi;
@@ -25,64 +27,143 @@ class ExposableTypeMap extends \Netlogix\JsonApiOrg\Resource\Information\Exposab
2527
{
2628

2729
const PATTERN = '%^(?<vendor>[^\\\\]+)\\\\(?<package>[^\\\\]+)\\\\(?<subpackage>.+)?\\\\domain\\\\(?<type>model|command)\\\\(?<flat>.*)$%i';
30+
2831
/**
29-
* @var ReflectionService
3032
* @Flow\Inject
33+
* @var ObjectManagerInterface
3134
*/
32-
public $reflectionService;
35+
protected $objectManager;
36+
3337
/**
34-
* @var PackageManagerInterface
3538
* @Flow\Inject
39+
* @var PsrSystemLoggerInterface
3640
*/
37-
protected $packageManager;
41+
protected $psrSystemLoggerInterface;
3842

3943
/**
4044
* All "ExposeType" objects are initialized automatically
4145
*/
4246
public function initializeObject()
4347
{
44-
foreach ($this->reflectionService->getClassNamesByAnnotation(JsonApi\ExposeType::class) as $className) {
45-
foreach ($this->reflectionService->getClassAnnotations($className,
46-
JsonApi\ExposeType::class) as $annotation) {
48+
list(
49+
$oneToOneTypeToClassMap,
50+
$classNameToPropertyNamesMap,
51+
$classNamesToMethodNamesMap
52+
) = static::collectKnownTypes($this->objectManager);
53+
54+
$this->oneToOneTypeToClassMap = array_merge($this->oneToOneTypeToClassMap, $oneToOneTypeToClassMap);
55+
56+
foreach ($classNameToPropertyNamesMap as $className => $properties) {
57+
foreach ($properties as $propertyName => $propertyVarType)
58+
try {
59+
$this->registerKnownPropertyType(
60+
$oneToOneTypeToClassMap[$className],
61+
$propertyName,
62+
$propertyVarType
63+
);
64+
} catch (\Exception $e) {
65+
$this->psrSystemLoggerInterface->error('Could not register known property type for type', [
66+
'className' => $className,
67+
'propertyName' => $propertyName,
68+
'propertyVarType' => $propertyVarType,
69+
]);
70+
}
71+
}
72+
73+
foreach ($classNamesToMethodNamesMap as $className => $methods) {
74+
foreach ($methods as $methodName => $methodVarType) {
75+
try {
76+
$this->registerKnownPropertyType(
77+
$oneToOneTypeToClassMap[$className],
78+
$methodName,
79+
$methodVarType
80+
);
81+
} catch (\Exception $e) {
82+
$this->psrSystemLoggerInterface->error('Could not register known property type for type', [
83+
'className' => $className,
84+
'methodName' => $methodName,
85+
'methodVarType' => $methodVarType,
86+
]);
87+
}
88+
}
89+
}
90+
91+
parent::initializeObject();
92+
}
93+
94+
protected function registerKnownPropertyType(string $typeName, string $propertyName, string $varType)
95+
{
96+
if (!$typeName || !$propertyName || !$varType) {
97+
return;
98+
}
99+
100+
$typeNameAndType = strtolower($typeName . '->' . $propertyName);
101+
102+
$varType = TypeHandling::parseType($varType);
103+
$isCollection = (bool)$varType['elementType'];
104+
$elementType = $isCollection ? $varType['elementType'] : $varType['type'];
105+
106+
if ($isCollection) {
107+
$this->typeAndPropertyNameToClassIdentifierMap[$typeNameAndType] = 'array<' . $elementType . '>';
108+
} else {
109+
$this->typeAndPropertyNameToClassIdentifierMap[$typeNameAndType] = $elementType;
110+
}
111+
}
112+
113+
/**
114+
* This method is compiled statically, as the ReflectionService should not be used in Production context.
115+
* The cached variant of the ReflectionService is missing at least the "methods annotated with".
116+
*
117+
* @Flow\CompileStatic
118+
* @param ObjectManagerInterface $objectManager
119+
* @return array
120+
*/
121+
protected static function collectKnownTypes(ObjectManagerInterface $objectManager): array
122+
{
123+
$oneToOneTypeToClassMap = [];
124+
$classNameToPropertyNamesMap = [];
125+
$classNameToMethodNamesMap = [];
126+
127+
$reflectionService = $objectManager->get(ReflectionService::class);
128+
$packageManager = $objectManager->get(PackageManager::class);
129+
130+
$exposedTypes = $reflectionService->getClassNamesByAnnotation(JsonApi\ExposeType::class);
131+
foreach ($exposedTypes as $className) {
132+
$annotations = $reflectionService->getClassAnnotations($className, JsonApi\ExposeType::class);
133+
134+
foreach ($annotations as $annotation) {
47135
assert($annotation instanceof JsonApi\ExposeType);
48136
$type = $annotation->typeName;
137+
49138
if (!$type) {
50139
$typeComponents = preg_split('%\\\\Domain\\\\(Model|Command)\\\\%i', $className, 2);
51140
$typeComponents[0] = explode('\\', $typeComponents[0]);
52-
while ($typeComponents[0] && !$this->packageManager->isPackageAvailable(join('.',
141+
while ($typeComponents[0] && !$packageManager->isPackageAvailable(join('.',
53142
$typeComponents[0]))) {
54143
unset($typeComponents[0][count($typeComponents[0]) - 1]);
55144
}
56145
$type = strtolower(end($typeComponents[0]) . '/' . str_replace('\\', '.', $typeComponents[1]));
57146
}
58-
$this->oneToOneTypeToClassMap[$className] = $type;
147+
$oneToOneTypeToClassMap[$className] = $type;
148+
$classNameToPropertyNamesMap[$className] = [];
149+
$classNameToMethodNamesMap[$className] = [];
59150

60-
$propertyNames = $this
61-
->reflectionService
151+
$propertyNames = $reflectionService
62152
->getPropertyNamesByAnnotation($className, JsonApi\ExposeProperty::class);
63153
array_walk(
64154
$propertyNames,
65-
function (string $propertyName) use ($type, $className) {
66-
try {
67-
$this->registerKnownPropertyType(
68-
$type,
69-
$propertyName,
70-
$this
71-
->reflectionService
72-
->getPropertyTagValues($className, $propertyName, 'var')[0]
73-
?: ''
74-
);
75-
} catch (\Exception $e) {
76-
}
155+
function (string $propertyName) use ($className, $reflectionService, &$classNameToPropertyNamesMap) {
156+
$classNameToPropertyNamesMap[$className][$propertyName] = $reflectionService
157+
->getPropertyTagValues($className, $propertyName, 'var')[0]
158+
?: '';
77159
}
78160
);
79161

80-
$methodNames = $this
81-
->reflectionService
162+
$methodNames = $reflectionService
82163
->getMethodsAnnotatedWith($className, JsonApi\ExposeProperty::class);
83164
array_walk(
84165
$methodNames,
85-
function (string $methodName) use ($type, $className) {
166+
function (string $methodName) use ($className, $reflectionService, &$classNameToMethodNamesMap) {
86167
$methodNameLength = strlen($methodName);
87168
if ($methodNameLength > 2 && substr($methodName, 0, 2) === 'is') {
88169
$propertyName = lcfirst(substr($methodName, 2));
@@ -91,41 +172,16 @@ function (string $methodName) use ($type, $className) {
91172
} else {
92173
return;
93174
}
94-
try {
95-
$this->registerKnownPropertyType(
96-
$type,
97-
$propertyName,
98-
$this
99-
->reflectionService
100-
->getMethodTagsValues($className, $methodName)['return'][0]
101-
?: ''
102-
);
103-
} catch (\Exception $e) {
104-
}
175+
176+
$classNameToMethodNamesMap[$className][$propertyName] = $reflectionService
177+
->getMethodTagsValues($className, $methodName)['return'][0]
178+
?: '';
105179
}
106180
);
107-
108181
}
109182
}
110-
parent::initializeObject();
111-
}
112-
113-
protected function registerKnownPropertyType(string $typeName, string $propertyName, string $varType)
114-
{
115-
if (!$typeName || !$propertyName || !$varType) {
116-
return;
117-
}
118-
119-
$typeNameAndType = strtolower($typeName . '->' . $propertyName);
120-
121-
$varType = TypeHandling::parseType($varType);
122-
$isCollection = (bool)$varType['elementType'];
123-
$elementType = $isCollection ? $varType['elementType'] : $varType['type'];
124183

125-
if ($isCollection) {
126-
$this->typeAndPropertyNameToClassIdentifierMap[$typeNameAndType] = 'array<' . $elementType . '>';
127-
} else {
128-
$this->typeAndPropertyNameToClassIdentifierMap[$typeNameAndType] = $elementType;
129-
}
184+
return [$oneToOneTypeToClassMap, $classNameToPropertyNamesMap, $classNameToMethodNamesMap];
130185
}
186+
131187
}

0 commit comments

Comments
 (0)