gdbots/pbjc-php

View on GitHub
src/Validator/SchemaInheritanceFields.php

Summary

Maintainability
F
4 days
Test Coverage
<?php

namespace Gdbots\Pbjc\Validator;

use Gdbots\Common\Util\StringUtils;
use Gdbots\Pbjc\FieldDescriptor;
use Gdbots\Pbjc\SchemaDescriptor;

class SchemaInheritanceFields implements Constraint
{
    /**
     * {@inheritdoc}
     */
    public function validate(SchemaDescriptor $a, SchemaDescriptor $b /* ignored */)
    {
        /** @var FieldDescriptor[] $currentFields */
        /** @var FieldDescriptor[] $inheritedFields */
        $currentFields = $a->getFields();
        $inheritedFields = $a->getInheritedFields();

        $diff = array_intersect(
            array_keys($currentFields),
            array_keys($inheritedFields)
        );
        if (count($diff)) {
            /** @var \ReflectionClass $ref */
            $ref = new \ReflectionClass(new FieldDescriptor('reflection', ['type' => 'string']));

            foreach ($diff as $name) {
                foreach ($ref->getProperties() as $property) {
                    // skip
                    if (in_array($property->getName(), ['default', 'overridable', 'description', 'languages', 'deprecated'])) {
                        continue;
                    }

                    $method = 'get' . ucfirst($property->getName());
                    if (!$ref->hasMethod($method)) {
                        $method = 'is' . ucfirst($property->getName());
                        if (!$ref->hasMethod($method)) {
                            continue;
                        }
                    }

                    /** @var FieldDescriptor $fa */
                    /** @var FieldDescriptor $fb */
                    $fa = $currentFields[$name];
                    $fb = $inheritedFields[$name];

                    if ($fa && $fb) {
                        $error = null;

                        switch ($method) {
                            case 'getAnyOf':
                                if (!$fa->$method() && !$fb->$method()) {
                                    continue 2;
                                }

                                $ea = [];
                                foreach ((array)$fa->$method() as $schema) {
                                    $ea[(string)$schema] = [$this->getClassName($schema)];

                                    if ($extends = $schema->getExtends()) {
                                        do {
                                            $ea[(string)$schema][] = $this->getClassName($extends);
                                        } while ($extends = $extends->getExtends());
                                    }

                                    $ea[(string)$schema] = array_reverse($ea[(string)$schema]);
                                }

                                $eb = [];
                                foreach ((array)$fb->$method() as $schema) {
                                    $eb[(string)$schema] = [$this->getClassName($schema)];

                                    if ($extends = $schema->getExtends()) {
                                        do {
                                            $eb[(string)$schema][] = $this->getClassName($extends);
                                        } while ($extends = $extends->getExtends());
                                    }

                                    $eb[(string)$schema] = array_reverse($eb[(string)$schema]);
                                }

                                if (0 === count($ea)) {
                                    $error = sprintf(
                                        'The schema "%s" field "%s" required at least schema ("%s")',
                                        $a->getId()->toString(),
                                        $property->getName(),
                                        implode('", "', array_keys($eb))
                                    );
                                }
                                if (0 === count($eb)) {
                                    $error = sprintf(
                                        'The schema "%s" field "%s" can\'t include schema',
                                        $a->getId()->toString(),
                                        $property->getName()
                                    );
                                }

                                foreach ([$ea, $eb] as $schemas) {
                                    foreach ($schemas as $classes) {
                                        $i = -1;
                                        $baseClass = array_values($classes)[0];

                                        foreach ($classes as $class) {
                                            $i++;

                                            if (class_exists($class)) {
                                                continue;
                                            }

                                            if ($baseClass === $class) {
                                                eval(sprintf('class %s {};', $class));
                                            } else {
                                                eval(sprintf('class %s extends %s {};', $class, array_values($classes)[$i - 1]));
                                            }
                                        }
                                    }
                                }

                                foreach ($ea as $schemadId => $classes) {
                                    foreach ($classes as $class) {
                                        $oa = new $class();

                                        $found = false;
                                        foreach ($eb as $subClasses) {
                                            foreach ($subClasses as $subClass) {
                                                $ob = new $subClass();

                                                if ($oa instanceof $ob) {
                                                    $found = true;
                                                    break 2;
                                                }
                                            }
                                        }

                                        if (!$found) {
                                            $error = sprintf(
                                                'The schema "%s" field "%s" contains an invalid "%s" schema',
                                                $a->getId()->toString(),
                                                $property->getName(),
                                                $schemadId
                                            );
                                        }
                                    }
                                }

                                break;

                            default:
                                if ($fa->$method() != $fb->$method()) {
                                    $error = sprintf(
                                        'The schema "%s" field "%s" is invalid',
                                        $a->getId()->toString(),
                                        $property->getName()
                                    );
                                }
                        }

                        if ($error) {
                            throw new \RuntimeException(sprintf('%s. See inherited mixin fields.', $error));
                        }
                    }
                }
            }
        }
    }

    /**
     * @param SchemaDescriptor $schema
     *
     * @return string
     */
    private function getClassName(SchemaDescriptor $schema)
    {
        return sprintf(
            '%s%s%sV%s',
            StringUtils::toCamelFromSlug($schema->getId()->getVendor()),
            StringUtils::toCamelFromSlug($schema->getId()->getPackage()),
            StringUtils::toCamelFromSlug($schema->getId()->getMessage()),
            preg_replace('/-/', '', $schema->getId()->getVersion())
        );
    }
}