Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:
- name: Setup PHP with PECL extension
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
php-version: '8.3'
extensions: curl

- name: Install
Expand Down
150 changes: 82 additions & 68 deletions composer.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/SDK/Language.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ abstract public function getParamDefault(array $param): string;

/**
* @param array $param
* @param string $lang Optional language variant (for multi-language SDKs)
* @return string
*/
abstract public function getParamExample(array $param): string;
abstract public function getParamExample(array $param, string $lang = ''): string;

Comment on lines 71 to 77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Language-aware getParamExample contract looks good; ensure all subclasses are updated

The new $lang parameter on getParamExample is a clean way to support language variants. Please verify that every concrete Language subclass has been updated to match this abstract signature (including the optional second argument), otherwise PHP will complain about incompatible method declarations at runtime.


🏁 Script executed:

#!/bin/bash
# Verify all Language subclasses implement the new getParamExample signature
rg -nP 'class\s+\w+\s+extends\s+Language' src/SDK -C3

echo
echo "Checking getParamExample declarations:"
rg -n 'function\s+getParamExample\s*\(' src/SDK -C2

Length of output: 7940


Critical: 15 Language subclasses missing updated getParamExample signature

Only Kotlin.php has been updated to match the new abstract signature with the string $lang parameter. All other concrete implementations must be updated:

  • src/SDK/Language/Web.php:155
  • src/SDK/Language/Ruby.php:294
  • src/SDK/Language/Swift.php:414
  • src/SDK/Language/ReactNative.php:193
  • src/SDK/Language/REST.php:89
  • src/SDK/Language/Python.php:363
  • src/SDK/Language/Node.php:145
  • src/SDK/Language/PHP.php:360
  • src/SDK/Language/Go.php:243
  • src/SDK/Language/Deno.php:186
  • src/SDK/Language/DotNet.php:266
  • src/SDK/Language/GraphQL.php:133
  • src/SDK/Language/CLI.php:382
  • src/SDK/Language/Dart.php:239

Each needs the string $lang = '' parameter added to match the abstract method signature, otherwise PHP will raise signature compatibility errors.

🤖 Prompt for AI Agents
In src/SDK/Language.php around lines 71-77 the abstract method
getParamExample(array $param, string $lang = '') was changed; update all
concrete implementations listed (src/SDK/Language/Web.php:155, Ruby.php:294,
Swift.php:414, ReactNative.php:193, REST.php:89, Python.php:363, Node.php:145,
PHP.php:360, Go.php:243, Deno.php:186, DotNet.php:266, GraphQL.php:133,
CLI.php:382, Dart.php:239) so their getParamExample signatures match exactly
(add the string $lang = '' parameter and type), and adjust any internal usage to
accept the new parameter (or ignore it safely) to restore signature
compatibility with the abstract class; after changes run static checks/unit
tests to ensure no further signature mismatches.

/**
* @param string $key
Expand Down
3 changes: 2 additions & 1 deletion src/SDK/Language/CLI.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,10 @@ public function getTypeName(array $parameter, array $spec = []): string

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand Down
3 changes: 2 additions & 1 deletion src/SDK/Language/Dart.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,10 @@ public function getParamDefault(array $param): string

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand Down
3 changes: 2 additions & 1 deletion src/SDK/Language/Deno.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,10 @@ public function getTypeName(array $parameter, array $spec = []): string

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand Down
3 changes: 2 additions & 1 deletion src/SDK/Language/DotNet.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,10 @@ public function getParamDefault(array $param): string

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand Down
3 changes: 2 additions & 1 deletion src/SDK/Language/Go.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,10 @@ public function getParamDefault(array $param): string

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand Down
3 changes: 2 additions & 1 deletion src/SDK/Language/GraphQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,10 @@ public function getParamDefault(array $param): string

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand Down
232 changes: 201 additions & 31 deletions src/SDK/Language/Kotlin.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,10 @@ public function getParamDefault(array $param): string

/**
* @param array $param
* @param string $lang Language variant: 'kotlin' (default) or 'java'
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = 'kotlin'): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand All @@ -230,40 +231,28 @@ public function getParamExample(array $param): string
$output .= "\"\"";
break;
case self::TYPE_OBJECT:
$output .= 'mapOf( "a" to "b" )';
$output .= $lang === 'java'
? 'Map.of("a", "b")'
: 'mapOf( "a" to "b" )';
break;
case self::TYPE_ARRAY:
$output .= 'listOf()';
$output .= $lang === 'java' ? 'List.of()' : 'listOf()';
break;
}
} else {
switch ($type) {
case self::TYPE_OBJECT:
$decoded = json_decode($example, true);
if ($decoded && is_array($decoded)) {
$mapEntries = [];
foreach ($decoded as $key => $value) {
$formattedKey = '"' . $key . '"';
if (is_string($value)) {
$formattedValue = '"' . $value . '"';
} elseif (is_bool($value)) {
$formattedValue = $value ? 'true' : 'false';
} elseif (is_null($value)) {
$formattedValue = 'null';
} elseif (is_array($value)) {
$formattedValue = 'listOf()'; // Simplified for nested arrays
} else {
$formattedValue = (string)$value;
}
$mapEntries[] = ' ' . $formattedKey . ' to ' . $formattedValue;
}
if (count($mapEntries) > 0) {
$output .= "mapOf(\n" . implode(",\n", $mapEntries) . "\n )";
if ($lang === 'java') {
$output .= $this->getJavaMapExample($decoded);
} else {
$output .= 'mapOf( "a" to "b" )';
$output .= $this->getKotlinMapExample($decoded);
}
} else {
$output .= 'mapOf( "a" to "b" )';
$output .= $lang === 'java'
? 'Map.of("a", "b")'
: 'mapOf( "a" to "b" )';
}
break;
case self::TYPE_FILE:
Expand All @@ -273,15 +262,9 @@ public function getParamExample(array $param): string
break;
case self::TYPE_ARRAY:
if ($this->isPermissionString($example)) {
$output .= $this->getPermissionExample($example);
$output .= $this->getPermissionExample($example, $lang);
} else {
if (\str_starts_with($example, '[')) {
$example = \substr($example, 1);
}
if (\str_ends_with($example, ']')) {
$example = \substr($example, 0, -1);
}
$output .= 'listOf(' . $example . ')';
$output .= $this->getArrayExample($example, $lang);
}
break;
case self::TYPE_BOOLEAN:
Expand All @@ -296,6 +279,190 @@ public function getParamExample(array $param): string
return $output;
}

/**
* Generate Kotlin-style map initialization
*
* @param array $data
* @param int $indentLevel Indentation level for nested maps
* @return string
*/
protected function getKotlinMapExample(array $data, int $indentLevel = 0): string
{
$mapEntries = [];
$baseIndent = str_repeat(' ', $indentLevel + 2);

foreach ($data as $key => $value) {
$formattedKey = '"' . $key . '"';
if (is_string($value)) {
$formattedValue = '"' . $value . '"';
} elseif (is_bool($value)) {
$formattedValue = $value ? 'true' : 'false';
} elseif (is_null($value)) {
$formattedValue = 'null';
} elseif (is_array($value)) {
// Check if it's an associative array (object) or indexed array
$isObject = !array_is_list($value);
if ($isObject) {
$formattedValue = $this->getKotlinMapExample($value, $indentLevel + 1);
} else {
$formattedValue = $this->getArrayExample(json_encode($value), 'kotlin');
}
} else {
$formattedValue = (string)$value;
}
$mapEntries[] = $baseIndent . $formattedKey . ' to ' . $formattedValue;
}

if (count($mapEntries) > 0) {
$closeIndent = str_repeat(' ', $indentLevel + 1);
return "mapOf(\n" . implode(",\n", $mapEntries) . "\n" . $closeIndent . ")";
} else {
return 'mapOf( "a" to "b" )';
}
}

/**
* Generate Java-style map initialization using Map.of()
*
* @param array $data
* @param int $indentLevel Indentation level for nested maps
* @return string
*/
protected function getJavaMapExample(array $data, int $indentLevel = 0): string
{
$mapEntries = [];
$baseIndent = str_repeat(' ', $indentLevel + 2);

foreach ($data as $key => $value) {
$formattedKey = '"' . $key . '"';
if (is_string($value)) {
$formattedValue = '"' . $value . '"';
} elseif (is_bool($value)) {
$formattedValue = $value ? 'true' : 'false';
} elseif (is_null($value)) {
$formattedValue = 'null';
} elseif (is_array($value)) {
// Check if it's an associative array (object) or indexed array
$isObject = !array_is_list($value);
if ($isObject) {
$formattedValue = $this->getJavaMapExample($value, $indentLevel + 1);
} else {
$formattedValue = $this->getArrayExample(json_encode($value), 'java');
}
} else {
$formattedValue = (string)$value;
}
$mapEntries[] = $baseIndent . $formattedKey . ', ' . $formattedValue;
}

if (count($mapEntries) > 0) {
$closeIndent = str_repeat(' ', $indentLevel + 1);
return "Map.of(\n" . implode(",\n", $mapEntries) . "\n" . $closeIndent . ")";
} else {
return 'Map.of("a", "b")';
}
}

/**
* Generate array example for the given language
*
* @param string $example Array example like '[1, 2, 3]' or '[{"key": "value"}]'
* @param string $lang Language variant: 'kotlin' or 'java'
* @return string
*/
protected function getArrayExample(string $example, string $lang = 'kotlin'): string
{
// Try to decode as JSON to handle arrays of objects
$decoded = json_decode($example, true);
if ($decoded && is_array($decoded)) {
$arrayItems = [];
foreach ($decoded as $item) {
if (is_array($item)) {
// Check if it's an associative array (object) or indexed array (nested array)
$isObject = !array_is_list($item);

if ($isObject) {
// It's an object/map, convert it
if ($lang === 'java') {
$arrayItems[] = $this->getJavaMapExample($item);
} else {
$arrayItems[] = $this->getKotlinMapExample($item);
}
} else {
// It's a nested array, recursively convert it
$arrayItems[] = $this->getArrayExample(json_encode($item), $lang);
}
} else {
// Primitive value
if (is_string($item)) {
$arrayItems[] = '"' . $item . '"';
} elseif (is_bool($item)) {
$arrayItems[] = $item ? 'true' : 'false';
} elseif (is_null($item)) {
$arrayItems[] = 'null';
} else {
$arrayItems[] = (string)$item;
}
}
}
return $lang === 'java'
? 'List.of(' . implode(', ', $arrayItems) . ')'
: 'listOf(' . implode(', ', $arrayItems) . ')';
}

// Fallback to old behavior for non-JSON arrays
if (\str_starts_with($example, '[')) {
$example = \substr($example, 1);
}
if (\str_ends_with($example, ']')) {
$example = \substr($example, 0, -1);
}
return $lang === 'java'
? 'List.of(' . $example . ')'
: 'listOf(' . $example . ')';
}

/**
* Generate permission example for the given language
*
* @param string $example Permission string like '["read(\"any\")"]'
* @param string $lang Language variant: 'kotlin' or 'java'
* @return string
*/
public function getPermissionExample(string $example, string $lang = 'kotlin'): string
{
$permissions = [];
$staticOp = $this->getStaticAccessOperator();
$quote = $this->getStringQuote();
$prefix = $this->getPermissionPrefix();

foreach ($this->extractPermissionParts($example) as $permission) {
$args = [];
if ($permission['id'] !== null) {
$args[] = $quote . $permission['id'] . $quote;
}
if ($permission['innerRole'] !== null) {
$args[] = $quote . $permission['innerRole'] . $quote;
}
$argsString = implode(', ', $args);

$action = $permission['action'];
$role = $permission['role'];
$action = $this->transformPermissionAction($action);
$role = $this->transformPermissionRole($role);

$permissions[] = $prefix . 'Permission' . $staticOp . $action . '(' . $prefix . 'Role' . $staticOp . $role . '(' . $argsString . '))';
}

$permissionsString = implode(', ', $permissions);

// For Java, use List.of() instead of listOf()
if ($lang === 'java') {
return 'List.of(' . $permissionsString . ')';
}
return 'listOf(' . $permissionsString . ')';
}

/**
* @return array
*/
Expand Down Expand Up @@ -496,6 +663,9 @@ public function getFilters(): array
new TwigFilter('propertyAssignment', function (array $property, array $spec) {
return $this->getPropertyAssignment($property, $spec);
}),
new TwigFilter('javaParamExample', function (array $param) {
return $this->getParamExample($param, 'java');
}, ['is_safe' => ['html']]),
];
}

Expand Down
3 changes: 2 additions & 1 deletion src/SDK/Language/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,10 @@ public function getReturn(array $method, array $spec): string

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand Down
3 changes: 2 additions & 1 deletion src/SDK/Language/PHP.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,10 @@ public function getParamDefault(array $param): string

/**
* @param array $param
* @param string $lang
* @return string
*/
public function getParamExample(array $param): string
public function getParamExample(array $param, string $lang = ''): string
{
$type = $param['type'] ?? '';
$example = $param['example'] ?? '';
Expand Down
Loading
Loading