Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: b61497fefa
Fetching contributors…

Cannot retrieve contributors at this time

386 lines (307 sloc) 12.364 kB
<?php
/**
* This file is part of the Propel package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\Generator\Builder\Util;
use Propel\Generator\Config\GeneratorConfigInterface;
use Propel\Generator\Exception\SchemaException;
use Propel\Generator\Model\Schema;
use Propel\Generator\Platform\PlatformInterface;
/**
* A class that is used to parse an input xml schema file and creates a Schema
* PHP object.
*
* @author Hans Lellelid <hans@xmpl.org> (Propel)
* @author Leon Messerschmidt <leon@opticode.co.za> (Torque)
* @author Jason van Zyl <jvanzyl@apache.org> (Torque)
* @author Martin Poeschl <mpoeschl@marmot.at> (Torque)
* @author Daniel Rall <dlr@collab.net> (Torque)
* @author Hugo Hamon <webmaster@apprendre-php.com>
*/
class SchemaReader
{
/** enables debug output */
const DEBUG = false;
private $schema;
private $currDB;
private $currTable;
private $currColumn;
private $currFK;
private $currIndex;
private $currUnique;
private $currBehavior;
private $currVendorObject;
private $isForReferenceOnly;
private $currentPackage;
private $currentXmlFile;
private $defaultPackage;
private $encoding;
/**
* two-dimensional array,
* first dimension is for schemas(key is the path to the schema file),
* second is for tags within the schema
*/
private $schemasTagsStack = array();
/**
* Creates a new instance for the specified database type.
*
* @param PlatformInterface $defaultPlatform The default database platform for the application.
* @param string $defaultPackage the default PHP package used for the om
* @param string $encoding The database encoding.
*/
public function __construct(PlatformInterface $defaultPlatform = null, $defaultPackage = null, $encoding = 'iso-8859-1')
{
$this->schema = new Schema($defaultPlatform);
$this->defaultPackage = $defaultPackage;
$this->firstPass = true;
$this->encoding = $encoding;
}
/**
* Set the Schema generator configuration
*
* @param GeneratorConfigInterface $generatorConfig
*/
public function setGeneratorConfig(GeneratorConfigInterface $generatorConfig)
{
$this->schema->setGeneratorConfig($generatorConfig);
}
/**
* Parses a XML input file and returns a newly created and
* populated Schema structure.
*
* @param string $xmlFile The input file to parse.
* @return Schema populated by <code>xmlFile</code>.
*/
public function parseFile($xmlFile)
{
// we don't want infinite recursion
if ($this->isAlreadyParsed($xmlFile)) {
return;
}
return $this->parseString(file_get_contents($xmlFile), $xmlFile);
}
/**
* Parses a XML input string and returns a newly created and
* populated Schema structure.
*
* @param string $xmlString The input string to parse.
* @param string $xmlFile The input file name.
* @return Schema
*/
public function parseString($xmlString, $xmlFile = null)
{
// we don't want infinite recursion
if ($this->isAlreadyParsed($xmlFile)) {
return;
}
// store current schema file path
$this->schemasTagsStack[$xmlFile] = array();
$this->currentXmlFile = $xmlFile;
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_set_object($parser, $this);
xml_set_element_handler($parser, 'startElement', 'endElement');
if (!xml_parse($parser, $xmlString)) {
throw new SchemaException(sprintf('XML error: %s at line %d',
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser))
);
}
xml_parser_free($parser);
array_pop($this->schemasTagsStack);
return $this->schema;
}
public function startElement($parser, $name, $attributes)
{
$parentTag = $this->peekCurrentSchemaTag();
if (false === $parentTag) {
switch ($name) {
case 'database':
if ($this->isExternalSchema()) {
$this->currentPackage = isset($attributes['package']) ? $attributes['package'] : null;
if (null === $this->currentPackage) {
$this->currentPackage = $this->defaultPackage;
}
} else {
$this->currDB = $this->schema->addDatabase($attributes);
}
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} elseif ('database' === $parentTag) {
switch ($name) {
case 'external-schema':
$xmlFile = isset($attributes['filename']) ? $attributes['filename'] : null;
// 'referenceOnly' attribute is valid in the main schema XML file only,
// and it's ignored in the nested external-schemas
if (!$this->isExternalSchema()) {
$isForRefOnly = isset($attributes['referenceOnly']) ? $attributes['referenceOnly'] : null;
$this->isForReferenceOnly = (null !== $isForRefOnly ? ('true' === strtolower($isForRefOnly)) : true); // defaults to TRUE
}
if ('/' !== $xmlFile{0}) {
$xmlFile = realpath(dirname($this->currentXmlFile) . DIRECTORY_SEPARATOR . $xmlFile);
if (!file_exists($xmlFile)) {
throw new SchemaException(sprintf('Unknown include external "%s"', $xmlFile));
}
}
$this->parseFile($xmlFile);
break;
case 'domain':
$this->currDB->addDomain($attributes);
break;
case 'table':
$this->currTable = $this->currDB->addTable($attributes);
if ($this->isExternalSchema()) {
$this->currTable->setForReferenceOnly($this->isForReferenceOnly);
$this->currTable->setPackage($this->currentPackage);
}
break;
case 'vendor':
$this->currVendorObject = $this->currDB->addVendorInfo($attributes);
break;
case 'behavior':
$this->currBehavior = $this->currDB->addBehavior($attributes);
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} elseif ('table' === $parentTag) {
switch ($name) {
case 'column':
$this->currColumn = $this->currTable->addColumn($attributes);
break;
case 'foreign-key':
$this->currFK = $this->currTable->addForeignKey($attributes);
break;
case 'index':
$this->currIndex = $this->currTable->addIndex($attributes);
break;
case 'unique':
$this->currUnique = $this->currTable->addUnique($attributes);
break;
case 'vendor':
$this->currVendorObject = $this->currTable->addVendorInfo($attributes);
break;
case 'id-method-parameter':
$this->currTable->addIdMethodParameter($attributes);
break;
case 'behavior':
$this->currBehavior = $this->currTable->addBehavior($attributes);
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} elseif ('column' === $parentTag) {
switch ($name) {
case 'inheritance':
$this->currColumn->addInheritance($attributes);
break;
case 'vendor':
$this->currVendorObject = $this->currColumn->addVendorInfo($attributes);
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} elseif ('foreign-key' === $parentTag) {
switch ($name) {
case 'reference':
$this->currFK->addReference($attributes);
break;
case 'vendor':
$this->currVendorObject = $this->currUnique->addVendorInfo($attributes);
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} elseif ('index' === $parentTag) {
switch ($name) {
case 'index-column':
$this->currIndex->addColumn($attributes);
break;
case 'vendor':
$this->currVendorObject = $this->currIndex->addVendorInfo($attributes);
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} elseif ('unique' === $parentTag) {
switch ($name) {
case 'unique-column':
$this->currUnique->addColumn($attributes);
break;
case 'vendor':
$this->currVendorObject = $this->currUnique->addVendorInfo($attributes);
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} elseif ($parentTag == 'behavior') {
switch ($name) {
case 'parameter':
$this->currBehavior->addParameter($attributes);
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} elseif ('vendor' === $parentTag) {
switch ($name) {
case 'parameter':
$this->currVendorObject->setParameter($attributes['name'], $attributes['value']);
break;
default:
$this->_throwInvalidTagException($parser, $name);
}
} else {
// it must be an invalid tag
$this->_throwInvalidTagException($parser, $name);
}
$this->pushCurrentSchemaTag($name);
}
protected function _throwInvalidTagException($parser, $tag_name)
{
$location = '';
if (null !== $this->currentXmlFile) {
$location .= sprintf('file %s,', $this->currentXmlFile);
}
$location .= sprintf('line %d', xml_get_current_line_number($parser));
if ($col = xml_get_current_column_number($parser)) {
$location .= sprintf(', column %d', $col);
}
throw new SchemaException(sprintf('Unexpected tag <%s> in %s', $tag_name, $location));
}
public function endElement($parser, $name)
{
if (self::DEBUG) {
print('endElement(' . $name . ") called\n");
}
$this->popCurrentSchemaTag();
}
protected function peekCurrentSchemaTag()
{
$keys = array_keys($this->schemasTagsStack);
return end($this->schemasTagsStack[end($keys)]);
}
protected function popCurrentSchemaTag()
{
$keys = array_keys($this->schemasTagsStack);
array_pop($this->schemasTagsStack[end($keys)]);
}
protected function pushCurrentSchemaTag($tag)
{
$keys = array_keys($this->schemasTagsStack);
$this->schemasTagsStack[end($keys)][] = $tag;
}
protected function isExternalSchema()
{
return count($this->schemasTagsStack) > 1;
}
protected function isAlreadyParsed($filePath)
{
return isset($this->schemasTagsStack[$filePath]);
}
}
Jump to Line
Something went wrong with that request. Please try again.