php-kafka/php-avro-schema-generator

View on GitHub
src/Optimizer/FullNameOptimizer.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

declare(strict_types=1);

namespace PhpKafka\PhpAvroSchemaGenerator\Optimizer;

use PhpKafka\PhpAvroSchemaGenerator\Schema\SchemaTemplateInterface;

class FullNameOptimizer extends AbstractOptimizer implements OptimizerInterface
{
    /**
     * @param SchemaTemplateInterface $schemaTemplate
     * @return SchemaTemplateInterface
     * @throws \JsonException
     */
    public function optimize(SchemaTemplateInterface $schemaTemplate): SchemaTemplateInterface
    {
        $data = json_decode($schemaTemplate->getSchemaDefinition(), true, JSON_THROW_ON_ERROR);

        $currentNamespace = $data['namespace'] ?? '';
        $data = $this->processSchema($currentNamespace, $data, true);

        return $schemaTemplate->withSchemaDefinition(
            json_encode($data, JSON_THROW_ON_ERROR | JSON_PRESERVE_ZERO_FRACTION)
        );
    }

    /**
     * @param string $currentNamespace
     * @param array|mixed $data
     * @param bool $isRoot
     * @return array|mixed|string|null
     */
    private function processSchema(string $currentNamespace, $data, bool $isRoot = false)
    {
        if (true === $this->isRecord($data) && false === $isRoot) {
            $newNamespace = $data['namespace'] ?? '';
            $data = $this->optimizeNamespace($currentNamespace, $data);
            $currentNamespace = $newNamespace;
        }

        $data = $this->handleTypes($currentNamespace, $data);

        if (true === isset($data['fields'])) {
            foreach ($data['fields'] as $index => $field) {
                $data['fields'][$index] = $this->processSchema($currentNamespace, $field);
            }
        }

        if (true === is_string($data)) {
            $data = $this->optimizeNamespace($currentNamespace, $data);
        }

        return $data;
    }

    /**
     * @param string $currentNamespace
     * @param array|mixed $data
     * @return array|mixed|string|null
     */
    private function handleTypes(string $currentNamespace, $data)
    {
        if (true === $this->typeIsRecord($data)) {
            $data['type'] = $this->processSchema($currentNamespace, $data['type']);
        } elseif (true === $this->typeIsTypeArray($data)) {
            foreach ($data['type'] as $index => $type) {
                $data['type'][$index] = $this->processSchema($currentNamespace, $type);
            }
        } elseif (true === $this->typeIsRecordArray($data)) {
            $data['items'] = $this->processSchema($currentNamespace, $data['items']);
        } elseif (true === $this->typeIsMultiTypeArray($data)) {
            foreach ($data['items'] as $index => $item) {
                $data['items'][$index] = $this->processSchema($currentNamespace, $item);
            }
        } elseif (true === $this->typeIsSingleypeArray($data)) {
            $data['items'] = $this->optimizeNamespace($currentNamespace, $data['items']);
        } elseif (true === $this->typeIsString($data)) {
            $data['type'] = $this->optimizeNamespace($currentNamespace, $data['type']);
        }

        return $data;
    }

    /**
     * @param string $currentNamespace
     * @param array|mixed $data
     * @return array|mixed|string|null
     */
    private function optimizeNamespace(string $currentNamespace, $data)
    {
        $data = $this->removeNamespaceFromArray($currentNamespace, $data);
        return $this->removeNamespaceFromString($currentNamespace, $data);
    }

    /**
     * @param string $currentNamespace
     * @param array|mixed $data
     * @return array|mixed|string|null
     */
    private function removeNamespaceFromArray(string $currentNamespace, $data)
    {
        if (false === is_array($data)) {
            return $data;
        }

        $namespace = $data['namespace'] ?? '';

        if ($currentNamespace === $namespace) {
            unset($data['namespace']);
        }

        return $data;
    }

    /**
     * @param string $currentNamespace
     * @param array|mixed $data
     * @return array|mixed|string|null
     */
    private function removeNamespaceFromString(string $currentNamespace, $data)
    {
        if (false === is_string($data)) {
            return $data;
        }

        $currentNameSpacePaths = explode('.', $currentNamespace);
        $dataNameSpacePaths = explode('.', $data);

        foreach ($currentNameSpacePaths as $idx => $currentNameSpacePath) {
            if ($currentNameSpacePath === $dataNameSpacePaths[$idx]) {
                unset($dataNameSpacePaths[$idx]);
            } else {
                break;
            }
        }

        if (1 === sizeof($dataNameSpacePaths)) {
            $data = array_pop($dataNameSpacePaths);
        }

        return $data;
    }
}