1212 */
1313
1414use 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 ;
1618use Neos \Flow \Reflection \ReflectionService ;
1719use Neos \Utility \TypeHandling ;
1820use 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