Last active
January 1, 2025 09:51
-
-
Save sasezaki/6c48c7ebcbc9c17a372e9d2680d2fc94 to your computer and use it in GitHub Desktop.
Big Query schema json to PHP's array-shape
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; | |
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; | |
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; | |
use PHPStan\PhpDocParser\Ast\Type; | |
use PHPStan\PhpDocParser\Printer\Printer; | |
require __DIR__ . '/vendor/autoload.php'; | |
$schemaJson = <<<'JSON' | |
[ | |
{ | |
"mode": "NULLABLE", | |
"name": "logName", | |
"type": "STRING" | |
}, | |
{ | |
"fields" : [ | |
{ | |
"name": "first_name", | |
"type": "STRING", | |
"mode": "NULLABLE" | |
}, | |
{ | |
"name": "product", | |
"type": "RECORD", | |
"mode": "NULLABLE", | |
"fields": [ | |
{ | |
"name": "id", | |
"type": "STRING", | |
"mode": "NULLABLE" | |
} | |
] | |
}, | |
{ | |
"name": "first_name", | |
"type": "FLOAT", | |
"mode": "NULLABLE" | |
} | |
], | |
"mode": "NULLABLE", | |
"name": "jsonPayload", | |
"type": "RECORD" | |
} | |
] | |
JSON; | |
// class ContextTypeSpecifyingExtenion implements MethodTypeSpecifyingExtension | |
// { | |
// public function __construct(private ContextTypeProviderInterface $contextTypeProvider){} | |
// public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes | |
// { | |
// return new SpecifiedTypes(['$context' => [$this->contextTypeProvider->getType()]]); | |
// } | |
// } | |
interface ContextTypeProviderInterface | |
{ | |
public function getType() : Type\ArrayShapeNode; | |
} | |
final class ContextTypeFromBigQueryJsonSchemaFileProvider implements ContextTypeProviderInterface | |
{ | |
private ?array $jsonPayloadFields; | |
public function __construct( | |
private string $schemaFile, | |
private BigQueryJsonSchemaJsonPayloadTypeConverterInterface $bigQueryJsonSchemaTypeConverter | |
) | |
{ | |
} | |
public function getType() : Type\ArrayShapeNode | |
{ | |
return $this->bigQueryJsonSchemaTypeConverter->toArrayShapeNode($this->getJsonPayloadFields()); | |
} | |
private function getJsonPayloadFields() : array | |
{ | |
if (!isset($this->jsonPayloadFields)) { | |
$schemaJson = file_get_contents($this->schemaFile); | |
$schema = json_decode($schemaJson, JSON_OBJECT_AS_ARRAY); | |
$jsonPayloadFields = null; | |
foreach($schema as $item) { | |
if ($item['name'] !== 'jsonPayload') { | |
continue; | |
} | |
$jsonPayloadFields = $item['fields']; | |
} | |
$this->jsonPayloadFields = $jsonPayloadFields; | |
} | |
return $this->jsonPayloadFields; | |
} | |
} | |
interface BigQueryJsonSchemaJsonPayloadTypeConverterInterface | |
{ | |
public function toArrayShapeNode(array $jsonPayloadFields) : Type\ArrayShapeNode; | |
} | |
/** | |
* | |
* > The name and type fields are required. All other fields are optional. | |
* https://cloud.google.com/bigquery/docs/schemas?hl=en#creating_a_JSON_schema_file | |
* | |
* - type | |
* https://cloud.google.com/bigquery/docs/reference/rest/v2/tables?hl=en#TableFieldSchema | |
* > The field data type. Possible values include: | |
* STRING, BYTES, INTEGER (or INT64), FLOAT (or FLOAT64), ...., NUMERIC, BIGNUMERIC, JSON, RECORD (or STRUCT), RANGE | |
* | |
* - mode | |
* > Optional. The field mode. Possible values include NULLABLE, REQUIRED and REPEATED. | |
* The default value is NULLABLE. | |
* | |
* @phpstan-type schema_item = array{name: string, type: string, mode?: 'NULLABLE'|'REQUIRED'|'REPEATED'} | |
*/ | |
final class StandardBigQueryJsonSchemaJsonPayloadTypeConverter implements BigQueryJsonSchemaJsonPayloadTypeConverterInterface | |
{ | |
/** | |
* @param array{jsonPayload: list<schema_item>} $jsonPayloadFields | |
*/ | |
public function toArrayShapeNode(array $jsonPayloadFields) : Type\ArrayShapeNode | |
{ | |
return new Type\ArrayShapeNode( | |
array_map('StandardBigQueryJsonSchemaJsonPayloadTypeConverter::itemToNode', $jsonPayloadFields) | |
); | |
} | |
/** | |
* @param schema_item $item | |
*/ | |
public static function itemToNode(array $item) | |
{ | |
if ($item['type'] === 'RECORD' || $item['type'] === 'STRUCT') { | |
if ($item['mode'] === 'REPEATED') { | |
// todo... | |
} | |
return new Type\ArrayShapeItemNode( | |
new Type\IdentifierTypeNode($item['name']), | |
$item['mode'] === 'NULLABLE', | |
new Type\ArrayShapeNode( | |
array_map('StandardBigQueryJsonSchemaJsonPayloadTypeConverter::itemToNode', $item['fields']) | |
) | |
); | |
} | |
$optional = true; | |
if (isset($item['mode']) && $item['mode'] !== 'NULLABLE') { | |
$optional = false; | |
} | |
return new Type\ArrayShapeItemNode( | |
new Type\IdentifierTypeNode($item['name']), | |
$optional, | |
new Type\IdentifierTypeNode(self::convertTypeToPhpScalarType($item['type'])) | |
); | |
} | |
/** | |
* @todo implement | |
*/ | |
public static function convertTypeToPhpScalarType(string $type) | |
{ | |
if (in_array($type, ['INTGER', 'INT64'])) { | |
return 'int'; | |
} | |
// fallback to string | |
return 'string'; | |
} | |
} | |
$contextTypeProvider = new ContextTypeFromBigQueryJsonSchemaFileProvider("data://,$schemaJson", new StandardBigQueryJsonSchemaJsonPayloadTypeConverter); | |
$schemaNode = $contextTypeProvider->getType(); | |
// sample | |
$phpDocNode = new PhpDocNode([ | |
new PhpDocTagNode( | |
'@param', | |
new ParamTagValueNode($schemaNode, false, '$context', 'desc') | |
), | |
]); | |
echo (new Printer())->print($phpDocNode); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment