/** * @param FieldDefinition|FieldDefinition[] $component The component to be built. * * @return string */ public static function build($component) { if (is_array($component)) { $ret = array(); foreach ($component as $c) { $ret[] = static::build($c); } return "(\n" . implode(",\n", $ret) . "\n)"; } else { $tmp = ''; if ($component->isConstraint) { $tmp .= 'CONSTRAINT '; } if (!empty($component->name)) { $tmp .= Context::escape($component->name) . ' '; } if (!empty($component->type)) { $tmp .= DataType::build($component->type) . ' '; } if (!empty($component->key)) { $tmp .= Key::build($component->key) . ' '; } if (!empty($component->references)) { $tmp .= 'REFERENCES ' . Reference::build($component->references) . ' '; } $tmp .= OptionsArray::build($component->options); return trim($tmp); } }
/** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * * @return CreateDefinition[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = new CreateDefinition(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -----------------------[ ( ]------------------------> 1 * * 1 --------------------[ CONSTRAINT ]------------------> 1 * 1 -----------------------[ key ]----------------------> 2 * 1 -------------[ constraint / column name ]-----------> 2 * * 2 --------------------[ data type ]-------------------> 3 * * 3 ---------------------[ options ]--------------------> 4 * * 4 --------------------[ REFERENCES ]------------------> 4 * * 5 ------------------------[ , ]-----------------------> 1 * 5 ------------------------[ ) ]-----------------------> 6 (-1) * * @var int $state */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } if ($state === 0) { if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { $state = 1; } else { $parser->error(__('An opening bracket was expected.'), $token); break; } } elseif ($state === 1) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'CONSTRAINT') { $expr->isConstraint = true; } elseif ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_KEY) { $expr->key = Key::parse($parser, $list); $state = 4; } else { $expr->name = $token->value; if (!$expr->isConstraint) { $state = 2; } } } elseif ($state === 2) { $expr->type = DataType::parse($parser, $list); $state = 3; } elseif ($state === 3) { $expr->options = OptionsArray::parse($parser, $list, static::$FIELD_OPTIONS); $state = 4; } elseif ($state === 4) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'REFERENCES') { ++$list->idx; // Skipping keyword 'REFERENCES'. $expr->references = Reference::parse($parser, $list); } else { --$list->idx; } $state = 5; } elseif ($state === 5) { if (!empty($expr->type) || !empty($expr->key)) { $ret[] = $expr; } $expr = new CreateDefinition(); if ($token->value === ',') { $state = 1; } elseif ($token->value === ')') { $state = 6; ++$list->idx; break; } else { $parser->error(__('A comma or a closing bracket was expected.'), $token); $state = 0; break; } } } // Last iteration was not saved. if (!empty($expr->type) || !empty($expr->key)) { $ret[] = $expr; } if ($state !== 0 && $state !== 6) { $parser->error(__('A closing bracket was expected.'), $list->tokens[$list->idx - 1]); } --$list->idx; return $ret; }
public function testParse() { $component = Key::parse(new Parser(), $this->getTokensList('')); $this->assertEquals(null, $component->name); }