public function processConfig($xml) { $return = false; $xmlObj = $xml instanceof SimpleXMLElement ? $xml : simplexml_load_string($xml); if (!$this->getFileInPath(self::DEFAULT_CONFIG_SCHEMA)) { $this->throwException('Schema document ' . self::DEFAULT_CONFIG_SCHEMA . ' not found in path'); } dom_import_simplexml($xmlObj)->ownerDocument->schemaValidate($this->getFileInPath(self::DEFAULT_CONFIG_SCHEMA)) or $this->throwException('Invalid configuration (failed schema check)'); $options = self::defaultOptions(); $dataTypeMap = array(self::OPTION_GRID_CLASS => array(), self::VALUE_GLOBAL => array()); $grids = array(); $gridClasses = array(); $gridCommands = array(); $gridParameters = array(); $columns = array(); $keys = array(); $uniqueKeys = array(); $references = array(); $referenceAliases = array(); if (isset($xmlObj->Options)) { foreach ($xmlObj->Options->children() as $option) { switch ($option->getName()) { // Handle all the bools together, since they're all processed the same way. case self::OPTION_CACHE_REFERENCED_GRIDS: case self::OPTION_CACHE_REFERENCED_GRID_SETS: case self::OPTION_DEBUG: case self::OPTION_LINK_UNPUBLISHED_REFERENCE_COLUMNS: case self::OPTION_OVERWRITE_ON_LOAD: case self::OPTION_PERMIT_DDL: case self::OPTION_PERPETUATE_AUTO_LINKS: case self::OPTION_PUBLISH_ALL_COLUMNS: case self::OPTION_PUBLISH_DEPENDENCIES: case self::OPTION_RELOAD_AFTER_PUBLISH: case self::OPTION_TYPED_GRID_CLASSES: $options[$option->getName()] = (string) $option == self::VALUE_TRUE ? true : false; break; case self::OPTION_COLUMN_CLASS: $className = (string) $option; $this->checkColumnClass($className); $options[self::OPTION_COLUMN_CLASS] = $className; break; case self::OPTION_GRID_CLASS: $className = (string) $option; $this->checkGridClass($className); $options[self::OPTION_GRID_CLASS] = $className; break; case self::OPTION_PAGE_SIZE: $options[$option->getName()] = (int) $option; break; case self::OPTION_TYPED_GRID_CLASSES_PREFIX: $options[self::OPTION_TYPED_GRID_CLASSES_PREFIX] = (string) $option; break; case 'DataTypeMap': $dataTypeMap[self::VALUE_GLOBAL] = $this->parseDataTypeMap($option); break; default: $this->throwException('Unrecognized option "' . $option->getName() . '"'); break; } } } if (isset($xmlObj->Repository)) { if (isset($xmlObj->Repository->Cache)) { $cacheXml = $xmlObj->Repository->Cache; $cacheClass = (string) $cacheXml->attributes()->class; if (!class_exists($cacheClass)) { // Attempt to load if ($file = self::getFileInPath($cacheClass . '.php')) { include $file; } // Check one more time if (!class_exists($cacheClass)) { $this->throwException('Cache class "' . $cacheClass . '" is not defined'); } } $settings = array(); foreach ($cacheXml->Parameter as $parameterXml) { $settings[(string) $parameterXml->attributes()->name] = isset($parameterXml->attributes()->value) ? (string) $parameterXml->attributes()->value : (string) $parameterXml; } $cache = call_user_func(array($cacheClass, 'createInstance'), $this); $cache->initialize($settings); $this->Cache($cache); } $readDataStore = null; $writeDataStore = null; $readXml = null; $writeXml = null; if (isset($xmlObj->Repository->DataStore)) { $readXml = $writeXml = $xmlObj->Repository->DataStore; } else { $readXml = $xmlObj->Repository->ReadDataStore; $writeXml = $xmlObj->Repository->WriteDataStore; } foreach (array('read' => $readXml, 'write' => $writeXml) as $storeType => $dataStoreXml) { if ($storeType == 'write' && $writeXml === $readXml) { continue; } $settings = array(); foreach ($dataStoreXml->Parameter as $parameterXml) { $settings[(string) $parameterXml->attributes()->name] = ((string) $parameterXml->attributes()->encrypted == self::VALUE_TRUE ? self::VALUE_ENC_PREFIX : '') . (isset($parameterXml->attributes()->value) ? (string) $parameterXml->attributes()->value : (string) $parameterXml); } $className = self::DATA_STORE_CLASS; if (isset($dataStoreXml->attributes()->class)) { $className = (string) $dataStoreXml->attributes()->class; if (empty($className) || !class_exists($className)) { $this->throwException('Undefined ' . self::DATA_STORE_CLASS . ' class "' . $className . '" requested'); } if ($className != self::DATA_STORE_CLASS && !in_array(self::DATA_STORE_CLASS, class_parents($className))) { trigger_error('Requested DataStore class "' . $className . '" does not appear to inherit from ' . self::DATA_STORE_CLASS . ' - I hope you know what you\'re doing', E_USER_WARNING); } } else { switch (strtolower((string) $dataStoreXml->attributes()->type)) { // Using just-in-time require_once calls in the event that lazy loading // has not been enabled on this system. Should be negligible with // regard to performance. case 'custom': $this->throwException('Custom data store requested but no class set'); break; case 'mssql': case 'sqlserver': require_once ($className = 'MSSQLDataStore') . '.php'; break; case 'mysql': require_once ($className = 'MySQLDataStore') . '.php'; break; case 'mysqli': if (!version_compare(PHP_VERSION, '5.3.0', '>=')) { $this->throwException('The MySQLi interface is available only in PHP version 5.3.0 or greater (current version is ' . PHP_VERSION . ')'); } // TODO: Write these files. require_once ($className = 'MySQLiDataStore') . '.php'; break; case 'odbc': require_once ($className = 'ODBCDataStore') . '.php'; break; case 'oci': case 'oracle': require_once ($className = 'OracleDataStore') . '.php'; break; case 'postgres': require_once ($className = 'PostgresDataStore') . '.php'; break; case 'sqlite': require_once ($className = 'SQLiteDataStore') . '.php'; break; default: $this->throwException('Unrecognized data store type "' . (string) $dataStoreXml->attributes()->type . '" requested'); break; } } $dataStore = call_user_func(array($className, 'createInstance'), $this); $dataStore->initialize($storeType == 'write' || $writeXml === $readXml ? true : false, $settings); if ($storeType == 'read') { $this->ReadDataStore($dataStore); } if ($storeType == 'write' || $writeXml === $readXml) { $this->WriteDataStore($dataStore); } } } foreach ($xmlObj->Grids->Grid as $gridXml) { $gridName = $this->xmlObjKeyName($gridXml); if (!$gridName) { $this->throwException('Could not extract a suitable grid name'); } if (isset($grids[$gridName])) { $this->throwException('Duplicate grid name ' . $gridName); } // TODO: Auto-pruning or just-in-time generation, so as not to have empty // arrays all over the place? $grids[$gridName] = (string) $gridXml->attributes()->dataName; $columns[$gridName] = array(); $keys[$gridName] = array(); $uniqueKeys[$gridName] = array(); $references[$gridName] = array(); if ($className = (string) $gridXml->attributes()->class) { if ($className == self::VALUE_NONE) { $className = self::DEFAULT_GRID_CLASS; } $gridClasses[$gridName] = $className; } else { if ($options[self::OPTION_TYPED_GRID_CLASSES]) { $gridClasses[$gridName] = $options[self::OPTION_TYPED_GRID_CLASSES_PREFIX] . $gridName; } } foreach ($gridXml->Columns->Column as $columnXml) { $columnName = $this->xmlObjKeyName($columnXml); if (!$columnName) { $this->throwException('Could not extract a suitable column name'); } if (isset($columns[$gridName][$columnName])) { $this->throwException('Duplicate column name ' . $columnName . ' on grid ' . $gridName); } if (isset($columnXml->attributes()->class) || strtoupper((string) $columnXml->attributes()->type) == Column::TYPE_CLASS) { if (!isset($columnXml->attributes()->class)) { $this->throwException('Column attribute "class" must be defined for type ' . Column::TYPE_CLASS); } $className = (string) $columnXml->attributes()->class; if ($className != self::VALUE_NONE) { $this->checkColumnClass($className); } } $columns[$gridName][$columnName] = $columnXml; // We want this one to hang around. } if (isset($gridXml->Commands)) { foreach ($gridXml->Commands->Command as $commandXml) { $command = new PersistenceCommand(); $command->setCommand((string) $commandXml->CommandText); $command->setType($this->makeKeyName((string) $commandXml->attributes()->type)); if (isset($commandXml->attributes()->context)) { $command->setContext($this->makeKeyName((string) $commandXml->attributes()->context)); } if (isset($commandXml->attributes()->placeholder)) { $command->setPlaceholder((string) $commandXml->attributes()->placeholder); } if (isset($commandXml->CommandText->attributes()->type)) { $command->setCommandType((string) $commandXml->CommandText->attributes()->type); } if (isset($commandXml->CommandParameter)) { foreach ($commandXml->CommandParameter as $parameterXml) { $command->addParameter($this->makeKeyName((string) $parameterXml->attributes()->column), isset($parameterXml->attributes()->placeholder) ? (string) $parameterXml->attributes()->placeholder : null); } } if (!isset($gridCommands[$gridName])) { $gridCommands[$gridName] = array(); } if (!isset($gridCommands[$gridName][$command->getType()])) { $gridCommands[$gridName][$command->getType()] = array(); } $gridCommands[$gridName][$command->getType()][] = $command; } } if (isset($gridXml->DataStoreParameters)) { $parameters = array(); foreach ($gridXml->DataStoreParameters->Parameter as $parameterXml) { $parameters[(string) $parameterXml->attributes()->name] = isset($parameterXml->attributes()->value) ? (string) $parameterXml->attributes()->value : (string) $parameterXml; } if (count($parameters) > 0) { $gridParameters[$gridName] = $parameters; } } if (isset($gridXml->DataTypeMap)) { $dataTypeMap[self::DEFAULT_GRID_CLASS][$gridName] = $this->parseDataTypeMap($gridXml->DataTypeMap); } if (isset($gridXml->Keys)) { if (isset($gridXml->Keys->Primary)) { $keyArray = array(); foreach ($gridXml->Keys->Primary->Key as $xmlKey) { $keyName = $this->makeKeyName((string) $xmlKey->attributes()->column); if (!$keyName) { $this->throwException('Invalid key name ' . (string) $xmlKey->attributes()->column . ' in ' . $gridName); } $keyArray[] = $keyName; } if (count($keyArray) <= 0) { $this->throwException('No suitable primary keys defined for grid ' . $gridName); } else { if (count($keyArray) == 1) { // One key and only one key, which is nice $keys[$gridName] = array_shift($keyArray); } else { $keys[$gridName] = $keyArray; } } } if (isset($gridXml->Keys->Unique)) { foreach ($gridXml->Keys->Unique as $xmlUnique) { $keyArray = array(); foreach ($xmlUnique->Key as $xmlKey) { $keyName = $this->makeKeyName((string) $xmlKey->attributes()->column); if (!$keyName) { $this->throwException('Invalid key name ' . (string) $xmlKey->attributes()->column . ' in ' . $gridName); } $keyArray[] = $keyName; } if (count($keyArray) <= 0) { $this->throwException('No suitable unique keys defined for grid ' . $gridName); } else { if (count($keyArray) == 1) { // One key and only one key, which is nice $uniqueKeys[$gridName][] = array_shift($keyArray); } else { $uniqueKeys[$gridName][] = $keyArray; } } } } if (count($keys[$gridName]) == 0 && count($uniqueKeys[$gridName]) == 0) { trigger_error('No primary or unique keys defined for grid ' . $gridName . ', object update will be impossible', E_USER_WARNING); } if (isset($gridXml->Keys->Foreign)) { foreach ($gridXml->Keys->Foreign->Key as $xmlKey) { $targetGrid = $this->makeKeyName((string) $xmlKey->attributes()->referenceGrid); if (!$targetGrid) { $this->throwException('Invalid referenceGrid ' . (string) $xmlKey->attributes()->referenceGrid . ' in foreign key under grid ' . $gridName); } if ($xmlKey->attributes()->referenceGridAlias) { $alias = $this->makeKeyName((string) $xmlKey->attributes()->referenceGridAlias); if (!isset($referenceAliases[$gridName])) { $referenceAliases[$gridName] = array(); } if (isset($referenceAliases[$gridName][$alias])) { $this->throwException('Duplicate referenceGridAlias "' . $alias . '"'); } $referenceAliases[$gridName][$alias] = $targetGrid; $targetGrid .= self::VALUE_SEPARATOR . $alias; } $columnName = $this->makeKeyName((string) $xmlKey->attributes()->column); if (!$columnName || !isset($columns[$gridName][$columnName])) { $this->throwException('Invalid column reference ' . (string) $xmlKey->attributes()->column . ' in foreign key under grid ' . $gridName); } if (!isset($references[$gridName][$targetGrid]) || !is_array($references[$gridName][$targetGrid])) { $references[$gridName][$targetGrid] = array(); } if (isset($references[$gridName][$targetGrid][$columnName])) { $this->throwException('Duplicate column reference ' . $columnName . ' in foreign key under grid ' . $gridName); } $targetColumnName = isset($xmlKey->attributes()->referenceColumn) ? $this->makeKeyName((string) $xmlKey->attributes()->referenceColumn) : $columnName; $references[$gridName][$targetGrid][$columnName] = $targetColumnName; } } } $this->cacheCall('new' . $gridName, '_newGrid', $gridName); } // TODO: Where/how to cache this, as identified by the resource initially fed to // us (in order to rapidly reload on successive iterations)? $this->_config = array(self::ARKEY_COLUMNS => $columns, self::ARKEY_DATATYPEMAP => $dataTypeMap, self::ARKEY_GRIDS => $grids, self::ARKEY_GRID_CLASSES => $gridClasses, self::ARKEY_GRID_COMMANDS => $gridCommands, self::ARKEY_GRID_PARAMETERS => $gridParameters, self::ARKEY_KEYS => $keys, self::ARKEY_OPTIONS => $options, self::ARKEY_REFERENCES => $references, self::ARKEY_REFERENCE_ALIASES => $referenceAliases, self::ARKEY_UNIQUEKEYS => $uniqueKeys); $this->setInitialized(); // Now use those values to finalize the relationships. try { $this->initializeKeys(); } catch (TorporException $e) { $this->setInitialized(false); throw $e; } return true; }
public function parseCommand(PersistenceCommand $command, Grid $grid, $final = true) { $commandText = $command->getCommand(); $placeholder = $command->getPlaceholder(); if (empty($commandText)) { $this->throwException('Invalid command text (empty)'); } $parameters =& $command->getParameters(); foreach ($parameters as $index => $parameter) { $parameterPlaceholder = null; if (strpos($parameter, Torpor::VALUE_SEPARATOR)) { // WARNING: Magic Number, splitting into at most 2 members, which allows for the possible // existence of VALUE_SEPARATOR in the remaining code (which we won't falsely detect, because // if there's a placeholder at all regardless of whether VALUE_SEPARATOR exists in it, we've // used that same value as a glue betwixt us and it, so this detection and split routine is // redundancy safe). list($parameter, $parameterPlaceholder) = explode(Torpor::VALUE_SEPARATOR, $parameter, 2); } if (!$grid->hasColumn($parameter)) { if (!$final) { continue; } $this->throwException('Unknown parameter "' . $parameter . '" (no matching column on ' . $grid->_getObjName() . ' grid)'); } if (!empty($parameterPlaceholder)) { if (strpos($commandText, $parameterPlaceholder) === false) { $this->throwException('Named placeholder "' . $parameterPlaceholder . '" not found in ' . $grid->_getObjName() . ' grid command: ' . $commandText); } // Replace all instances. $commandText = str_replace($parameterPlaceholder, $this->autoQuoteColumn($grid->Column($parameter)), $commandText); if (!$final) { unset($parameters[$index]); } } else { if (!empty($placeholder)) { if (strpos($commandText, $placeholder) === false) { $this->throwException('Placeholder "' . $placeholder . '" not found in ' . $grid->_getObjName() . ' grid command: ' . $commandText); } // Replace only a single instance. $commandText = substr_replace($commandText, $this->autoQuoteColumn($grid->Column($parameter)), strpos($commandText, $placeholder), strlen($placeholder)); if (!$final) { unset($parameters[$index]); } } else { $this->throwException('Empty bind variable, cannot parse ' . $parameter . ' into ' . $grid->_getObjName() . ' grid command: ' . $commandText); } } } if ($final && !empty($placeholder) && strpos($commandText, $placeholder) !== false) { $this->throwException('Un-parsed placeholders found in ' . $grid->_getObjName() . ' grid command: ' . $commandText); } return $commandText; }