@@ -28,6 +28,7 @@ function getTypedParams() {
2828 foreach ($ paramsToValues as $ paramName =>$ paramValue ) {
2929 $ format = $ paramsToFormat [$ paramName ];
3030
31+ // the parameter name was not present in the annotations
3132 if (!array_key_exists ($ paramName , $ paramsToFormat )) {
3233 ///TODO: return 500
3334 echo "Error: unknown param format: $ paramName \n" ;
@@ -40,14 +41,15 @@ function getTypedParams() {
4041
4142 // fill the new object with the param values
4243 ///TODO: handle nested formated objects
43- foreach ($ paramValue as $ key =>$ value ) {
44+ foreach ($ paramValue as $ propertyName =>$ propertyValue ) {
4445 ///TODO: return 404
45- if (!array_key_exists ($ key , $ classFormat )) {
46+ // the property was not present in the class definition
47+ if (!array_key_exists ($ propertyName , $ classFormat )) {
4648 echo "Error: unknown param: $ paramName \n" ;
4749 return [];
4850 }
4951
50- $ obj ->$ key = $ value ;
52+ $ obj ->$ propertyName = $ propertyValue ;
5153 }
5254
5355 $ paramToTypedMap [$ paramName ] = $ obj ;
@@ -73,24 +75,117 @@ function getParamNamesToValuesMap($backtrace): array {
7375 }
7476}
7577
78+ /**
79+ * Parses format string enriched by nullability and array modifiers.
80+ * In case the format contains array, this data class can be recursive.
81+ * Example: string?[]? can either be null or of string?[] type, an array of nullable strings
82+ * Example2: string[]?[] is an array of null or string arrays
83+ */
84+ class FormatParser {
85+ public bool $ nullable = false ;
86+ public bool $ isArray = false ;
87+ // contains the format stripped of the nullability ?, null if it is an array
88+ public ?string $ format = null ;
89+ // contains the format definition of nested elements, null if it is not an array
90+ public ?FormatParser $ nested = null ;
91+
92+ public function __construct (string $ format ) {
93+ // check nullability
94+ if (str_ends_with ($ format , "? " )) {
95+ $ this ->nullable = true ;
96+ $ format = substr ($ format , 0 , -1 );
97+ }
98+
99+ // check array
100+ if (str_ends_with ($ format , "[] " )) {
101+ $ this ->isArray = true ;
102+ $ format = substr ($ format , 0 , -2 );
103+ $ this ->nested = new FormatParser ($ format );
104+ }
105+ else {
106+ $ this ->format = $ format ;
107+ }
108+ }
109+ }
110+
76111
77112class MetaFormat {
113+ // validates primitive formats of intrinsic PHP types
114+ ///TODO: make this static somehow (or cached)
115+ private $ validators ;
116+
117+ public function __construct () {
118+ $ this ->validators = AnnotationHelper::getValidators ();
119+ }
120+
121+
78122 /**
79123 * Validates the given format.
80124 * @return bool Returns whether the format and all nested formats are valid.
81125 */
82126 public function validate () {
83- return true ;
127+ // check whether all higher level contracts hold
128+ if (!$ this ->validateSelf ())
129+ return false ;
130+
131+ // check properties
132+ $ selfFormat = AnnotationHelper::getClassFormats (get_class ($ this ));
133+ foreach ($ selfFormat as $ propertyName =>$ propertyFormat ) {
134+ ///TODO: check if this is true
135+ /// if the property is checked by type only, there is no need to check it as an invalid assignment would rise an error
136+ $ value = $ this ->$ propertyName ;
137+ $ format = $ propertyFormat ["format " ];
138+ if ($ format === null )
139+ continue ;
140+
141+ // enables parsing more complicated formats (string[]?, string?[], string?[][]?, ...)
142+ $ parsedFormat = new FormatParser ($ format );
143+ if (!$ this ->recursiveFormatChecker ($ value , $ parsedFormat ))
144+ return false ;
145+ }
146+
147+ }
148+
149+ private function recursiveFormatChecker ($ value , FormatParser $ parsedFormat ) {
150+ // enables parsing more complicated formats (string[]?, string?[], string?[][]?, ...)
151+
152+ // check nullability
153+ if ($ value === null )
154+ return $ parsedFormat ->nullable ;
155+
156+ // handle arrays
157+ if ($ parsedFormat ->isArray ) {
158+ if (!is_array ($ value ))
159+ return false ;
160+
161+ // if any element fails, the whole format fails
162+ foreach ($ value as $ element ) {
163+ if (!$ this ->recursiveFormatChecker ($ element , $ parsedFormat ->nested ))
164+ return false ;
165+ }
166+ return true ;
167+ }
168+
169+ ///TODO: raise an error
170+ // check whether the validator exists
171+ if (!array_key_exists ($ parsedFormat ->format , $ this ->validators )) {
172+ echo "Error: missing validator for format: " . $ parsedFormat ->format . "\n" ;
173+ return false ;
174+ }
175+
176+ return $ this ->validators [$ parsedFormat ->format ]($ value );
84177 }
85178
86179 /**
87180 * Validates this format. Automatically called by the validate method on all fields.
88181 * Primitive formats should always override this, composite formats might want to override
89182 * this in case more complex contracts need to be enforced.
183+ * This method should not check the format of nested types.
90184 * @return bool Returns whether the format is valid.
91185 */
92- protected function validate_this () {
93-
186+ protected function validateSelf () {
187+ // there are no constraints by default
188+ return true ;
94189 }
95190}
96191
@@ -146,10 +241,13 @@ class TestView extends MetaView {
146241 * @checked_param format:group group
147242 * @checked_param format:uuid user_id
148243 */
149- function endpoint ($ group ) {
244+ function endpoint ($ group, $ user_id ) {
150245 $ params = $ this ->getTypedParams ();
151246 $ formattedGroup = $ params ["group " ];
152247 var_dump ($ formattedGroup );
248+
249+ // $a = new GroupFormat();
250+ // $a->validate();
153251 }
154252
155253 // the names of the format and the output do not have to be identical, the strings in the desired data format refer the output names
0 commit comments