src/SDL/Support.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php
/**
 * This file is part of Railt package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
declare(strict_types=1);

namespace Railt\SDL;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Railt\Json\Exception\JsonException;
use Railt\Json\Json;
use Railt\SDL\Contracts\Behavior\AllowsTypeIndication;
use Railt\SDL\Contracts\Definitions\Definition;
use Railt\SDL\Contracts\Definitions\TypeDefinition;
use Railt\SDL\Contracts\Dependent\ArgumentDefinition;
use Railt\SDL\Contracts\Dependent\FieldDefinition;

/**
 * Trait Support
 */
trait Support
{
    /**
     * @var string
     */
    private static $typeDefinition = '%s<%s>';

    /**
     * @var string
     */
    private static $fieldDefinition = '{%s: %s}';

    /**
     * @var string
     */
    private static $argumentDefinition = '(%s: %s)';

    /**
     * @var string
     */
    private static $syntaxList = '[%s]';

    /**
     * @var string
     */
    private static $syntaxNonNull = '%s!';

    /**
     * @param mixed $value
     * @return string
     * @throws JsonException
     */
    protected function valueWithType($value): string
    {
        return \mb_strtolower(\gettype($value)) . ' ' . $this->valueToString($value);
    }

    /**
     * @param mixed|iterable|null $value
     * @return string
     * @throws JsonException
     */
    protected function valueToString($value): string
    {
        $result = $this->valueToScalar($value);

        if (\is_array($result)) {
            $result = Json::encode($result);
            $result = \preg_replace('/"([_A-Za-z][_0-9A-Za-z]*)":/u', '$1: ', $result);
            $result = \preg_replace('/:\s+(.*?),/u', ': $1, ', $result);

            return $result;
        }

        return (string)$result;
    }

    /**
     * @param mixed $value
     * @return mixed
     * @throws JsonException
     */
    protected function valueToScalar($value)
    {
        if (\is_scalar($value)) {
            return $value;
        }

        if (\is_iterable($value)) {
            $result = [];

            /** @var iterable $value */
            foreach ($value as $key => $sub) {
                $result[$key] = $this->valueToScalar($sub);
            }

            return $result;
        }

        if ($value instanceof Definition) {
            return $this->typeToString($value);
        }

        if ($value instanceof Arrayable) {
            return $this->valueToScalar($value->toArray());
        }

        if ($value instanceof Jsonable) {
            return Json::decode($value->toJson(), \JSON_OBJECT_AS_ARRAY);
        }

        if ($value instanceof \JsonSerializable) {
            return $this->valueToScalar(Json::encode($value->jsonSerialize()));
        }

        return \ucfirst(\strtolower(\gettype($value)));
    }

    /**
     * @param AllowsTypeIndication&Definition $type
     * @return string
     */
    protected function typeToString(Definition $type): string
    {
        if ($type instanceof ArgumentDefinition) {
            return \vsprintf(self::$argumentDefinition, [
                $type->getName(),
                $this->typeIndicatorToString($type),
            ]);
        }

        if ($type instanceof FieldDefinition) {
            return \vsprintf(self::$fieldDefinition, [
                $type->getName(),
                $this->typeIndicatorToString($type),
            ]);
        }

        if ($type instanceof TypeDefinition) {
            return \sprintf(self::$typeDefinition, $type->getTypeName(), $type->getName());
        }

        return $type->getName();
    }

    /**
     * @param AllowsTypeIndication $type
     * @return string
     */
    protected function typeIndicatorToString(AllowsTypeIndication $type): string
    {
        try {
            $result = $type->getTypeDefinition()->getName();

            if ($type->isList()) {
                if ($type->isListOfNonNulls()) {
                    $result = \sprintf(self::$syntaxNonNull, $result);
                }

                $result = \sprintf(self::$syntaxList, $result);
            }

            if ($type->isNonNull()) {
                $result = \sprintf(self::$syntaxNonNull, $result);
            }

            return $result;
        } catch (\Throwable $e) {
            return '?';
        }
    }
}