/** * Attempts to construct a map based on given DBC, predicting what each field could possibly hold by way of sampling */ public static function fromDBC(DBC $dbc, $attach = true) { $fields = $dbc->getFieldCount(); $samples = $dbc->getRecordCount() > self::SAMPLES ? self::SAMPLES : $dbc->getRecordCount(); $block = $dbc->getStringBlock(); preg_match_all('#\\0#', $block, $matches, PREG_OFFSET_CAPTURE); $strings = array(); foreach ($matches[0] as $offset) { $offset = (int) $offset[1] + 1; if ($offset < strlen($block) - 1) { $strings[$offset] = true; } } $matrix = array_fill(1, $fields, 0); for ($i = 0; $i < $samples; $i++) { $record = $dbc->getRecord($i); $values = $record->asArray(); foreach ($values as $offset => $value) { if ($value < 0) { $matrix[$offset] += 1 << 0; } if (self::isProbableFloat($value)) { $matrix[$offset] += 1 << 8; } if (isset($strings[$value]) || $value === 0) { $matrix[$offset] += 1 << 16; if ($value !== 0) { $matrix[$offset] |= 1 << 24; } } } } $map = new self(); for ($i = 1; $i <= $fields; $i++) { $probs = $matrix[$i]; $int = ($probs & 0xff) / $samples; $flt = (($probs & 0xff00) >> 8) / $samples; $str = (($probs & 0xff0000) >> 16) / $samples; $strbit = ($probs & 0xff000000) >> 24; $field = 'field' . $i; if ($flt > 0.6) { $type = DBC::FLOAT; } else { if ($strbit > 0 && $str > 0.99) { $type = DBC::STRING; if ($i + DBC::LOCALIZATION <= $fields) { $type = DBC::STRING_LOC; for ($j = $i + 1; $j <= $i + DBC::LOCALIZATION; $j++) { $probs = $matrix[$j]; $str = (($probs & 0xff0000) >> 16) / $samples; $strbit = ($probs & 0xff000000) >> 24; if ($str !== 1 || $strbit !== 0) { $type = DBC::STRING; } } if ($type === DBC::STRING_LOC) { $i += DBC::LOCALIZATION; } } } else { if ($int > 0.01) { $type = DBC::INT; } else { $type = DBC::UINT; } } } $map->add($field, $type); } if ($attach && $dbc->getMap() === null) { $dbc->attach($map); } return $map; }
/** * Exports given DBC in SQL format to given target (defaults to output stream) using given table name */ public function export(DBC $dbc, $target = self::OUTPUT, $table = 'dbc') { $target = $target === null ? self::OUTPUT : $target; $map = $dbc->getMap(); if ($map === null) { throw new DBCException(self::NO_MAP); return; } $sql = fopen($target, 'w+'); $table = "`" . $this->escape($table) . "`"; fwrite($sql, "DROP TABLE IF EXISTS " . $table . ";" . PHP_EOL . PHP_EOL); $dd = array(); $fields = $map->getFields(); foreach ($fields as $name => $rule) { $count = max($rule & 0xff, 1); $null = false; if ($rule & DBCMap::UINT_MASK) { $type = 'INT(11) UNSIGNED'; } else { if ($rule & DBCMap::INT_MASK) { $type = 'INT(11) SIGNED'; } else { if ($rule & DBCMap::FLOAT_MASK) { $type = 'FLOAT'; } else { if ($rule & DBCMap::STRING_MASK || $rule & DBCMap::STRING_LOC_MASK) { $type = 'TEXT'; $null = true; } } } } for ($i = 1; $i <= $count; $i++) { $suffix = $count > 1 ? $i : ''; $dd[] = ' `' . $this->escape($name) . $suffix . '` ' . $type . ' ' . ($null ? 'NULL' : 'NOT NULL'); } } reset($fields); fwrite($sql, "CREATE TABLE " . $table . " (" . PHP_EOL . implode(',' . PHP_EOL, $dd) . ',' . PHP_EOL . ' PRIMARY KEY (`' . key($fields) . '`)' . PHP_EOL . ');' . PHP_EOL . PHP_EOL); foreach ($dbc as $i => $record) { if ($i % $this->recordsPerQuery === 0) { fwrite($sql, "INSERT INTO " . $table . " VALUES" . PHP_EOL . "\t("); } else { fwrite($sql, "," . PHP_EOL . "\t("); } fwrite($sql, $this->join($record->extract())); if (($i + 1) % $this->recordsPerQuery === 0 || $i === $dbc->getRecordCount() - 1) { fwrite($sql, ");" . PHP_EOL); } else { fwrite($sql, ")"); } } fclose($sql); return $target; }