/** * @param SchemaId|string $id * @param array $parameters */ public function __construct($id, array $parameters = []) { $this->id = $id instanceof SchemaId ? $id : SchemaId::fromString($id); foreach ($parameters as $key => $value) { $classProperty = lcfirst(StringUtils::toCamelFromSlug($key)); // existing properties if (property_exists(get_called_class(), $classProperty)) { switch ($classProperty) { case 'isMixin': case 'isLatestVersion': case 'deprecated': $value = (bool) $value; break; case 'fields': $fields = []; /** @var FieldDescriptor $field */ foreach ($value as $field) { $fields[$field->getName()] = $field; } $value = $fields; break; case 'mixins': $mixins = []; /** @var SchemaDescriptor $mixin */ foreach ($value as $mixin) { $mixins[$mixin->getId()->getCurieWithMajorRev()] = $mixin; } $value = $mixins; break; } $this->{$classProperty} = $value; } } }
/** * @param Schema $schema * @param \stdClass $rootObject * @param string $path * @return array */ protected function mapSchema(Schema $schema, \stdClass $rootObject, $path = null) { $map = []; foreach ($schema->getFields() as $field) { $fieldName = $field->getName(); $type = $field->getType(); $fieldPath = empty($path) ? $fieldName : $path . '.' . $fieldName; if ($fieldName === Schema::PBJ_FIELD_NAME) { $map[$fieldName] = ['type' => 'string', 'index' => 'not_analyzed', 'include_in_all' => false]; continue; } $method = 'map' . ucfirst(StringUtils::toCamelFromSlug($type->getTypeValue())); if ($field->isAMap()) { $templateName = str_replace('-', '_', SlugUtils::create($fieldPath . '-template')); if (is_callable([$this, $method])) { $rootObject->dynamic_templates[] = [$templateName => ['path_match' => $fieldPath . '.*', 'mapping' => $this->{$method}($field, $rootObject, $fieldPath)]]; } else { $rootObject->dynamic_templates[] = [$templateName => ['path_match' => $fieldPath . '.*', 'mapping' => $this->applyAnalyzer($this->types[$type->getTypeValue()], $field, $rootObject, $path)]]; } } else { if (is_callable([$this, $method])) { $map[$fieldName] = $this->{$method}($field, $rootObject, $fieldPath); } else { $map[$fieldName] = $this->applyAnalyzer($this->types[$type->getTypeValue()], $field, $rootObject, $path); } } } return $map; }
/** * {@inheritdoc} */ public function validate(SchemaDescriptor $a, SchemaDescriptor $b) { $fa = array_merge($a->getInheritedFields(), $a->getFields()); $fb = array_merge($b->getInheritedFields(), $b->getFields()); /** @var \Gdbots\Pbjc\FieldDescriptor $field */ /** @var \Gdbots\Pbjc\FieldDescriptor[] $fb */ foreach ($fa as $name => $field) { if (!isset($fb[$name])) { continue; } $method = 'get' . StringUtils::toCamelFromSnake($this->attribute); if (!method_exists($field, $method)) { $method = 'is' . StringUtils::toCamelFromSnake($this->attribute); if (!method_exists($field, $method)) { throw new \RuntimeException(sprintf('Invalid FieldDescriptor attribute "%s"', $this->attribute)); } } if ($field->{$method}() != $fb[$name]->{$method}()) { $value = $field->{$method}(); if ($value instanceof Enum) { $value = $value->__toString(); } if ($value instanceof Type) { $value = $value->getTypeName()->__toString(); } if ($value === true) { $value = 'true'; } if ($value === false) { $value = 'false'; } throw new ValidatorException(sprintf('The schema "%s" field "%s" should be of %s "%s".', $b, $name, $this->attribute, $value)); } } }
/** * @param mixed $value * @param string Field $field * @param string $message */ public function __construct($value, Field $field, $message = null) { $this->value = $value; $this->field = $field; $message = sprintf('Failed to encode [%s] for field [%s]. Detail: %s', is_scalar($this->value) ? $this->value : StringUtils::varToString($this->value), $this->field->getName(), $message); parent::__construct($message); }
/** * @param string $slug * @throws InvalidArgumentException */ protected function __construct($slug) { if (!is_string($slug)) { throw new InvalidArgumentException(sprintf('String expected but got [%s].', StringUtils::varToString($slug))); } if (!SlugUtils::isValid($slug, true) || !SlugUtils::containsDate($slug)) { throw new InvalidArgumentException(sprintf('The value [%s] is not a valid dated slug.', $slug)); } $this->slug = $slug; }
/** * @param string $string * @throws InvalidArgumentException */ protected function __construct($string) { if (!is_string($string)) { throw new InvalidArgumentException(sprintf('String expected but got [%s].', StringUtils::varToString($string))); } $this->string = trim((string) $string); if (empty($this->string)) { throw new InvalidArgumentException('String cannot be empty.'); } }
/** * @return static */ public static final function create() { $type = get_called_class(); if (!isset(self::$instances[$type])) { $a = explode('\\', $type); $typeName = StringUtils::toSlugFromCamel(str_replace('Type', '', end($a))); self::$instances[$type] = new static(TypeName::create($typeName)); } return self::$instances[$type]; }
/** * @param string $slug * @throws \InvalidArgumentException */ protected function __construct($slug) { @trigger_error(sprintf('"%s" is deprecated. Use "Gdbots\\Pbj\\WellKnown\\DatedSlugIdentifier" from "gdbots/pbj" 1.1.x or later instead.', __CLASS__), E_USER_DEPRECATED); if (!is_string($slug)) { throw new \InvalidArgumentException(sprintf('String expected but got [%s].', StringUtils::varToString($slug))); } if (!SlugUtils::isValid($slug, true) || !SlugUtils::containsDate($slug)) { throw new \InvalidArgumentException(sprintf('The value [%s] is not a valid dated slug.', $slug)); } $this->slug = $slug; }
/** * @param SchemaDescriptor $schema * @param string $filename * @param string $directory * @param bool $isLatest * * @return string */ protected function getSchemaTarget(SchemaDescriptor $schema, $filename, $directory = null, $isLatest = false) { $filename = str_replace(['{vendor}', '{package}', '{category}', '{version}', '{major}'], [$schema->getId()->getVendor(), $schema->getId()->getPackage(), $schema->getId()->getCategory(), $schema->getId()->getVersion()->toString(), $schema->getId()->getVersion()->getMajor()], $filename); if ($directory === null) { $directory = sprintf('%s/%s/%s', StringUtils::toCamelFromSlug($schema->getId()->getVendor()), StringUtils::toCamelFromSlug($schema->getId()->getPackage()), StringUtils::toCamelFromSlug($schema->getId()->getCategory())); } if ($directory) { $directory .= '/'; } return sprintf('%s/%s%s%s', $this->compileOptions->getOutput(), $directory, $filename, $this->extension); }
/** * @param string $string * @throws \InvalidArgumentException */ protected function __construct($string) { @trigger_error(sprintf('"%s" is deprecated. Use "Gdbots\\Pbj\\WellKnown\\StringIdentifier" from "gdbots/pbj" 1.1.x or later instead.', __CLASS__), E_USER_DEPRECATED); if (!is_string($string)) { throw new \InvalidArgumentException(sprintf('String expected but got [%s].', StringUtils::varToString($string))); } $this->string = trim((string) $string); if (empty($this->string)) { throw new \InvalidArgumentException('String cannot be empty.'); } }
/** * @param SchemaDescriptor $schema * @param bool $majorRev * @param string $baseClassName * @param bool $withAs * * @return string */ public function getClassName(SchemaDescriptor $schema, $majorRev = false, $baseClassName = null, $withAs = false) { $className = StringUtils::toCamelFromSlug($schema->getId()->getMessage()); if ($majorRev) { $className = sprintf('%sV%d', StringUtils::toCamelFromSlug($schema->getId()->getMessage()), $schema->getId()->getVersion()->getMajor()); } if ($baseClassName == $className) { $classNameBase = sprintf('%s%s%s', StringUtils::toCamelFromSlug($schema->getId()->getVendor()), StringUtils::toCamelFromSlug($schema->getId()->getPackage()), $className); if ($withAs) { $className = sprintf('%s as %s', $className, $classNameBase); } else { $className = $classNameBase; } } return $className; }
/** * @param string $name * @param array $parameters * * @throws \InvalidArgumentException */ public function __construct($name, array $parameters) { if (!$name || strlen($name) > 127 || preg_match(self::VALID_NAME_PATTERN, $name) === false) { throw new \InvalidArgumentException(sprintf('Field [%s] must match pattern [%s] with less than 127 characters.', $name, self::VALID_NAME_PATTERN)); } foreach ($parameters as $key => $value) { $classProperty = lcfirst(StringUtils::toCamelFromSlug($key)); // existing properties if (property_exists(get_called_class(), $classProperty)) { switch ($classProperty) { case 'name': case 'languages': continue 2; case 'type': /** @var \Gdbots\Pbjc\Type\Type $class */ $class = sprintf('\\Gdbots\\Pbjc\\Type\\%sType', StringUtils::toCamelFromSlug($parameters['type'])); $value = $class::create(); break; case 'rule': if (null !== $value && in_array($value, FieldRule::values())) { $value = FieldRule::create($value); } break; case 'format': if (null !== $value && in_array($value, Format::values())) { $value = Format::create($value); } break; case 'required': case 'useTypeDefault': case 'overridable': case 'deprecated': $value = (bool) $value; break; case 'min': case 'max': case 'minLength': case 'maxLength': case 'precision': case 'scale': $value = (int) $value; break; } $this->{$classProperty} = $value; } elseif (substr($key, -8) == '-options') { $language = substr($key, 0, -8); // remove "-options" if (is_array($value)) { $value = new LanguageBag($value); } $this->getLanguages()->set($language, $value); } } $this->name = $name; $this->applyDefaults(); $this->applyFieldRule(); $this->applyStringOptions(); $this->applyNumericOptions(); }
/** * @param string $slug * * @return string */ public function toCamelFromSlug($slug) { return StringUtils::toCamelFromSlug($slug); }
/** * {@inheritdoc} */ public function generateManifest(array $schemas) { $messages = []; if (!($filename = $this->compileOptions->getManifest())) { return; } // extract previous schemas if (file_exists($filename)) { $content = file_get_contents($filename); if (preg_match_all('/\'([a-z0-9-]+:[a-z0-9\\.-]+:[a-z0-9-]+?:[a-z0-9-]+(:v[0-9]+)?)\' => \'(.*)\'/', $content, $matches) !== false) { foreach ($matches[1] as $key => $value) { $messages[$value] = $matches[3][$key]; } } } // merge with selected schemas (only non-mixin schema's) /** @var SchemaDescriptor $schema */ foreach ($schemas as $schema) { if ($schema->isMixinSchema()) { continue; } if (!array_key_exists($schema->getId()->getCurie(), $messages)) { $messages[$schema->getId()->getCurie()] = sprintf('%s\\%sV%d', $schema->getLanguage('php')->get('namespace'), StringUtils::toCamelFromSlug($schema->getId()->getMessage()), $schema->getId()->getVersion()->getMajor()); } if (SchemaStore::hasOtherSchemaMajorRev($schema->getId())) { /** @var SchemaDescriptor $s */ foreach (SchemaStore::getOtherSchemaMajorRev($schema->getId()) as $s) { if (!array_key_exists($s->getId()->getCurieWithMajorRev(), $messages)) { $messages[$s->getId()->getCurieWithMajorRev()] = sprintf('%s\\%sV%d', $s->getLanguage('php')->get('namespace'), StringUtils::toCamelFromSlug($s->getId()->getMessage()), $s->getId()->getVersion()->getMajor()); } } } } // delete invalid schemas foreach ($messages as $key => $value) { if (!SchemaStore::getSchemaById($key, true)) { unset($messages[$key]); } } $response = new GeneratorResponse(); $response->addFile($this->renderFile('manifest.twig', $filename, ['messages' => $messages])); return $response; }
/** * Generates and writes files for each schema. * * @param string $language * @param CompileOptions $options * * @throws \InvalidArgumentException */ public function run($language, CompileOptions $options) { $namespaces = $options->getNamespaces(); if (!$namespaces || count($namespaces) === 0) { throw new \InvalidArgumentException('Missing "namespaces" options.'); } if (!is_array($namespaces)) { $namespaces = [$namespaces]; } foreach ($namespaces as $namespace) { if (!preg_match('/^([a-z0-9-]+):([a-z0-9\\.-]+)$/', $namespace)) { throw new \InvalidArgumentException(sprintf('The namespace "%s" must follow "vendor:package" format.', $namespace)); } } if (!$options->getOutput()) { throw new \InvalidArgumentException('Missing "output" directory options.'); } $class = sprintf('\\Gdbots\\Pbjc\\Generator\\%sGenerator', StringUtils::toCamelFromSlug($language)); /** @var \Gdbots\Pbjc\Generator\Generator $generator */ $generator = new $class($options); $outputFiles = []; /** @var EnumDescriptor $enum */ foreach (SchemaStore::getEnums() as $enum) { if (!$options->getIncludeAll() && !in_array($enum->getId()->getNamespace(), $namespaces)) { continue; } /** @var $response \Gdbots\Pbjc\Generator\GeneratorResponse */ if ($response = $generator->generateEnum($enum)) { $outputFiles = array_merge($outputFiles, $response->getFiles()); } } /** @var SchemaDescriptor $schema */ foreach (SchemaStore::getSchemas() as $schema) { if (!$options->getIncludeAll() && !in_array($schema->getId()->getNamespace(), $namespaces)) { continue; } /** @var $response \Gdbots\Pbjc\Generator\GeneratorResponse */ if ($response = $generator->generateSchema($schema)) { $outputFiles = array_merge($outputFiles, $response->getFiles()); } } if ($options->getManifest()) { /** @var $response \Gdbots\Pbjc\Generator\GeneratorResponse */ if ($response = $generator->generateManifest(SchemaStore::getSchemas())) { $outputFiles = array_merge($outputFiles, $response->getFiles()); } } if ($callback = $options->getCallback()) { foreach ($outputFiles as $outputFile) { call_user_func($callback, $outputFile); } } }
public function testAddInvalidTypeToMap() { // todo: refactor, this test is weird and confusing $shouldWork = MapsMessage::create(); $shouldFail = clone $shouldWork; /* * some int types won't fail because they're all ints of course, just different ranges. * e.g. an Int is also all other unsigned ints (except BigInt but that's a class so we're fine) */ $allInts = ['TinyInt', 'SmallInt', 'MediumInt', 'Int', 'SignedTinyInt', 'SignedSmallInt', 'SignedMediumInt', 'SignedInt', 'Timestamp']; $allStrings = ['Binary', 'Blob', 'MediumBlob', 'MediumText', 'String', 'Text']; foreach ($shouldWork::getAllTypes() as $type => $class) { foreach ($this->getTypeValues() as $k => $v) { $thrown = false; if ($type == $k) { if (is_array($v)) { $shouldWork->addToMap($type, 'test1', $v[0]); $shouldWork->addToMap($type, 'test2', $v[1]); } else { $shouldWork->addToMap($type, 'test1', $v); } continue; } try { if (is_array($v)) { $shouldFail->addToMap($type, 'test1', $v[0]); $shouldFail->addToMap($type, 'test2', $v[1]); } else { $shouldFail->addToMap($type, 'test1', $v); } switch ($type) { case 'Binary': if (in_array($k, $allStrings)) { continue 2; } break; case 'Blob': if (in_array($k, $allStrings)) { continue 2; } break; case 'Decimal': if (in_array($k, ['Float'])) { continue 2; } break; case 'Date': if (in_array($k, ['DateTime'])) { continue 2; } break; case 'DateTime': if (in_array($k, ['Date'])) { continue 2; } break; case 'Float': if (in_array($k, ['Decimal'])) { continue 2; } break; case 'Identifier': if (in_array($k, ['TimeUuid', 'Uuid'])) { continue 2; } break; case 'MediumBlob': if (in_array($k, $allStrings)) { continue 2; } break; case 'MediumText': if (in_array($k, $allStrings)) { continue 2; } break; case 'String': if (in_array($k, $allStrings)) { continue 2; } break; case 'Text': if (in_array($k, $allStrings)) { continue 2; } break; case 'Timestamp': if (in_array($k, $allInts)) { continue 2; } break; case 'Uuid': if (in_array($k, ['Identifier', 'TimeUuid'])) { continue 2; } break; default: } if (false !== strpos($type, 'Int') && in_array($k, $allInts)) { continue; } } catch (\Exception $e) { $thrown = true; } if (!$thrown) { if (is_array($v)) { $this->fail(sprintf('[%s] accepted an invalid/mismatched [%s] value', $type, StringUtils::varToString($v[0]))); $this->fail(sprintf('[%s] accepted an invalid/mismatched [%s] value', $type, StringUtils::varToString($v[1]))); } else { $this->fail(sprintf('[%s] accepted an invalid/mismatched [%s] value', $type, StringUtils::varToString($v))); } } } } //echo json_encode($shouldWork, JSON_PRETTY_PRINT); }