diff --git a/src/ControllerMethodParameter.php b/src/ControllerMethodParameter.php index 389ad70..340854f 100644 --- a/src/ControllerMethodParameter.php +++ b/src/ControllerMethodParameter.php @@ -2,14 +2,7 @@ namespace OpenAPIExtractor; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; -use PhpParser\Node\Expr\ConstFetch; -use PhpParser\Node\Expr\UnaryMinus; use PhpParser\Node\Param; -use PhpParser\Node\Scalar\LNumber; -use PhpParser\Node\Scalar\String_; class ControllerMethodParameter { public OpenApiType $type; @@ -21,46 +14,12 @@ public function __construct(string $context, array $definitions, public string $ $this->type = OpenApiType::resolve($context, $definitions, $methodParameter->type); } if ($methodParameter->default != null) { - $this->type->hasDefaultValue = true; - $this->type->defaultValue = self::exprToValue($context, $methodParameter->default); - } - } - - private static function exprToValue(string $context, Expr $expr): mixed { - if ($expr instanceof ConstFetch) { - $value = $expr->name->getLast(); - return match ($value) { - "null" => null, - "true" => true, - "false" => false, - default => Logger::panic($context, "Unable to evaluate constant value '" . $value . "'"), - }; - } - if ($expr instanceof String_) { - return $expr->value; - } - if ($expr instanceof LNumber) { - return intval($expr->value); - } - if ($expr instanceof UnaryMinus) { - return -self::exprToValue($context, $expr->expr); - } - if ($expr instanceof Array_) { - $values = array_map(fn (ArrayItem $item): mixed => self::exprToValue($context, $item), $expr->items); - $filteredValues = array_filter($values, fn (mixed $value) => $value !== null); - if (count($filteredValues) != count($values)) { - return null; + try { + $this->type->defaultValue = Helpers::exprToValue($context, $methodParameter->default); + $this->type->hasDefaultValue = true; + } catch (UnsupportedExprException $e) { + Logger::debug($context, $e); } - return $values; - } - if ($expr instanceof ArrayItem) { - return self::exprToValue($context, $expr->value); } - if ($expr instanceof Expr\ClassConstFetch || $expr instanceof Expr\BinaryOp) { - // Not supported - return null; - } - - Logger::panic($context, "Unable to evaluate expression '" . get_class($expr) . "'"); } } diff --git a/src/Helpers.php b/src/Helpers.php index b2581b8..373a2e2 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -6,8 +6,12 @@ use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\AttributeGroup; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Expr\ConstFetch; +use PhpParser\Node\Expr\UnaryMinus; +use PhpParser\Node\Scalar\LNumber; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; @@ -260,4 +264,47 @@ public static function collectUsedRefs(array $data): array { }); return $refs; } + + /** + * @throws LoggerException + * @throws UnsupportedExprException + */ + public static function exprToValue(string $context, Expr $expr): mixed { + if ($expr instanceof ConstFetch) { + $value = $expr->name->getLast(); + return match ($value) { + 'null' => null, + 'true' => true, + 'false' => false, + default => Logger::panic($context, "Unable to evaluate constant value '$value'"), + }; + } + if ($expr instanceof String_) { + return $expr->value; + } + if ($expr instanceof LNumber) { + return intval($expr->value); + } + if ($expr instanceof UnaryMinus) { + return -self::exprToValue($context, $expr->expr); + } + if ($expr instanceof Array_) { + $array = []; + foreach ($expr->items as $item) { + try { + $value = self::exprToValue($context, $item->value); + if ($item->key !== null) { + $array[self::exprToValue($context, $item->key)] = $value; + } else { + $array[] = $value; + } + } catch (UnsupportedExprException $e) { + Logger::debug($context, $e); + } + } + return $array; + } + + throw new UnsupportedExprException($expr, $context); + } } diff --git a/src/UnsupportedExprException.php b/src/UnsupportedExprException.php new file mode 100644 index 0000000..4c5fd72 --- /dev/null +++ b/src/UnsupportedExprException.php @@ -0,0 +1,15 @@ +context . ': Unable to parse Expr: ' . get_class($this->expr)); + } +} diff --git a/tests/appinfo/routes.php b/tests/appinfo/routes.php index f7d4471..10ad83a 100644 --- a/tests/appinfo/routes.php +++ b/tests/appinfo/routes.php @@ -61,5 +61,7 @@ ['name' => 'Settings#stringValueParameter', 'url' => '/api/{apiVersion}/string-value', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#intValueParameter', 'url' => '/api/{apiVersion}/int-value', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#numericParameter', 'url' => '/api/{apiVersion}/numeric', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'Settings#arrayListParameter', 'url' => '/api/{apiVersion}/array-list', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'Settings#arrayKeyedParameter', 'url' => '/api/{apiVersion}/array-keyed', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ], ]; diff --git a/tests/lib/Controller/SettingsController.php b/tests/lib/Controller/SettingsController.php index cef3fe2..27eb597 100644 --- a/tests/lib/Controller/SettingsController.php +++ b/tests/lib/Controller/SettingsController.php @@ -386,4 +386,28 @@ public function intValueParameter(int $value): DataResponse { public function numericParameter(mixed $value): DataResponse { return new DataResponse(); } + + /** + * A route with list + * + * @param list $value Some array value + * @return DataResponse, array{}> + * + * 200: Admin settings updated + */ + public function arrayListParameter(array $value = ['test']): DataResponse { + return new DataResponse(); + } + + /** + * A route with keyed array + * + * @param array $value Some array value + * @return DataResponse, array{}> + * + * 200: Admin settings updated + */ + public function arrayKeyedParameter(array $value = ['test' => 'abc']): DataResponse { + return new DataResponse(); + } } diff --git a/tests/openapi-administration.json b/tests/openapi-administration.json index bc47890..61dc919 100644 --- a/tests/openapi-administration.json +++ b/tests/openapi-administration.json @@ -2161,6 +2161,172 @@ } } } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/array-list": { + "post": { + "operationId": "settings-array-list-parameter", + "summary": "A route with list", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "value[]", + "in": "query", + "description": "Some array value", + "schema": { + "type": "array", + "default": [ + "test" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v2" + ], + "default": "v2" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Admin settings updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/array-keyed": { + "post": { + "operationId": "settings-array-keyed-parameter", + "summary": "A route with keyed array", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "value", + "in": "query", + "description": "Some array value", + "schema": { + "type": "string" + } + }, + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v2" + ], + "default": "v2" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Admin settings updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } } }, "tags": [] diff --git a/tests/openapi-full.json b/tests/openapi-full.json index 30e6770..175cdff 100644 --- a/tests/openapi-full.json +++ b/tests/openapi-full.json @@ -2289,6 +2289,172 @@ } } }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/array-list": { + "post": { + "operationId": "settings-array-list-parameter", + "summary": "A route with list", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "value[]", + "in": "query", + "description": "Some array value", + "schema": { + "type": "array", + "default": [ + "test" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v2" + ], + "default": "v2" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Admin settings updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/array-keyed": { + "post": { + "operationId": "settings-array-keyed-parameter", + "summary": "A route with keyed array", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "value", + "in": "query", + "description": "Some array value", + "schema": { + "type": "string" + } + }, + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v2" + ], + "default": "v2" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Admin settings updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/apps/notifications/api/{apiVersion}/default-admin-overwritten": { "post": { "operationId": "admin_settings-moved-to-default-scope",